Initial Commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
export { registerNodeFsProvider } from "./registry.js";
|
||||
@@ -0,0 +1,49 @@
|
||||
# `registry`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Registers `NodeFsStorage` against the module-augmentation registry maintained by `@suaveplan/storage/factory`. After `registerNodeFsProvider()` runs once, the `createStorage({ type: "file", config: NodeFsStorageOptions })` factory dispatches to `new NodeFsStorage(config)` with full TypeScript narrowing on the `file` slot.
|
||||
|
||||
## Features
|
||||
|
||||
- Registers the `file` provider type with the storage factory.
|
||||
- Idempotent — repeated `registerNodeFsProvider()` calls are no-ops, so the function is safe to call from any consumer's startup path without worrying about double-registration.
|
||||
- Declaration-merges `StorageProviderRegistry` and `StorageProviderConfig` so `createStorage` is fully typed.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { registerNodeFsProvider } from "@suaveplan/storage-file";
|
||||
import { createStorage } from "@suaveplan/storage/factory";
|
||||
|
||||
registerNodeFsProvider();
|
||||
const storage = createStorage({ type: "file", config: { rootDir: "/tmp/store" } });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { registerNodeFsProvider, NodeFsStorage } from "@suaveplan/storage-file";
|
||||
import { createStorage } from "@suaveplan/storage/factory";
|
||||
|
||||
// Safe in any startup module — idempotent.
|
||||
registerNodeFsProvider();
|
||||
registerNodeFsProvider();
|
||||
|
||||
const storage = createStorage({ type: "file", config: { rootDir: "/var/lib/app/storage", retry: { maxAttempts: 5 } } });
|
||||
console.log(storage instanceof NodeFsStorage); // → true
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `registerNodeFsProvider(): void` — register the `file` provider. Idempotent.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The registration delegates to `registerStorageProvider("file", NodeFsStorage)` from `@suaveplan/storage/factory`. The cast through `unknown` is required because the factory's loose `ProviderCtor` signature accepts `unknown` for config — `NodeFsStorage` accepts a stricter type but is structurally compatible.
|
||||
- Registration is NOT triggered on import — consumers must call `registerNodeFsProvider()` explicitly. This keeps `import "@suaveplan/storage-file"` side-effect-free per `"sideEffects": false`.
|
||||
- The module-augmentation `declare module "@suaveplan/storage/factory"` is type-only — it does not require the consumer to import this file at runtime to benefit from the typing.
|
||||
@@ -0,0 +1,52 @@
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import {
|
||||
clearStorageProviders,
|
||||
createStorage,
|
||||
hasStorageProvider,
|
||||
} from "@suaveplan/storage/factory";
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
} from "@suaveplan/testing/runner";
|
||||
import { NodeFsStorage } from "../node-fs-storage/node-fs-storage.js";
|
||||
import { registerNodeFsProvider } from "./registry.js";
|
||||
|
||||
describe("registerNodeFsProvider", () => {
|
||||
let root: string;
|
||||
beforeEach(async () => {
|
||||
root = await mkdtemp(join(tmpdir(), "nfs-registry-"));
|
||||
clearStorageProviders();
|
||||
});
|
||||
afterEach(async () => {
|
||||
clearStorageProviders();
|
||||
await rm(root, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it("registers the file provider and resolves it via createStorage", {
|
||||
timeout: 10000,
|
||||
}, async () => {
|
||||
registerNodeFsProvider();
|
||||
expect(hasStorageProvider("file")).toBe(true);
|
||||
const storage = createStorage({
|
||||
type: "file" as never,
|
||||
config: { rootDir: root } as never,
|
||||
});
|
||||
expect(storage).toBeInstanceOf(NodeFsStorage);
|
||||
await storage.set("k", new Uint8Array([1]));
|
||||
expect(await storage.get("k")).toEqual(new Uint8Array([1]));
|
||||
});
|
||||
|
||||
it("is idempotent across repeated registerNodeFsProvider calls", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
registerNodeFsProvider();
|
||||
registerNodeFsProvider();
|
||||
registerNodeFsProvider();
|
||||
expect(hasStorageProvider("file")).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
hasStorageProvider,
|
||||
registerStorageProvider,
|
||||
} from "@suaveplan/storage/factory";
|
||||
import { NodeFsStorage } from "../node-fs-storage/node-fs-storage.js";
|
||||
import type { NodeFsStorageOptions } from "../types/node-fs-storage.types.js";
|
||||
|
||||
declare module "@suaveplan/storage/factory" {
|
||||
interface StorageProviderRegistry {
|
||||
file: NodeFsStorage;
|
||||
}
|
||||
interface StorageProviderConfig {
|
||||
file: NodeFsStorageOptions;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register `NodeFsStorage` against the `@suaveplan/storage` factory under
|
||||
* the canonical type slot `"file"`. Idempotent — repeated calls do nothing
|
||||
* once the slot is occupied, and re-register cleanly after a downstream
|
||||
* `clearStorageProviders()` call.
|
||||
*/
|
||||
export function registerNodeFsProvider(): void {
|
||||
if (hasStorageProvider("file")) return;
|
||||
registerStorageProvider(
|
||||
"file",
|
||||
NodeFsStorage as unknown as new (
|
||||
config?: unknown
|
||||
) => NodeFsStorage
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user