@nwire/container 0.8.17 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,33 +1,109 @@
1
1
  # @nwire/container
2
2
 
3
- > DI seam the four-method `Container` interface every Nwire package composes through.
3
+ Typed DI container, generic over the app's `Cradle` shape. Awilix-backed under the hood — you get scope hierarchy, lazy proxy cradle, lifetimes (singleton/transient/scoped), disposers, and factory-with-deps for free.
4
4
 
5
- ## What it is
5
+ ```bash
6
+ pnpm add @nwire/container
7
+ ```
6
8
 
7
- A small `Container` interface (`resolve` / `has` / `register` / `createScope`) plus an in-memory default. Every other Nwire package depends only on this, so you can bring your own DI (plain object, Awilix, tsyringe) without rewriting the framework.
9
+ ## Why
8
10
 
9
- ## Install
11
+ Apps grow into needing:
10
12
 
11
- ```bash
12
- pnpm add @nwire/container
13
+ - **Typed bindings.** `container.cradle.logger` should be `Logger`, not `unknown`.
14
+ - **Per-request scopes.** Each HTTP request gets its own scope so `user`, `tenant`, `requestId` don't leak across requests.
15
+ - **Lifetimes.** `singleton` for connections; `scoped` for per-request transactions; `transient` for fresh-each-time values.
16
+ - **Disposers.** Graceful shutdown calls `db.close()`, `redis.quit()` automatically when a scope ends.
17
+ - **Source location.** Studio shows "this binding was registered at `src/wires/api.ts:42`".
18
+
19
+ Awilix has done this for over a decade. We wrap it with a typed `Container<TCradle>` interface so app code stays decoupled from awilix-specific imports, and you opt into the full Awilix surface via `.raw` when you need it.
20
+
21
+ ## Surface
22
+
23
+ ```ts
24
+ interface Container<TCradle extends object = object> {
25
+ resolve<T, K extends string>(name: K): K extends keyof TCradle ? TCradle[K] : T;
26
+ register<T, K extends string>(
27
+ name: K,
28
+ factory: K extends keyof TCradle ? TCradle[K] | (() => TCradle[K]) : T | (() => T),
29
+ ): void;
30
+ readonly cradle: TCradle;
31
+ createScope(): Container<TCradle>;
32
+ has(name: keyof TCradle | (string & {})): boolean;
33
+ list?(): ReadonlyArray<BindingEntry>;
34
+ readonly raw: AwilixContainer<TCradle>;
35
+ }
36
+
37
+ function createContainer<TCradle extends object = object>(): Container<TCradle>;
38
+ ```
39
+
40
+ ## Consumer example
41
+
42
+ ```ts
43
+ import { createContainer } from "@nwire/container";
44
+
45
+ // Each plugin exports its cradle contribution as a type.
46
+ import type { AuthCradle } from "@nwire/auth"; // { "auth.user": User; … }
47
+ import type { DbCradle } from "@nwire/data-drizzle"; // { "db.pg": PgClient }
48
+
49
+ // App composes the cradle (plus its own bindings).
50
+ type AppCradle = AuthCradle &
51
+ DbCradle & {
52
+ config: AppConfig;
53
+ logger: Logger;
54
+ };
55
+
56
+ const root = createContainer<AppCradle>();
57
+ root.register("config", { port: 3000, dbUrl: process.env.DATABASE_URL! });
58
+ root.register("logger", () => ({ log: (s) => process.stdout.write(s + "\n") }));
59
+ root.register("db.pg", () => new PgClient(root.cradle.config.dbUrl)); // ← typed
60
+
61
+ root.cradle.logger.log(`Listening on :${root.cradle.config.port}`); // ← typed, autocomplete
62
+
63
+ // Per-request scope — child inherits, owns request-scoped values
64
+ server.on("request", async (req, res) => {
65
+ const scope = root.createScope();
66
+ scope.register("requestId", crypto.randomUUID());
67
+ scope.register("user", await loadUser(req));
68
+ // hand `scope` to your handler / framework
69
+ });
13
70
  ```
14
71
 
15
- ## Within nwire-app
72
+ ## Lazy by default
16
73
 
17
- For developers using this package as part of the Nwire stack. Plugins register on the container during `boot`; handlers resolve via `ctx.resolve("key")`. Swap the implementation by passing a different `Container` to `createApp`.
74
+ `container.cradle` is an Awilix proxy. Untouched bindings never instantiate; touched ones are cached (singleton) or fresh-each-time (transient) per the registration.
18
75
 
19
76
  ```ts
