@teqfw/di 2.5.1 → 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 dependency descriptor. Accepted forms are: absent/empty, a flat Record<dependencyKey, CDC string> for a dependency-free default export or limited single-export shorthand, or a hierarchical Record<exportName, Record<dependencyKey, CDC string>> for named exports.',
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 runtime accepts hierarchical descriptors for named exports and the flat shorthand form for dependency-free default exports or 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,26 +1,22 @@
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
- Minimal single-export example:
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
- cast: "App_Helper_Cast$",
17
+ default: {
18
+ cast: "App_Helper_Cast$",
19
+ },
24
20
  };
25
21
 
26
22
  export default class App_Root {
@@ -53,48 +49,14 @@ export default function App_Helper_Cast() {
53
49
 
54
50
  Rules:
55
51
 
56
- - the canonical form of `__deps__` is hierarchical and keyed by export name
57
- - each export entry maps constructor dependency names to CDC identifiers
58
- - if `__deps__` is absent the module has no dependencies
59
- - a flat `__deps__` object is shorthand for a dependency-free default export or limited single-export cases
60
- - a named export may declare dependencies without requiring a `default` entry
61
- - dependencies are resolved recursively before instantiation
62
-
63
- When the CDC selects `App_Module$`, the container uses the default export. When the CDC selects `App_Module__Factory$`, the container uses the named export `Factory`.
64
-
65
- Canonical export-scoped form:
66
-
67
- ```js
68
- export const __deps__ = {
69
- default: {
70
- cast: "App_Helper_Cast$",
71
- },
72
- Factory: {
73
- cast: "App_Helper_Cast$",
74
- },
75
- };
76
-
77
- export default class RuntimeWrapper {
78
- constructor() {
79
- return {mode: "wrapper"};
80
- }
81
- }
82
-
83
- export class Factory {
84
- constructor({ cast }) {
85
- this.configure = function (params = {}) {
86
- return {
87
- mode: "factory",
88
- name: cast(params.name ?? "app"),
89
- };
90
- };
91
- }
92
- }
93
- ```
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.
94
56
 
95
- ## Container Configuration
57
+ ## Canonical Container Setup
96
58
 
97
- 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.
98
60
 
99
61
  ```js
100
62
  import path from "node:path";
@@ -105,230 +67,128 @@ const __filename = fileURLToPath(import.meta.url);
105
67
  const __dirname = path.dirname(__filename);
106
68
 
107
69
  const container = new Container();
108
-
109
70
  container.addNamespaceRoot("App_", path.resolve(__dirname, "./src/App"), ".mjs");
110
71
  ```
111
72
 
112
- Configuration must be completed before the first dependency resolution.
113
-
114
- Browser-oriented or isomorphic configuration may also use URL-based import roots:
73
+ Namespace roots may also use URL-backed module-specifier bases:
115
74
 
116
75
  ```js
117
76
  container.addNamespaceRoot("App_", "https://cdn.example.com/app", ".mjs");
118
- container.addNamespaceRoot("Web_", "//cdn.example.com/web", ".mjs");
119
77
  ```
120
78
 
121
- ## Resolving Dependencies
79
+ ## Resolve Root Dependency
122
80
 
123
- Dependencies are obtained using CDC identifiers.
81
+ Applications typically resolve one root dependency and let the container build the full object graph.
124
82
 
125
83
  ```js
126
84
  const root = await container.get("App_Root$");
127
- ```
128
-
129
- The container:
130
-
131
- - parses the CDC
132
- - loads the module
133
- - resolves dependencies declared in `__deps__`
134
- - instantiates modules
135
- - links the dependency graph
136
- - freezes the produced value
137
-
138
- Example usage:
139
-
140
- ```js
141
85
  console.log(root.configure({name: 123}).name);
142
86
  console.log(Object.isFrozen(root));
143
87
  ```
144
88
 
145
- ## Typical CDC Usage Patterns
146
-
147
- CDC identifiers define how the container interprets a dependency. The following patterns represent common usage scenarios.
148
-
149
- ### Module As-Is
150
-
151
- A CDC without lifecycle marker returns the module export as-is.
152
-
153
- ```text
154
- App_Service
155
- ```
156
-
157
- Typical usage:
158
-
159
- - utility modules
160
- - stateless helpers
161
- - constant providers
162
-
163
- Example:
164
-
165
- ```js
166
- const math = await container.get("App_Math");
167
- ```
168
-
169
- ### Singleton from Default Export
170
-
171
- A lifecycle marker `$` creates a singleton instance.
172
-
173
- ```text
174
- App_Service$
175
- ```
176
-
177
- Typical usage:
178
-
179
- - application services
180
- - repositories
181
- - infrastructure adapters
182
-
183
- ### Transient Instance
184
-
185
- Marker `$$` creates a new instance for every request.
186
-
187
- ```text
188
- App_Service$$
189
- ```
190
-
191
- Typical usage:
192
-
193
- - request objects
194
- - short-lived workers
195
- - task processors
196
-
197
- ### Named Export Resolution
198
-
199
- CDC may reference a named export using the `__ExportName` segment.
200
-
201
- ```text
202
- App_Module__build$
203
- ```
204
-
205
- Typical usage:
206
-
207
- - builders
208
- - factory entry points
209
- - specialized constructors
89
+ ## Named Export
210
90
 
211
- 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.
212
92
 
213
93
  ```js
214
94
  export const __deps__ = {
215
95
  default: {
216
- cast: "Fl32_Web_Helper_Cast$",
96
+ cast: "App_Helper_Cast$",
217
97
  },
218
98
  Factory: {
219
- cast: "Fl32_Web_Helper_Cast$",
99
+ cast: "App_Helper_Cast$",
220
100
  },
221
101
  };
222
102
 
223
103
  export default class RuntimeWrapper {
224
104
  constructor() {
225
- return {mode: "wrapper"};
105
+ return {mode: "runtime"};
226
106
  }
227
107
  }
228
108
 
229
109
  export class Factory {
230
110
  constructor({ cast }) {
231
111
  this.configure = function (params = {}) {
232
- return {cast, params};
112
+ return {
113
+ mode: "factory",
114
+ name: cast(params.name ?? "app"),
115
+ };
233
116
  };
234
117
  }
235
118
  }
236
119
  ```
237
120
 
238
- ### Wrapper Application
121
+ Resolution examples:
239
122
 
240
- Wrappers modify the produced value through postprocess plugins.
241
-
242
- ```text
243
- App_Service$$_log_trace
123
+ ```js
124
+ const runtime = await container.get("App_Module$");
125
+ const factory = await container.get("App_Module__Factory$");
244
126
  ```
245
127
 
246
- The container creates the dependency and applies wrappers in declared order.
128
+ ## Singleton And Transient
247
129
 
248
- ### Shorthand Form
130
+ Common lifecycle-based compositions:
249
131
 
250
- Some single-export modules may use a flat `__deps__` object as shorthand.
251
-
252
- ```js
253
- export const __deps__ = {
254
- cast: "App_Helper_Cast$",
255
- };
132
+ ```txt
133
+ App_Service$
134
+ App_Task$$
135
+ App_Task$$$
256
136
  ```
257
137
 
258
- 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.
259
141
 
260
- ### Empty Descriptor
142
+ ## Wrappers
261
143
 
262
- Modules with no declared dependencies omit `__deps__` entirely.
144
+ Wrapper exports are selected by CDC suffixes and are applied after postprocess hooks.
263
145
 
264
- ```js
265
- export default class App_Empty {
266
- constructor() {
267
- this.ready = function () {
268
- return true;
269
- };
270
- }
271
- }
146
+ ```txt
147
+ App_Service$$_wrapLog_wrapTrace
272
148
  ```
273
149
 
274
- Typical usage:
150
+ This pattern is useful when runtime behavior should be decorated without changing the service module contract.
275
151
 
276
- - logging
277
- - tracing
278
- - metrics collection
279
- - behavioral instrumentation
152
+ ## Platform Modules
280
153
 
281
- ### Platform Dependencies
154
+ CDC may refer to platform modules directly.
282
155
 
283
- CDC may reference runtime platform modules.
284
-
285
- ```text
156
+ ```txt
286
157
  node:fs
287
158
  npm:@humanfs/core
288
159
  node:worker_threads
289
- npm:lodash
290
160
  ```
291
161
 
292
- These identifiers provide access to Node.js built-ins and npm packages.
293
-
294
- Typical usage:
162
+ These forms resolve the selected platform module export as-is unless a lifecycle marker is explicitly added.
295
163
 
296
- - `node:worker_threads` for Node.js worker-thread primitives
297
- - `npm:@humanfs/core` for a scoped npm package dependency
164
+ ## Non-Canonical Shorthand
298
165
 
299
- ### Root Graph Resolution
300
-
301
- 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.
302
167
 
303
168
  ```js
304
- const root = await container.get("App_Root$");
305
- ```
306
-
307
- The container recursively resolves all dependencies declared through `__deps__` and constructs the object graph. Empty descriptor modules omit `__deps__` entirely.
308
-
309
- ## Wrapper-Based Behavioral Composition
310
-
311
- 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.
312
-
313
- Example:
169
+ export const __deps__ = {
170
+ cast: "App_Helper_Cast$",
171
+ };
314
172
 
315
- ```text
316
- App_Service$$_log
173
+ export default class App_Short {
174
+ constructor({ cast }) {
175
+ this.cast = cast;
176
+ }
177
+ }
317
178
  ```
318
179
 
319
- The service module remains unaware of logging. The wrapper introduces cross-cutting behavior during the container postprocess stage.
320
-
321
- This mechanism enables patterns similar to aspect-oriented programming while preserving deterministic dependency resolution.
180
+ Prefer the hierarchical form for new integrations and for any module that exposes named exports.
322
181
 
323
- ## Minimal Smoke Pattern
182
+ ## Empty Descriptor
324
183
 
325
- A minimal integration scenario demonstrating container operation:
184
+ Modules with no declared dependencies omit `__deps__` entirely.
326
185
 
327
186
  ```js
328
- const container = new Container();
329
- container.addNamespaceRoot("Fx_", FIXTURE_DIR, ".mjs");
330
-
331
- 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
+ }
332
194
  ```
333
-
334
- The container loads modules, resolves dependencies, instantiates objects, and returns a frozen result.