@teqfw/di 2.5.0 → 2.6.0

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/ai/extensions.md CHANGED
@@ -1,73 +1,103 @@
1
1
  # extensions.md
2
2
 
3
- Version: 20260331
3
+ Version: 20260606
4
4
 
5
5
  ## Purpose
6
6
 
7
- The container supports controlled extension of the dependency resolution process through **preprocess handlers** and **postprocess wrappers**. These mechanisms allow the behavior of dependency resolution and object creation to be adjusted without modifying container internals.
7
+ The container supports controlled extension of dependency resolution through three distinct mechanisms:
8
8
 
9
- Extensions are registered during container configuration and become part of the deterministic resolution pipeline.
9
+ - preprocess hooks;
10
+ - postprocess hooks;
11
+ - wrapper exports.
10
12
 
11
- ## Extension Points
13
+ These mechanisms are related but not interchangeable.
12
14
 
13
- Two extension points are available:
15
+ ## Preprocess Hooks
14
16
 
15
- - **Preprocess handlers** transform dependency identifiers before resolution.
16
- - **Postprocess wrappers** — modify or decorate created objects after instantiation.
17
+ Preprocess hooks transform CDC identities before resolution.
17
18
 
18
- These extension points correspond to the preprocess and postprocess stages of the container pipeline described in **container.md**.
19
+ Signature:
19
20
 
20
- ## Preprocess Handlers
21
+ ```js
22
+ (depId) => depId
23
+ ```
24
+
25
+ Properties:
26
+
27
+ - registered with `addPreprocess()`;
28
+ - run in declared order;
29
+ - receive and return DepId DTO values;
30
+ - affect identifier interpretation before module resolution.
31
+
32
+ Typical uses:
33
+
34
+ - CDC alias rewriting;
35
+ - policy-driven identifier normalization;
36
+ - project-specific prefix adaptation.
37
+
38
+ ## Postprocess Hooks
21
39
 
22
- Preprocess handlers receive a dependency identifier before it is interpreted by the container. A handler may transform the identifier and return a modified value that continues through the resolution pipeline.
40
+ Postprocess hooks transform resolved values after instantiation and before wrapper exports.
23
41
 
24
- Typical uses of preprocess handlers include:
42
+ Signature:
25
43
 
26
- - rewriting dependency identifiers
27
- - applying identifier aliases
28
- - enforcing project-specific identifier conventions
44
+ ```js
45
+ (value) => value
46
+ ```
47
+
48
+ Properties:
49
+
50
+ - registered with `addPostprocess()`;
51
+ - run for every resolved value;
52
+ - run in declared order;
53
+ - do not alter CDC parsing or module resolution.
29
54
 
30
- Preprocess handlers operate only on identifiers and do not interact with instantiated objects.
55
+ Typical uses:
31
56
 
32
- ## Postprocess Wrappers
57
+ - global instrumentation;
58
+ - value normalization;
59
+ - cross-cutting adaptation applied uniformly to all resolved values.
33
60
 
34
- Postprocess wrappers operate on objects created by the container. A wrapper receives the instantiated object and may return a modified or decorated version.
61
+ ## Wrapper Exports
35
62
 
36
- Wrappers are typically used for:
63
+ Wrapper exports are selected directly from CDC suffixes and are resolved from the same module namespace as the dependency being composed.
37
64
 
38
- - logging or tracing
39
- - instrumentation
40
- - capability injection
41
- - runtime adaptation of object behavior
65
+ Signature:
42
66
 
43
- Wrappers do not modify the dependency resolution logic itself and operate only after object instantiation.
67
+ ```js
68
+ (value) => value
69
+ ```
44
70
 
45
- ## Wrapper Selection
71
+ Properties:
46
72
 
47
- Wrappers are applied when their identifiers appear in a CDC. Wrapper identifiers are appended after the lifecycle marker and separated by underscores.
73
+ - selected by wrapper suffixes such as `_wrapLog`;
74
+ - applied only when present in the CDC;
75
+ - executed after postprocess hooks and before freeze;
76
+ - not registered globally in the container.
48
77
 
49
78
  Example:
50
79
 