20
- import { createApp } from "@nwire/forge";
21
- import { AwilixNwireContainer } from "@nwire/container-awilix";
77
+ const factory = vi.fn(() => new ExpensiveDb());
78
+ root.register("db", factory);
22
79
 
23
- const container = new AwilixNwireContainer();
24
- container.register("db", () => new PrismaClient());
80
+ // factory not called yet
81
+ void root.cradle.db; // now called once
82
+ void root.cradle.db; // ← cached, not called again
83
+ ```
25
84
 
26
- const app = createApp("learnflow", { modules, container });
85
+ ## Full Awilix when you need it
86
+
87
+ `container.raw` is the underlying `AwilixContainer`. Use it for disposers, asClass, scoped lifetimes, async resolution, or `loadModules` — anything beyond the simple register/cradle/resolve surface:
88
+
89
+ ```ts
90
+ import { asFunction, asValue, Lifetime } from "awilix";
91
+
92
+ root.raw.register({
93
+ pgPool: asFunction(({ config }) => new PgPool(config.dbUrl))
94
+ .singleton()
95
+ .disposer((p) => p.end()), // graceful shutdown
96
+ pgTx: asFunction(({ pgPool }) => pgPool.transaction())
97
+ .setLifetime(Lifetime.SCOPED) // one transaction per request scope
98
+ .disposer((tx) => tx.rollback()), // auto-rollback if scope ends without commit
99
+ });
27
100
  ```
28
101
 
29
- ## API
102
+ ## Used by
103
+
104
+ Every Nwire transport (HTTP, queue, cron, MCP) creates its scope chain through this surface. Plugins register into it. Handlers read from a wire-composed ctx that pulls from the cradle.
105
+
106
+ ## Notes
30
107
 
31
- - `Container`interface every adapter implements.
32
- - `InMemoryContainer` `Map`-backed default; good for tests and small wires.
33
- - `rootContainer` — a pre-built blank `InMemoryContainer` framework layers compose with.
108
+ - Awilix uses PROXY injection mode by default destructure parameters resolve from the cradle by name.
109
+ - `keyof TCradle` keys can include any string, including dotted names (`"auth.user"`, `"db.pg"`). Namespacing is the convention to avoid plugin name clashes.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `Container<TCradle>` — Awilix-backed generic. App declares its cradle
3
+ * shape; plugins export type fragments the app intersects in. No global
4
+ * augmentation, no cross-app pollution.
5
+ *
6
+ * - declared keys resolve via both `.cradle.X` and `.resolve("X")`
7
+ * - wrong-shape factory for a declared key is a real type error
8
+ * - unknown names fall back to `<T>` generic
9
+ * - `.cradle` is a typed Awilix proxy: lazy resolution
10
+ * - child scopes inherit cradle typing
11
+ * - `.raw` exposes the underlying AwilixContainer for advanced cases
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=cradle.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cradle.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/cradle.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * `Container<TCradle>` — Awilix-backed generic. App declares its cradle
3
+ * shape; plugins export type fragments the app intersects in. No global
4
+ * augmentation, no cross-app pollution.
5
+ *
6
+ * - declared keys resolve via both `.cradle.X` and `.resolve("X")`
7
+ * - wrong-shape factory for a declared key is a real type error
8
+ * - unknown names fall back to `<T>` generic
9
+ * - `.cradle` is a typed Awilix proxy: lazy resolution
10
+ * - child scopes inherit cradle typing
11
+ * - `.raw` exposes the underlying AwilixContainer for advanced cases
12
+ */
13
+ import { describe, expect, it, expectTypeOf, vi } from "vitest";
14
+ import { asFunction, asValue, Lifetime } from "awilix";
15
+ import { createContainer } from "../container.js";
16
+ describe("createContainer<TCradle> — typed binding surface", () => {
17
+ it("resolves a declared key to its declared type via .cradle and .resolve", () => {
18
+ const c = createContainer();
19
+ c.register("config", { port: 3000, host: "0.0.0.0" });
20
+ c.register("logger", () => ({ log: () => { } }));
21
+ expectTypeOf(c.cradle.config).toEqualTypeOf();
22
+ expectTypeOf(c.cradle.logger).toEqualTypeOf();
23
+ expectTypeOf(c.resolve("config")).toEqualTypeOf();
24
+ expect(c.cradle.config.port).toBe(3000);
25
+ expect(c.resolve("logger")).toBe(c.cradle.logger); // factory.singleton() caches
26
+ c.cradle.logger.log("ok");
27
+ });
28
+ it("rejects a wrong-shaped registration at the type level", () => {
29
+ const c = createContainer();
30
+ // @ts-expect-error config must satisfy AppConfig, not a string
31
+ c.register("config", "not-config");
32
+ expect(c.has("config")).toBe(true);
33
+ });
34
+ it("falls back to the <T> generic for unknown names (escape hatch)", () => {
35
+ const c = createContainer();
36
+ c.register("widget", { id: "w-1" });
37
+ const widget = c.resolve("widget");
38
+ expectTypeOf(widget).toEqualTypeOf();
39
+ expect(widget.id).toBe("w-1");
40
+ });
41
+ it("cradle is lazy — factories run only on first access", () => {
42
+ const factory = vi.fn(() => ({ log: () => { } }));
43
+ const c = createContainer();
44
+ c.register("logger", factory);
45
+ expect(factory).not.toHaveBeenCalled(); // ← never touched, never called
46
+ void c.cradle.logger;
47
+ expect(factory).toHaveBeenCalledTimes(1);
48
+ void c.cradle.logger;
49
+ expect(factory).toHaveBeenCalledTimes(1); // ← singleton: cached
50
+ });
51
+ it("child scope inherits TCradle typing from the parent", () => {
52
+ const parent = createContainer();
53
+ parent.register("config", { port: 8080, host: "localhost" });
54
+ const child = parent.createScope();
55
+ expectTypeOf(child.cradle.config).toEqualTypeOf();
56
+ expect(child.cradle.config.host).toBe("localhost");
57
+ });
58
+ it("child scope overrides parent for the scope only", () => {
59
+ const parent = createContainer();
60
+ parent.register("config", { port: 80, host: "prod" });
61
+ const child = parent.createScope();
62
+ child.register("config", { port: 99, host: "test" });
63
+ expect(child.cradle.config.host).toBe("test");
64
+ expect(parent.cradle.config.host).toBe("prod");
65
+ });
66
+ it(".raw exposes the underlying AwilixContainer for advanced cases", () => {
67
+ const c = createContainer();
68
+ // Use Awilix's full surface directly — lifetimes, disposers, factory-with-deps.
69
+ c.raw.register({
70
+ logger: asValue({ log: () => { } }),
71
+ config: asFunction(() => ({ port: 1, host: "x" })).setLifetime(Lifetime.SINGLETON),
72
+ });
73
+ expect(c.cradle.logger).toBeDefined();
74
+ expect(c.cradle.config.port).toBe(1);
75
+ });
76
+ it("untyped container works with default <TCradle = object>", () => {
77
+ const c = createContainer();
78
+ c.register("anything", 42);
79
+ expect(c.resolve("anything")).toBe(42);
80
+ expect(c.has("anything")).toBe(true);
81
+ });
82
+ });
83
+ //# sourceMappingURL=cradle.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cradle.test.js","sourceRoot":"","sources":["../../src/__tests__/cradle.test.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,24 @@
1
1
  /**
2
2
  * `@nwire/container` — DI seam.
3
3
  *
4
- * Defines the four-method `Container` interface every Nwire layer composes
5
- * through (`resolve` / `has` / `register` / `createScope`) and ships an
6
- * in-memory default. Bring your own DI (plain object, Awilix, tsyringe)
7
- * under any Nwire package without rewriting the framework.
4
+ * Generic over a `TCradle` shape (Awilix-style). Apps declare their cradle
5
+ * type at the boot site; plugins export type fragments the app intersects
6
+ * in. Nothing is globally augmented.
8
7
  *
9
- * See: architecture-sketch.html §05 (Foundation tier).
8
+ * type AppCradle = AuthCradle & DbCradle & { config: AppConfig };
9
+ * const c = createContainer<AppCradle>();
10
+ * c.register("auth.user", user); // typed
11
+ * c.cradle["auth.user"] // → User, autocomplete + lazy
12
+ * c.resolve("auth.user") // → User, escape hatch
13
+ *
14
+ * Implementation is Awilix-backed: scope hierarchy, lazy proxy cradle,
15
+ * lifetimes (singleton/transient/scoped), disposers, factory-with-deps.
16
+ * Source-location capture wraps `register()` so Studio's `.nwire/di.json`
17
+ * surfaces "where was this binding made?".
18
+ *
19
+ * Apps consume `createContainer<TCradle>()` as the canonical factory.
10
20
  */
21
+ import { type AwilixContainer } from "awilix";
11
22
  import { type SourceLocation } from "@nwire/messages";
12
23
  /**
13
24
  * One row per active registration, surfaced by `container.list()`. Studio
@@ -16,49 +27,75 @@ import { type SourceLocation } from "@nwire/messages";
16
27
  *
17
28
  * `kind` reflects how the value was stored: a function factory under
18
29
  * `register()` is `"transient"` (re-evaluated on every resolve); a plain
19
- * value is `"singleton"`.
30
+ * value is `"singleton"`. Awilix's full lifetime spectrum
31
+ * (singleton/transient/scoped) is available via `.raw.register({…})`.
20
32
  */
21
33
  export interface BindingEntry {
22
34
  readonly name: string;
23
35
  readonly kind: "singleton" | "transient";
24
36
  readonly source?: SourceLocation;
25
37
  }
26
- export interface Container {
27
- /** Look up a registered value by name. Throws when unregistered. */
28
- resolve<T = unknown>(name: string): T;
29
- /** Register a value or factory under a name. Last-write wins. */
30
- register<T = unknown>(name: string, factory: T | (() => T)): void;
38
+ export interface Container<TCradle extends object = object> {
39
+ /**
40
+ * Imperative lookup by name. Throws when unregistered.
41
+ *
42
+ * Names declared on `TCradle` resolve to their declared type. Unknown
43
+ * names default to `unknown` — pass an explicit `<T>` to override.
44
+ */
45
+ resolve<T = unknown, K extends string = string>(name: K): K extends keyof TCradle ? TCradle[K] : T;
46
+ /**
47
+ * Register a value or factory under a name. Last-write wins.
48
+ *
49
+ * - Plain value → Awilix `asValue(v)` (singleton-equivalent).
50
+ * - Function `() => T` → Awilix `asFunction(fn).singleton()` (cached after first resolve).
51
+ *
52
+ * Apps that need Awilix's full lifetime / disposer / factory-with-deps
53
+ * surface use `container.raw.register({…})` directly — the underlying
54
+ * `AwilixContainer` is exposed via `.raw`.
55
+ */
56
+ register<T = unknown, K extends string = string>(name: K, factory: K extends keyof TCradle ? TCradle[K] | (() => TCradle[K]) : T | (() => T)): void;
57
+ /**
58
+ * Typed lazy proxy. `container.cradle["auth.user"]` resolves through the
59
+ * same machinery `container.resolve("auth.user")` uses — bindings are
60
+ * resolved only on access, so untouched ones never instantiate.
61
+ * Awilix's PROXY-mode cradle, surfaced under our typed interface.
62
+ */
63
+ readonly cradle: TCradle;
31
64
  /**
32
65
  * Create a child scope that inherits parent registrations but lets the
33
66
  * caller override or register locally. Used for per-request scopes —
34
67
  * each HTTP request gets a scoped container so per-request values
35
68
  * (envelope, user, tenant) don't leak across requests.
36
69
  */
37
- createScope(): Container;
70
+ createScope(): Container<TCradle>;
38
71
  /** True when a name is registered (either locally or on a parent). */
39
- has(name: string): boolean;
72
+ has(name: keyof TCradle | (string & {})): boolean;
40
73
  /**
41
- * Snapshot every binding visible to this container (own bindings union
42
- * parent bindings; child overrides win on name collisions). Purely
74
+ * Snapshot every binding visible to this container. Purely
43
75
  * introspective — never throws, never resolves factories. Studio + CLI
44
- * `nwire cache` use this to surface the DI surface; runtime code should
45
- * stick to `resolve` / `has`.
76
+ * `nwire cache` use this to surface the DI surface.
46
77
  *
47
- * Optional so external `Container` adapters (Awilix, tsyringe, plain
48
- * objects) stay valid without re-implementing introspection.
78
+ * Optional so external `Container` adapters stay valid without
79
+ * re-implementing introspection.
49
80
  */
50
81
  list?(): ReadonlyArray<BindingEntry>;
82
+ /**
83
+ * Escape hatch — the underlying Awilix container. Use when you need
84
+ * Awilix-specific surface (`asClass`, scoped lifetimes, disposers,
85
+ * `loadModules`, …). Apps that stay on `register` + `cradle` + `resolve`
86
+ * never touch this.
87
+ */
88
+ readonly raw: AwilixContainer<TCradle>;
51
89
  }
52
- export declare class InMemoryContainer implements Container {
53
- private readonly parent?;
54
- private readonly bindings;
55
- constructor(parent?: InMemoryContainer | undefined);
56
- resolve<T = unknown>(name: string): T;
57
- register<T = unknown>(name: string, factory: T | (() => T)): void;
58
- createScope(): Container;
59
- has(name: string): boolean;
60
- list(): ReadonlyArray<BindingEntry>;
61
- }
62
- /** A blank root container, ready to be populated by the wire. */
63
- export declare const rootContainer: Container;
90
+ /**
91
+ * Build a container — generic over `TCradle`. The returned value
92
+ * satisfies the `Container<TCradle>` interface and is backed by Awilix
93
+ * (PROXY-mode cradle, scope hierarchy, lifetimes, disposers).
94
+ *
95
+ * type AppCradle = { logger: Logger; pg: PgClient };
96
+ * const root = createContainer<AppCradle>();
97
+ * root.register("logger", asValue(logger));
98
+ * root.cradle.logger.log("…");
99
+ */
100
+ export declare function createContainer<TCradle extends object = object>(): Container<TCradle>;
64
101
  //# sourceMappingURL=container.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE7E;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;CAClC;AAED,MAAM,WAAW,SAAS;IACxB,oEAAoE;IACpE,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAC;IAEtC,iEAAiE;IACjE,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC;IAElE;;;;;OAKG;IACH,WAAW,IAAI,SAAS,CAAC;IAEzB,sEAAsE;IACtE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAE3B;;;;;;;;;OASG;IACH,IAAI,CAAC,IAAI,aAAa,CAAC,YAAY,CAAC,CAAC;CACtC;AAOD,qBAAa,iBAAkB,YAAW,SAAS;IAGrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAFpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;gBAEhC,MAAM,CAAC,EAAE,iBAAiB,YAAA;IAEvD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;IAUrC,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI;IAUjE,WAAW,IAAI,SAAS;IAIxB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAK1B,IAAI,IAAI,aAAa,CAAC,YAAY,CAAC;CAepC;AAED,iEAAiE;AACjE,eAAO,MAAM,aAAa,EAAE,SAAmC,CAAC"}
1
+ {"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE7E;;;;;;;;;GASG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,WAAW,CAAC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC;CAClC;AAED,MAAM,WAAW,SAAS,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM;IACxD;;;;;OAKG;IACH,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,EAC5C,IAAI,EAAE,CAAC,GACN,CAAC,SAAS,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAE5C;;;;;;;;;OASG;IACH,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,EAC7C,IAAI,EAAE,CAAC,EACP,OAAO,EAAE,CAAC,SAAS,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GACjF,IAAI,CAAC;IAER;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IAEzB;;;;;OAKG;IACH,WAAW,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;IAElC,sEAAsE;IACtE,GAAG,CAAC,IAAI,EAAE,MAAM,OAAO,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC;IAElD;;;;;;;OAOG;IACH,IAAI,CAAC,IAAI,aAAa,CAAC,YAAY,CAAC,CAAC;IAErC;;;;;OAKG;IACH,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC;CACxC;AA8ED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,KAAK,SAAS,CAAC,OAAO,CAAC,CAErF"}
package/dist/container.js CHANGED
@@ -1,29 +1,44 @@
1
1
  /**
2
2
  * `@nwire/container` — DI seam.
3
3
  *
4
- * Defines the four-method `Container` interface every Nwire layer composes
5
- * through (`resolve` / `has` / `register` / `createScope`) and ships an
6
- * in-memory default. Bring your own DI (plain object, Awilix, tsyringe)
7
- * under any Nwire package without rewriting the framework.
4
+ * Generic over a `TCradle` shape (Awilix-style). Apps declare their cradle
5
+ * type at the boot site; plugins export type fragments the app intersects
6
+ * in. Nothing is globally augmented.
8
7
  *
9
- * See: architecture-sketch.html §05 (Foundation tier).
8
+ * type AppCradle = AuthCradle & DbCradle & { config: AppConfig };
9
+ * const c = createContainer<AppCradle>();
10
+ * c.register("auth.user", user); // typed
11
+ * c.cradle["auth.user"] // → User, autocomplete + lazy
12
+ * c.resolve("auth.user") // → User, escape hatch
13
+ *
14
+ * Implementation is Awilix-backed: scope hierarchy, lazy proxy cradle,
15
+ * lifetimes (singleton/transient/scoped), disposers, factory-with-deps.
16
+ * Source-location capture wraps `register()` so Studio's `.nwire/di.json`
17
+ * surfaces "where was this binding made?".
18
+ *
19
+ * Apps consume `createContainer<TCradle>()` as the canonical factory.
10
20
  */
21
+ import { createContainer as createAwilixContainer, asValue, asFunction, InjectionMode, } from "awilix";
11
22
  import { captureSourceLocation } from "@nwire/messages";
12
- export class InMemoryContainer {
13
- parent;
14
- bindings = new Map();
15
- constructor(parent) {
16
- this.parent = parent;
23
+ /** Per-binding source-location capture, keyed by the underlying Awilix container. */
24
+ const sourceByContainer = new WeakMap();
25
+ class NwireContainer {
26
+ raw;
27
+ constructor(awilix) {
28
+ this.raw =
29
+ awilix ??
30
+ createAwilixContainer({
31
+ injectionMode: InjectionMode.PROXY,
32
+ });
33
+ if (!sourceByContainer.has(this.raw)) {
34
+ sourceByContainer.set(this.raw, new Map());
35
+ }
36
+ }
37
+ get cradle() {
38
+ return this.raw.cradle;
17
39
  }
18
40
  resolve(name) {
19
- const own = this.bindings.get(name);
20
- if (own !== undefined) {
21
- const v = own.value;
22
- return typeof v === "function" ? v() : v;
23
- }
24
- if (this.parent)
25
- return this.parent.resolve(name);
26
- throw new Error(`Container: no binding for "${name}"`);
41
+ return this.raw.resolve(name);
27
42
  }
28
43
  register(name, factory) {
29
44
  // Skip two frames: this `register` itself plus the synthetic `Error`
@@ -32,32 +47,51 @@ export class InMemoryContainer {
32
47
  // frames (forge's app capability registration, plugin .provide(), etc.)
33
48
  // until it lands on user code.
34
49
  const source = captureSourceLocation(2);
35
- this.bindings.set(name, { value: factory, source });
50
+ const sources = sourceByContainer.get(this.raw);
51
+ if (source && sources)
52
+ sources.set(name, source);
53
+ if (typeof factory === "function") {
54
+ this.raw.register({
55
+ [name]: asFunction(factory).singleton(),
56
+ });
57
+ }
58
+ else {
59
+ this.raw.register({
60
+ [name]: asValue(factory),
61
+ });
62
+ }
36
63
  }
37
64
  createScope() {
38
- return new InMemoryContainer(this);
65
+ return new NwireContainer(this.raw.createScope());
39
66
  }
40
67
  has(name) {
41
- if (this.bindings.has(name))
42
- return true;
43
- return this.parent?.has(name) ?? false;
68
+ return this.raw.hasRegistration(name);
44
69
  }
45
70
  list() {
46
- // Walk parent chain first so child-overrides win when we re-insert.
47
- const merged = new Map();
48
- for (const entry of this.parent?.list?.() ?? []) {
49
- merged.set(entry.name, entry);
50
- }
51
- for (const [name, binding] of this.bindings) {
52
- merged.set(name, {
53
- name,
54
- kind: typeof binding.value === "function" ? "transient" : "singleton",
55
- source: binding.source,
56
- });
57
- }
58
- return Array.from(merged.values());
71
+ const sources = sourceByContainer.get(this.raw) ?? new Map();
72
+ const registrations = this.raw.registrations;
73
+ return Object.keys(registrations).map((name) => {
74
+ // Awilix exposes `lifetime` on each registration; classify by it.
75
+ // `SINGLETON` (which we use for asFunction(...).singleton()) and
76
+ // `TRANSIENT` are the two we surface; SCOPED collapses into "transient"
77
+ // for the Studio view.
78
+ const reg = registrations[name].lifetime;
79
+ const kind = reg === "TRANSIENT" ? "transient" : "singleton";
80
+ return { name, kind, source: sources.get(name) };
81
+ });
59
82
  }
60
83
  }
61
- /** A blank root container, ready to be populated by the wire. */
62
- export const rootContainer = new InMemoryContainer();
84
+ /**
85
+ * Build a container — generic over `TCradle`. The returned value
86
+ * satisfies the `Container<TCradle>` interface and is backed by Awilix
87
+ * (PROXY-mode cradle, scope hierarchy, lifetimes, disposers).
88
+ *
89
+ * type AppCradle = { logger: Logger; pg: PgClient };
90
+ * const root = createContainer<AppCradle>();
91
+ * root.register("logger", asValue(logger));
92
+ * root.cradle.logger.log("…");
93
+ */
94
+ export function createContainer() {
95
+ return new NwireContainer();
96
+ }
63
97
  //# sourceMappingURL=container.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"container.js","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,qBAAqB,EAAuB,MAAM,iBAAiB,CAAC;AAqD7E,MAAM,OAAO,iBAAiB;IAGC;IAFZ,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE7D,YAA6B,MAA0B;QAA1B,WAAM,GAAN,MAAM,CAAoB;IAAG,CAAC;IAE3D,OAAO,CAAc,IAAY;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;YACtB,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC;YACpB,OAAO,OAAO,CAAC,KAAK,UAAU,CAAC,CAAC,CAAE,CAAa,EAAE,CAAC,CAAC,CAAE,CAAO,CAAC;QAC/D,CAAC;QACD,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,CAAI,IAAI,CAAC,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,GAAG,CAAC,CAAC;IACzD,CAAC;IAED,QAAQ,CAAc,IAAY,EAAE,OAAsB;QACxD,qEAAqE;QACrE,uEAAuE;QACvE,sEAAsE;QACtE,wEAAwE;QACxE,+BAA+B;QAC/B,MAAM,MAAM,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAkB,EAAE,MAAM,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,WAAW;QACT,OAAO,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACrC,CAAC;IAED,GAAG,CAAC,IAAY;QACd,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACzC,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;IACzC,CAAC;IAED,IAAI;QACF,oEAAoE;QACpE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAwB,CAAC;QAC/C,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;YAChD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC5C,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE;gBACf,IAAI;gBACJ,IAAI,EAAE,OAAO,OAAO,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW;gBACrE,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;QACL,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACrC,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,CAAC,MAAM,aAAa,GAAc,IAAI,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"container.js","sourceRoot":"","sources":["../src/container.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nwire/container",
3
- "version": "0.8.17",
4
- "description": "Nwire — DI container contract + InMemory default. Production adapters land as @nwire/container-awilix.",
3
+ "version": "0.9.1",
4
+ "description": "Nwire — DI container contract + Awilix-backed default. Generic over TCradle; ships scope hierarchy, lazy cradle proxy, lifetimes, disposers via Awilix.",
5
5
  "keywords": [
6
6
  "container",
7
7
  "di",
@@ -27,7 +27,8 @@
27
27
  "access": "public"
28
28
  },
29
29
  "dependencies": {
30
- "@nwire/messages": "0.8.17"
30
+ "awilix": "^12.0.4",
31
+ "@nwire/messages": "0.9.1"
31
32
  },
32
33
  "devDependencies": {
33
34
  "@types/node": "^22.19.9",