51
80
  ```txt
52
- App_Service_User$$_wrapLog_wrapTrace
81
+ App_Service$$_wrapLog_wrapTrace
53
82
  ```
54
83
 
55
- In this example the container creates a new instance of the dependency and applies the wrappers `wrapLog` and `wrapTrace` during the postprocess stage.
56
-
57
- Wrappers do not change the structure of `__deps__`. Dependency descriptors remain either hierarchical export-scoped descriptors, shorthand flat descriptors for limited single-export cases, or omitted entirely for empty-descriptor modules.
84
+ In this example the container composes the dependency, applies postprocess hooks, then applies wrapper exports `wrapLog` and `wrapTrace` in that order.
58
85
 
59
86
  ## Execution Order
60
87
 
61
- Multiple wrappers may be applied to a single dependency. Wrappers are executed in the order in which they appear in the CDC.
62
-
63
- Each wrapper receives the object returned by the previous wrapper and may return a new object that becomes the input to the next wrapper.
88
+ The extension-related execution order is:
64
89
 
65
- ## Extension Constraints
90
+ 1. preprocess hooks;
91
+ 2. instantiation or as-is resolution;
92
+ 3. postprocess hooks;
93
+ 4. wrapper exports;
94
+ 5. freeze.
66
95
 
67
- Extensions must satisfy the following constraints:
96
+ ## Constraints
68
97
 
69
- - they must be registered before the container becomes active
70
- - they must be deterministic and free of side effects that alter container state
71
- - they must not bypass lifecycle semantics enforced by the container
98
+ Extensions must satisfy these constraints:
72
99
 
73
- These constraints ensure that extensions do not compromise deterministic dependency resolution.
100
+ - they must be registered before the first `get()` if they use container registration APIs;
101
+ - they should be deterministic for the same input;
102
+ - they must not rely on mutating frozen returned values;
103
+ - they must not bypass lifecycle semantics enforced by the container.
package/ai/package-api.ts CHANGED
@@ -110,9 +110,10 @@ export const PACKAGE_API: PackageApiContract = {
110
110
  name: 'addNamespaceRoot',
111
111
  signature: 'addNamespaceRoot(prefix: string, target: string, defaultExt: string): void',
112
112
  stage: 'builder',
113
- summary: 'Registers one namespace-to-filesystem mapping rule.',
113
+ summary: 'Registers one namespace root that maps a CDC prefix to a module-specifier base.',
114
114
  constraints: [
115
115
  'Allowed only before the first get().',
116
+ 'The target may be filesystem-backed or URL-backed depending on runtime environment.',
116
117
  'Namespace rules are snapshotted and locked on the first get().',
117
118
  ],
118
119
  },
@@ -130,10 +131,10 @@ export const PACKAGE_API: PackageApiContract = {
130
131
  name: 'addPostprocess',
131
132
  signature: 'addPostprocess(fn: (value: unknown) => unknown): void',
132
133
  stage: 'builder',
133
- summary: 'Adds an ordered post-instantiation value transform applied to every resolved value.',
134
+ summary: 'Adds an ordered value transform applied to every resolved value after instantiation.',
134
135
  constraints: [
135
136
  'Allowed only before the first get().',
136
- 'Runs before wrapper exports and before freeze.',
137
+ 'Runs after instantiation and before wrapper exports.',
137
138
  ],
138
139
  },
139
140
  {
@@ -168,7 +169,7 @@ export const PACKAGE_API: PackageApiContract = {
168
169
  name: 'get',
169
170
  signature: 'get(cdc: string): Promise<any>',
170
171
  stage: 'runtime',
171
- summary: 'Parses, resolves, instantiates, postprocesses, wraps, applies lifecycle, freezes, and returns a linked value.',
172
+ summary: 'Parses, preprocesses, resolves, instantiates, postprocesses, wraps, applies lifecycle, freezes, and returns a linked value.',
172
173
  constraints: [
173
174
  'The first get() locks configuration and creates internal infrastructure.',
174
175
  'Any fatal pipeline error moves the container into failed state.',
@@ -220,7 +221,7 @@ export const PACKAGE_API: PackageApiContract = {
220
221
  fields: {
221
222
  moduleName: 'Logical module identifier without platform prefix.',
222
223
  platform: '"teq" | "node" | "npm".',
223
- exportName: 'Named export or "default"; null means whole-module/as-is resolution.',
224
+ exportName: 'Named export or "default"; null means whole-module as-is resolution.',
224
225
  composition: '"A" | "F" in the current implementation.',
225
226
  life: '"S" | "T" | null in the current implementation.',
226
227
  wrappers: 'Ordered wrapper export names.',
@@ -267,15 +268,16 @@ export const PACKAGE_API: PackageApiContract = {
267
268
  kind: 'module-contract',
268
269
  summary: 'Shape expected from application modules resolved by the container.',
269
270
  fields: {
270
- __deps__: 'Optional export-scoped dependency descriptor. Canonical form is hierarchical: Record<exportName, Record<dependencyKey, CDC string>>. A flat Record<dependencyKey, CDC string> is shorthand for limited single-export cases.',
271
+ __deps__: 'Optional dependency descriptor. Canonical form is hierarchical Record<exportName, Record<dependencyKey, CDC string>>. Supported shorthand form is a flat Record<dependencyKey, CDC string> for default-export-only modules. Omission means there are no declared dependencies.',
271
272
  moduleNamespace: 'Whole ES module namespace object returned for as-is CDC without selected export.',
272
273
  defaultExport: 'Used when the parsed DepId selects exportName="default" for factory composition.',
273
- namedExports: 'May be selected via __ExportName for factory composition and may also provide wrapper functions.',
274
+ namedExports: 'May be selected via __ExportName for factory composition and may also provide wrapper exports.',
274
275
  wrapperExport: 'Named export whose identifier appears in depId.wrappers; it must be synchronous and unary.',
275
276
  },
276
277
  notes: [
277
- 'The current runtime accepts both export-scoped hierarchical descriptors and the flat shorthand form for limited single-export cases.',
278
- 'Wrapper functions are exported by the same resolved module namespace, not registered globally in the container.',
278
+ 'The hierarchical export-scoped descriptor is canonical.',
279
+ 'The flat shorthand descriptor is supported only for default-export-only modules and does not replace the canonical hierarchical model.',
280
+ 'Wrapper exports are resolved from the same module namespace and are not globally registered in the container.',
279
281
  ],
280
282
  },
281
283
  ],
@@ -429,8 +431,8 @@ export const PACKAGE_API: PackageApiContract = {
429
431
  operationalNotes: [
430
432
  'Only two runtime entrypoints are supported by package.json exports: @teqfw/di and @teqfw/di/src/Config/NamespaceRegistry.mjs.',
431
433
  'Resolved values are frozen before being returned.',
432
- 'Dependency descriptors are canonical when hierarchical and export-scoped; flat descriptors are shorthand for limited single-export cases; omission means no dependencies.',
433
- 'With the default parser, CDC without lifecycle returns the whole module namespace as-is; named export selection implies factory composition.',
434
+ 'The hierarchical export-scoped descriptor is canonical. The flat shorthand descriptor is supported only for default-export-only modules.',
435
+ 'With the default parser, CDC without lifecycle resolves the selected module export as-is.',
434
436
  'Named wrapper exports are executed after addPostprocess() hooks and before freeze.',
435
437
  'In the current implementation, CDC markers $$ and $$$ both end up as transient/no-cache behavior.',
436
438
  'types.d.ts is broader than the runtime import surface. Presence of an alias there does not by itself make the underlying module a supported runtime entrypoint.',
package/ai/usage.md CHANGED
@@ -1,59 +1,35 @@
1
1
  # usage.md
2
2
 
3
- Version: 20260331
3
+ Version: 20260606
4
4
 
5
5
  ## Purpose
6
6
 
7
- This document describes typical usage patterns of the container. The examples illustrate how modules declare dependencies, how the container is configured in the composition root, and how Canonical Dependency Codes (CDC) control dependency resolution, instantiation, and behavioral modification.
7
+ This document shows canonical usage patterns for the container. Examples are intentionally short and prioritize supported, recommended forms over convenience shorthand.
8
8
 
9
- The container performs deterministic runtime linking of ES modules and returns frozen object graphs constructed from explicitly declared dependency contracts.
9
+ ## Canonical Module Descriptor
10
10
 
11
- ## Module Structure
12
-
13
- Modules intended for container linking may expose a default export, a named export, or both. The static dependency descriptor `__deps__` is canonical when it is hierarchical and keyed by export name. It describes the dependencies of the export selected by the CDC.
14
-
15
- Dependencies are injected into the constructor as a single structured object. In TeqFW-style modules the public API is defined inside the constructor through assignments to `this`, while internal state may be held in constructor-local variables captured by closures.
16
-
17
- Example module:
11
+ The canonical form of `__deps__` is hierarchical and keyed by export name.
18
12
 
19
13
  ```js
20
14
  // @ts-check
21
15
 
22
16
  export const __deps__ = {
23
17
  default: {
24
- cast: "Fl32_Web_Helper_Cast$",
25
- },
26
- Factory: {
27
- cast: "Fl32_Web_Helper_Cast$",
18
+ cast: "App_Helper_Cast$",
28
19
  },
29
20
  };
30
21
 
31
- /**
32
- * Runtime wrapper that can hold non-DI behavior.
33
- */
34
-
35
- export default class RuntimeWrapper {
36
- constructor() {
37
- return proxy;
38
- }
39
- }
40
-
41
- /**
42
- * @typedef {Object} App_Factory$Deps
43
- * @property {object} cast
44
- */
45
-
46
- export class Factory {
22
+ export default class App_Root {
47
23
  /**
48
- * @param {App_Factory$Deps} deps
24
+ * @param {{cast: (value: unknown) => string}} deps
49
25
  */
50
26
  constructor({ cast }) {
51
- this.configure = function (params = {}) {
52
- return {
53
- mode: "factory",
54
- cast,
55
- params,
56
- };
27
+ return {
28
+ configure(params = {}) {
29
+ return {
30
+ name: cast(params.name ?? "app"),
31
+ };
32
+ },
57
33
  };
58
34
  }
59
35
  }
@@ -64,28 +40,23 @@ Dependency module:
64
40
  ```js
65
41
  // @ts-check
66
42
 
67
- export default class App_Child {
68
- constructor() {
69
- this.getName = function () {
70
- return "child";
71
- };
72
- }
43
+ export default function App_Helper_Cast() {
44
+ return function cast(value) {
45
+ return String(value);
46
+ };
73
47
  }
74
48
  ```
75
49
 
76
50
  Rules:
77
51
 
78
- - the canonical form of `__deps__` is hierarchical and keyed by export name
79
- - each export entry maps constructor dependency names to CDC identifiers
80
- - if `__deps__` is absent the module has no dependencies
81
- - a flat `__deps__` object is shorthand for limited single-export cases
82
- - dependencies are resolved recursively before instantiation
52
+ - the hierarchical export-scoped form is canonical;
53
+ - each export entry maps constructor dependency names to CDC identifiers;
54
+ - if `__deps__` is omitted, the module has no declared dependencies;
55
+ - dependencies are resolved recursively before instantiation.
83
56
 
84
- When the CDC selects `App_Module$`, the container uses the default export. When the CDC selects `App_Module__Factory$`, the container uses the named `Factory` export. In the first case the default export can act as a runtime wrapper or module shell; in the second case the named export is the DI-managed component that receives `__deps__.Factory`.
57
+ ## Canonical Container Setup
85
58
 
86
- ## Container Configuration
87
-
88
- The container is configured in the composition root of the application. Namespace roots define how CDC prefixes map to module locations.
59
+ The container is configured in the composition root before the first resolution.
89
60
 
90
61
  ```js
91
62
  import path from "node:path";
@@ -96,224 +67,128 @@ const __filename = fileURLToPath(import.meta.url);
96
67
  const __dirname = path.dirname(__filename);
97
68
 
98
69
  const container = new Container();
99
-
100
70
  container.addNamespaceRoot("App_", path.resolve(__dirname, "./src/App"), ".mjs");
101
71
  ```
102
72
 
103
- Configuration must be completed before the first dependency resolution.
104
-
105
- ## Resolving Dependencies
106
-
107
- Dependencies are obtained using CDC identifiers.
73
+ Namespace roots may also use URL-backed module-specifier bases:
108
74
 
109
75
  ```js
110
- const root = await container.get("App_Root$");
76
+ container.addNamespaceRoot("App_", "https://cdn.example.com/app", ".mjs");
111
77
  ```
112
78
 
113
- The container:
114
-
115
- - parses the CDC
116
- - loads the module
117
- - resolves dependencies declared in `__deps__`
118
- - instantiates modules
119
- - links the dependency graph
120
- - freezes the produced value
79
+ ## Resolve Root Dependency
121
80
 
122
- Example usage:
81
+ Applications typically resolve one root dependency and let the container build the full object graph.
123
82
 
124
83
  ```js
125
- console.log(root.getName());
126
- console.log(root.getChild().getName());
84
+ const root = await container.get("App_Root$");
85
+ console.log(root.configure({name: 123}).name);
127
86
  console.log(Object.isFrozen(root));
128
87
  ```
129
88
 
130
- ## Typical CDC Usage Patterns
131
-
132
- CDC identifiers define how the container interprets a dependency. The following patterns represent common usage scenarios.
133
-
134
- ### Module As-Is
135
-
136
- A CDC without lifecycle marker returns the module export as-is.
137
-
138
- ```text
139
- App_Service
140
- ```
141
-
142
- Typical usage:
143
-
144
- - utility modules
145
- - stateless helpers
146
- - constant providers
147
-
148
- Example:
149
-
150
- ```js
151
- const math = await container.get("App_Math");
152
- ```
153
-
154
- ### Singleton from Default Export
155
-
156
- A lifecycle marker `$` creates a singleton instance.
157
-
158
- ```text
159
- App_Service$
160
- ```
161
-
162
- Typical usage:
163
-
164
- - application services
165
- - repositories
166
- - infrastructure adapters
167
-
168
- ### Transient Instance
169
-
170
- Marker `$$` creates a new instance for every request.
171
-
172
- ```text
173
- App_Service$$
174
- ```
175
-
176
- Typical usage:
177
-
178
- - request objects
179
- - short-lived workers
180
- - task processors
181
-
182
- ### Named Export Resolution
183
-
184
- CDC may reference a named export using the `__ExportName` segment.
185
-
186
- ```text
187
- App_Module__build$
188
- ```
189
-
190
- Typical usage:
191
-
192
- - builders
193
- - factory entry points
194
- - specialized constructors
89
+ ## Named Export
195
90
 
196
- For modules with a runtime default export and a DI-managed named export, the canonical descriptor follows the export-scoped hierarchical form, for example:
91
+ Named exports use the `__ExportName` segment in the CDC and the same hierarchical `__deps__` structure.
197
92
 
198
93
  ```js
199
94
  export const __deps__ = {
200
95
  default: {
201
- cast: "Fl32_Web_Helper_Cast$",
96
+ cast: "App_Helper_Cast$",
202
97
  },
203
98
  Factory: {
204
- cast: "Fl32_Web_Helper_Cast$",
99
+ cast: "App_Helper_Cast$",
205
100
  },
206
101
  };
207
102
 
208
103
  export default class RuntimeWrapper {
209
104
  constructor() {
210
- return proxy;
105
+ return {mode: "runtime"};
211
106
  }
212
107
  }
213
108
 
214
109
  export class Factory {
215
110
  constructor({ cast }) {
216
111
  this.configure = function (params = {}) {
217
- return { cast, params };
112
+ return {
113
+ mode: "factory",
114
+ name: cast(params.name ?? "app"),
115
+ };
218
116
  };
219
117
  }
220
118
  }
221
119
  ```
222
120
 
223
- ### Wrapper Application
224
-
225
- Wrappers modify the produced value through postprocess plugins.
121
+ Resolution examples:
226
122
 
227
- ```text
228
- App_Service$$_log_trace
123
+ ```js
124
+ const runtime = await container.get("App_Module$");
125
+ const factory = await container.get("App_Module__Factory$");
229
126
  ```
230
127
 
231
- The container creates the dependency and applies wrappers in declared order.
232
-
233
- ### Shorthand Form
128
+ ## Singleton And Transient
234
129
 
235
- Some single-export modules may use a flat `__deps__` object as shorthand.
130
+ Common lifecycle-based compositions:
236
131
 
237
- ```js
238
- export const __deps__ = {
239
- cast: "Fl32_Web_Helper_Cast$",
240
- };
132
+ ```txt
133
+ App_Service$
134
+ App_Task$$
135
+ App_Task$$$
241
136
  ```
242
137
 
243
- This form is a convenience for limited cases and does not replace the canonical hierarchical model.
138
+ - `$` creates and reuses a singleton instance;
139
+ - `$$` creates a new instance for each request;
140
+ - `$$$` is a transient alias in the current implementation.
244
141
 
245
- ### Empty Descriptor
142
+ ## Wrappers
246
143
 
247
- Modules with no declared dependencies omit `__deps__` entirely.
144
+ Wrapper exports are selected by CDC suffixes and are applied after postprocess hooks.
248
145
 
249
- ```js
250
- export default class App_Empty {
251
- constructor() {
252
- this.ready = function () {
253
- return true;
254
- };
255
- }
256
- }
146
+ ```txt
147
+ App_Service$$_wrapLog_wrapTrace
257
148
  ```
258
149
 
259
- Typical usage:
260
-
261
- - logging
262
- - tracing
263
- - metrics collection
264
- - behavioral instrumentation
150
+ This pattern is useful when runtime behavior should be decorated without changing the service module contract.
265
151
 
266
- ### Platform Dependencies
152
+ ## Platform Modules
267
153
 
268
- CDC may reference runtime platform modules.
154
+ CDC may refer to platform modules directly.
269
155
 
270
- ```text
156
+ ```txt
271
157
  node:fs
272
158
  npm:@humanfs/core
273
159
  node:worker_threads
274
- npm:lodash
275
160
  ```
276
161
 
277
- These identifiers provide access to Node.js built-ins and npm packages.
162
+ These forms resolve the selected platform module export as-is unless a lifecycle marker is explicitly added.
278
163
 
279
- Typical usage:
164
+ ## Non-Canonical Shorthand
280
165
 
281
- - `node:worker_threads` for Node.js worker-thread primitives
282
- - `npm:@humanfs/core` for a scoped npm package dependency
283
-
284
- ### Root Graph Resolution
285
-
286
- Applications typically resolve a single root dependency.
166
+ A flat `__deps__` object is a supported shorthand for default-export-only modules, but it is not the canonical model.
287
167
 
288
168
  ```js
289
- const root = await container.get("App_Root$");
290
- ```
291
-
292
- The container recursively resolves all dependencies declared through `__deps__` and constructs the object graph. Empty descriptor modules omit `__deps__` entirely.
293
-
294
- ## Wrapper-Based Behavioral Composition
295
-
296
- Wrappers enable declarative behavioral composition at the dependency level. By attaching wrapper identifiers to CDC strings the caller can modify the runtime behavior of a dependency without modifying its module implementation.
297
-
298
- Example:
169
+ export const __deps__ = {
170
+ cast: "App_Helper_Cast$",
171
+ };
299
172
 
300
- ```text
301
- App_Service$$_log
173
+ export default class App_Short {
174
+ constructor({ cast }) {
175
+ this.cast = cast;
176
+ }
177
+ }
302
178
  ```
303
179
 
304
- The service module remains unaware of logging. The wrapper introduces cross-cutting behavior during the container postprocess stage.
180
+ Prefer the hierarchical form for new integrations and for any module that exposes named exports.
305
181
 
306
- This mechanism enables patterns similar to aspect-oriented programming while preserving deterministic dependency resolution.
182
+ ## Empty Descriptor
307
183
 
308
- ## Minimal Smoke Pattern
309
-
310
- A minimal integration scenario demonstrating container operation:
184
+ Modules with no declared dependencies omit `__deps__` entirely.
311
185
 
312
186
  ```js
313
- const container = new Container();
314
- container.addNamespaceRoot("Fx_", FIXTURE_DIR, ".mjs");
315
-
316
- const value = await container.get("Fx_Root$");
187
+ export default class App_Empty {
188
+ constructor() {
189
+ this.ready = function () {
190
+ return true;
191
+ };
192
+ }
193
+ }
317
194
  ```
318
-
319
- The container loads modules, resolves dependencies, instantiates objects, and returns a frozen result.