@teqfw/di 2.6.2 → 2.8.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.
@@ -1,6 +1,6 @@
1
1
  # extensions.md
2
2
 
3
- Version: 20260606
3
+ Version: 20260730
4
4
 
5
5
  ## Purpose
6
6
 
@@ -12,9 +12,19 @@ The container supports controlled extension of dependency resolution through thr
12
12
 
13
13
  These mechanisms are related but not interchangeable.
14
14
 
15
+ ## Contents
16
+
17
+ - [Preprocess Hooks](#preprocess-hooks)
18
+ - [Postprocess Hooks](#postprocess-hooks)
19
+ - [Wrapper Exports](#wrapper-exports)
20
+ - [Execution Order](#execution-order)
21
+ - [Constraints](#constraints)
22
+
15
23
  ## Preprocess Hooks
16
24
 
17
- Preprocess hooks transform CDC identities before resolution.
25
+ Preprocess hooks transform parsed dependency identities before resolution.
26
+
27
+ They apply to the root request and every dependency declaration discovered transitively in the requested graph, exactly once per occurrence.
18
28
 
19
29
  Signature:
20
30
 
@@ -31,10 +41,11 @@ Properties:
31
41
 
32
42
  Typical uses:
33
43
 
34
- - CDC alias rewriting;
44
+ - Dependency Specifier alias rewriting;
35
45
  - policy-driven identifier normalization;
36
46
  - project-specific prefix adaptation.
37
47
 
48
+
38
49
  ## Postprocess Hooks
39
50
 
40
51
  Postprocess hooks transform resolved values after instantiation and before wrapper exports.
@@ -42,7 +53,7 @@ Postprocess hooks transform resolved values after instantiation and before wrapp
42
53
  Signature:
43
54
 
44
55
  ```js
45
- (value) => value
56
+ (value, context) => value
46
57
  ```
47
58
 
48
59
  Properties:
@@ -50,7 +61,7 @@ Properties:
50
61
  - registered with `addPostprocess()`;
51
62
  - run for every resolved value;
52
63
  - run in declared order;
53
- - do not alter CDC parsing or module resolution.
64
+ - do not alter dependency specifier parsing or module resolution.
54
65
 
55
66
  Typical uses:
56
67
 
@@ -60,7 +71,7 @@ Typical uses:
60
71
 
61
72
  ## Wrapper Exports
62
73
 
63
- Wrapper exports are selected directly from CDC suffixes and are resolved from the same module namespace as the dependency being composed.
74
+ Wrapper exports are selected directly from dependency specifier suffixes and are resolved from the same module namespace as the dependency being composed.
64
75
 
65
76
  Signature:
66
77
 
@@ -71,7 +82,7 @@ Signature:
71
82
  Properties:
72
83
 
73
84
  - selected by wrapper suffixes such as `_wrapLog`;
74
- - applied only when present in the CDC;
85
+ - applied only when present in the Dependency Specifier;
75
86
  - executed after postprocess hooks and before freeze;
76
87
  - not registered globally in the container.
77
88
 
@@ -66,28 +66,23 @@ export interface PackageApiContract {
66
66
  */
67
67
  export const PACKAGE_API: PackageApiContract = {
68
68
  packageName: '@teqfw/di',
69
- packageRole: 'Deterministic runtime DI container for native ES modules with explicit CDC contracts.',
69
+ packageRole: 'Deterministic runtime DI container for native ES modules with explicit Dependency Specifier contracts.',
70
70
  canonicalEntrypoints: [
71
71
  '@teqfw/di',
72
- '@teqfw/di/src/Config/NamespaceRegistry.mjs',
72
+ '@teqfw/di/node/registry/namespace',
73
+ '@teqfw/di/node/registry/package',
73
74
  ],
74
75
  publicRuntime: [
75
76
  {
76
77
  alias: 'TeqFw_Di_Container',
77
78
  kind: 'class',
78
- role: 'Primary runtime composition root. Resolves CDC identifiers into frozen linked values.',
79
+ role: 'Primary runtime composition root. Resolves Dependency Specifiers into frozen linked values.',
79
80
  imports: [
80
81
  {
81
82
  specifier: '@teqfw/di',
82
83
  exportName: 'default',
83
84
  canonical: true,
84
85
  },
85
- {
86
- specifier: '@teqfw/di/src/Container.mjs',
87
- exportName: 'default',
88
- canonical: false,
89
- note: 'Explicit source subpath export. Prefer the package root import.',
90
- },
91
86
  ],
92
87
  methods: [
93
88
  {
@@ -100,7 +95,7 @@ export const PACKAGE_API: PackageApiContract = {
100
95
  name: 'addNamespaceRoot',
101
96
  signature: 'addNamespaceRoot(prefix: string, target: string, defaultExt: string): void',
102
97
  stage: 'builder',
103
- summary: 'Registers one namespace root that maps a CDC prefix to a module-specifier base.',
98
+ summary: 'Registers one namespace root that maps a Module Token prefix to a module-specifier base.',
104
99
  constraints: [
105
100
  'Allowed only before the first get().',
106
101
  'The target may be filesystem-backed or URL-backed depending on runtime environment.',
@@ -111,10 +106,10 @@ export const PACKAGE_API: PackageApiContract = {
111
106
  name: 'addPreprocess',
112
107
  signature: 'addPreprocess(fn: (depId: TeqFw_Di_DepId$DTO) => TeqFw_Di_DepId$DTO): void',
113
108
  stage: 'builder',
114
- summary: 'Adds an ordered CDC preprocessing hook.',
109
+ summary: 'Adds an ordered parsed dependency identity preprocessing hook.',
115
110
  constraints: [
116
111
  'Allowed only before the first get().',
117
- 'Each hook must return another DepId DTO.',
112
+ 'Each hook is invoked synchronously in registration order and must return another DepId DTO.',
118
113
  ],
119
114
  },
120
115
  {
@@ -124,7 +119,7 @@ export const PACKAGE_API: PackageApiContract = {
124
119
  summary: 'Adds an ordered value transform applied to every resolved value after instantiation.',
125
120
  constraints: [
126
121
  'Allowed only before the first get().',
127
- 'Runs after instantiation and before wrapper exports.',
122
+ 'Runs synchronously in registration order after instantiation and before wrapper exports.',
128
123
  ],
129
124
  },
130
125
  {
@@ -147,9 +142,9 @@ export const PACKAGE_API: PackageApiContract = {
147
142
  },
148
143
  {
149
144
  name: 'register',
150
- signature: 'register(cdc: string, mock: unknown): void',
145
+ signature: 'register(specifier: string, mock: unknown): void',
151
146
  stage: 'builder',
152
- summary: 'Registers a mock by canonical DepId identity after parsing the provided CDC.',
147
+ summary: 'Registers a mock by canonical DepId identity after parsing the provided Dependency Specifier.',
153
148
  constraints: [
154
149
  'Allowed only before the first get().',
155
150
  'Requires enableTestMode() first.',
@@ -157,7 +152,7 @@ export const PACKAGE_API: PackageApiContract = {
157
152
  },
158
153
  {
159
154
  name: 'get',
160
- signature: 'get(cdc: string): Promise<any>',
155
+ signature: 'get(specifier: string): Promise<any>',
161
156
  stage: 'runtime',
162
157
  summary: 'Parses, preprocesses, resolves, instantiates, postprocesses, wraps, applies lifecycle, freezes, and returns a linked value.',
163
158
  constraints: [
@@ -169,12 +164,22 @@ export const PACKAGE_API: PackageApiContract = {
169
164
  ],
170
165
  },
171
166
  {
172
- alias: 'TeqFw_Di_Config_NamespaceRegistry',
167
+ alias: 'TeqFw_Di_Node_Registry_Package',
168
+ kind: 'class',
169
+ role: 'Node.js-only composition-stage static runtime package graph builder.',
170
+ imports: [{specifier: '@teqfw/di/node/registry/package', exportName: 'default', canonical: true}],
171
+ methods: [
172
+ {name: 'constructor', signature: 'new PackageRegistry({ fs, path, appRoot })', stage: 'composition', summary: 'Creates a static package graph builder.'},
173
+ {name: 'build', signature: 'build(): Promise<ReadonlyArray<PackageRecord>>', stage: 'composition', summary: 'Builds immutable dependency-first postorder package records; independent dependency names use ascending lexical order as the stable tie-breaker.', constraints: ['Reads only transitive dependencies and static package metadata.', 'Does not load modules or interpret application providers.']},
174
+ ],
175
+ },
176
+ {
177
+ alias: 'TeqFw_Di_Node_Registry_Namespace',
173
178
  kind: 'class',
174
- role: 'Composition-stage helper that discovers namespace roots from the root package and installed npm dependencies.',
179
+ role: 'Node.js-only composition-stage helper that discovers namespace roots from the root package and installed npm dependencies.',
175
180
  imports: [
176
181
  {
177
- specifier: '@teqfw/di/src/Config/NamespaceRegistry.mjs',
182
+ specifier: '@teqfw/di/node/registry/namespace',
178
183
  exportName: 'default',
179
184
  canonical: true,
180
185
  },
@@ -186,7 +191,7 @@ export const PACKAGE_API: PackageApiContract = {
186
191
  stage: 'composition',
187
192
  summary: 'Creates a registry builder over filesystem and path adapters.',
188
193
  constraints: [
189
- 'Intended for composition root code, not for DI-managed runtime modules.',
194
+ 'Intended only for Node.js composition root code, not for DI-managed or browser runtime modules.',
190
195
  'Consumes static package.json metadata only.',
191
196
  ],
192
197
  },
@@ -196,13 +201,16 @@ export const PACKAGE_API: PackageApiContract = {
196
201
  stage: 'composition',
197
202
  summary: 'Builds an immutable namespace registry sorted by descending prefix length.',
198
203
  constraints: [
199
- 'Fails fast on invalid namespace metadata or duplicate canonical prefixes.',
204
+ 'Selects package.json#teqfw.fw.di.namespaces when present; otherwise temporarily falls back to the legacy package.json#teqfw.namespaces array.',
205
+ 'Both schemas require arrays; canonical metadata wins exclusively and the arrays are never merged.',
206
+ 'Fails fast on invalid selected metadata or duplicate prefixes with publisher attribution.',
200
207
  ],
201
208
  },
202
209
  ],
203
210
  },
204
211
  ],
205
212
  structuralContracts: [
213
+ {name: 'Runtime Package Record', kind: 'protocol', aliases: ['TeqFw_Di_Node_Registry_Package_Record'], summary: 'Immutable static package metadata record.', fields: {name: 'Declared package name.', rootAbs: 'Resolved package root.', rootReal: 'Canonical package root.', packageJson: 'Recursively frozen package metadata.'}},
206
214
  {
207
215
  name: 'DepId DTO',
208
216
  kind: 'dto',
@@ -215,7 +223,7 @@ export const PACKAGE_API: PackageApiContract = {
215
223
  composition: '"A" | "F" in the current implementation.',
216
224
  life: '"S" | "T" | null in the current implementation.',
217
225
  wrappers: 'Ordered wrapper export names.',
218
- origin: 'Original CDC string for diagnostics.',
226
+ origin: 'Original Dependency Specifier string for diagnostics.',
219
227
  },
220
228
  notes: [
221
229
  'Identity excludes origin.',
@@ -245,8 +253,8 @@ export const PACKAGE_API: PackageApiContract = {
245
253
  kind: 'module-contract',
246
254
  summary: 'Shape expected from application modules resolved by the container.',
247
255
  fields: {
248
- __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.',
249
- moduleNamespace: 'Whole ES module namespace object returned for as-is CDC without selected export.',
256
+ __deps__: 'Optional dependency descriptor. Canonical form is hierarchical Record<exportName, Record<dependencyKey, Dependency Specifier string>>. Supported shorthand form is a flat Record<dependencyKey, Dependency Specifier string> for default-export-only modules. Omission means there are no declared dependencies.',
257
+ moduleNamespace: 'Whole ES module namespace object returned for as-is Dependency Specifier without selected export.',
250
258
  defaultExport: 'Used when the parsed DepId selects exportName="default" for factory composition.',
251
259
  namedExports: 'May be selected via __ExportName for factory composition and may also provide wrapper exports.',
252
260
  wrapperExport: 'Named export whose identifier appears in depId.wrappers; it must be synchronous and unary.',
@@ -260,17 +268,30 @@ export const PACKAGE_API: PackageApiContract = {
260
268
  ],
261
269
  typeMapClassification: [
262
270
  {
263
- alias: 'TeqFw_Di_Config_NamespaceRegistry',
264
- source: './src/Config/NamespaceRegistry.mjs',
271
+ alias: 'TeqFw_Di_Node_Registry_Package',
272
+ source: './src/Node/Registry/Package.mjs',
273
+ exposure: 'public-runtime',
274
+ reason: 'Exported through package.json exports as the supported Node.js composition subpath.',
275
+ canonicalUse: 'Node.js composition-stage package graph imported from @teqfw/di/node/registry/package.',
276
+ },
277
+ {
278
+ alias: 'TeqFw_Di_Node_Registry_Package_Record',
279
+ source: './src/Node/Registry/Package.mjs#record',
280
+ exposure: 'public-structural',
281
+ reason: 'Public immutable record contract returned by PackageRegistry.build().',
282
+ },
283
+ {
284
+ alias: 'TeqFw_Di_Node_Registry_Namespace',
285
+ source: './src/Node/Registry/Namespace.mjs',
265
286
  exposure: 'public-runtime',
266
- reason: 'Exported through package.json exports as a supported subpath entrypoint.',
267
- canonicalUse: 'Composition-stage helper imported from @teqfw/di/src/Config/NamespaceRegistry.mjs.',
287
+ reason: 'Exported through package.json exports as the supported Node.js composition subpath.',
288
+ canonicalUse: 'Node.js composition-stage helper imported from @teqfw/di/node/registry/namespace.',
268
289
  },
269
290
  {
270
291
  alias: 'TeqFw_Di_Container',
271
292
  source: './src/Container.mjs',
272
293
  exposure: 'public-runtime',
273
- reason: 'Default export of the package root and of the explicit source subpath.',
294
+ reason: 'Default export of the package root.',
274
295
  canonicalUse: 'Primary container API imported from @teqfw/di.',
275
296
  },
276
297
  {
@@ -280,25 +301,25 @@ export const PACKAGE_API: PackageApiContract = {
280
301
  reason: 'Internal immutable-core helper. Not exported via package.json.',
281
302
  },
282
303
  {
283
- alias: 'TeqFw_Di_Container_Instantiate_Instantiator',
304
+ alias: 'TeqFw_Di_Container_Instantiate',
284
305
  source: './src/Container/Instantiate.mjs',
285
306
  exposure: 'internal',
286
307
  reason: 'Internal immutable-core helper. Not exported via package.json.',
287
308
  },
288
309
  {
289
- alias: 'TeqFw_Di_Container_Lifecycle_Registry',
310
+ alias: 'TeqFw_Di_Container_Lifecycle',
290
311
  source: './src/Container/Lifecycle.mjs',
291
312
  exposure: 'internal',
292
313
  reason: 'Internal lifecycle cache component. Not exported via package.json.',
293
314
  },
294
315
  {
295
- alias: 'TeqFw_Di_Container_Resolve_GraphResolver',
316
+ alias: 'TeqFw_Di_Container_GraphResolver',
296
317
  source: './src/Container/GraphResolver.mjs',
297
318
  exposure: 'internal',
298
319
  reason: 'Internal graph builder used by Container.get(). Not exported via package.json.',
299
320
  },
300
321
  {
301
- alias: 'TeqFw_Di_Container_Wrapper_Executor',
322
+ alias: 'TeqFw_Di_Container_Executor',
302
323
  source: './src/Container/Executor.mjs',
303
324
  exposure: 'internal',
304
325
  reason: 'Internal wrapper-stage executor. Not exported via package.json.',
@@ -399,11 +420,13 @@ export const PACKAGE_API: PackageApiContract = {
399
420
  },
400
421
  ],
401
422
  operationalNotes: [
402
- 'Only two runtime entrypoints are supported by package.json exports: @teqfw/di and @teqfw/di/src/Config/NamespaceRegistry.mjs.',
423
+ 'Canonical Node.js registry entrypoints are @teqfw/di/node/registry/namespace and @teqfw/di/node/registry/package. They must not be imported by browser runtime modules.',
424
+ 'The legacy compatibility entry point @teqfw/di/src/Config/NamespaceRegistry.mjs is deprecated. Every other src/** path is internal and unsupported; new code must use @teqfw/di/node/registry/namespace.',
425
+ 'NamespaceRegistry uses the canonical package.json#teqfw.fw.di.namespaces array and temporarily falls back to package.json#teqfw.namespaces only when canonical metadata is absent.',
403
426
  'Resolved values are frozen before being returned.',
404
427
  'The hierarchical export-scoped descriptor is canonical. The flat shorthand descriptor is supported only for default-export-only modules.',
405
428
  'Named wrapper exports are executed after addPostprocess() hooks and before freeze.',
406
- 'In the current implementation, CDC markers $$ and $$$ both end up as transient/no-cache behavior.',
429
+ '$$ selects transient lifecycle; $$$ explicitly selects direct lifecycle.',
407
430
  '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.',
408
431
  ],
409
432
  } as const;
@@ -0,0 +1,283 @@
1
+ # usage.md
2
+
3
+ Version: 20260730
4
+
5
+ ## Purpose
6
+
7
+ This document shows canonical usage patterns for the container. Examples are intentionally short and prioritize supported, recommended forms over convenience shorthand.
8
+
9
+ ## Contents
10
+
11
+ - [Canonical Module Descriptor](#canonical-module-descriptor)
12
+ - [Canonical Container Setup](#canonical-container-setup)
13
+ - [Lifecycle Composition](#lifecycle-composition)
14
+ - [Test Mode And Mocks](#test-mode-and-mocks)
15
+ - [Browser Entry Point](#browser-entry-point)
16
+ - [Package-Backed Composition](#package-backed-composition)
17
+ - [Package Namespace Metadata](#package-namespace-metadata)
18
+
19
+ ## Canonical Module Descriptor
20
+
21
+ The preferred module exposes one Principal Application Value through `default export`. The `__deps__` export is declarative metadata listing the values required to link it. The canonical declaration form is hierarchical and keyed by export name.
22
+
23
+ ```js
24
+ // @ts-check
25
+
26
+ export const __deps__ = {
27
+ default: {
28
+ cast: "App_Helper_Cast$",
29
+ },
30
+ };
31
+
32
+ export default class App_Root {
33
+ /**
34
+ * @param {{cast: (value: unknown) => string}} deps
35
+ */
36
+ constructor({ cast }) {
37
+ return {
38
+ configure(params = {}) {
39
+ return {
40
+ name: cast(params.name ?? "app"),
41
+ };
42
+ },
43
+ };
44
+ }
45
+ }
46
+ ```
47
+
48
+ Dependency module:
49
+
50
+ ```js
51
+ // @ts-check
52
+
53
+ export default function App_Helper_Cast() {
54
+ return function cast(value) {
55
+ return String(value);
56
+ };
57
+ }
58
+ ```
59
+
60
+ Rules:
61
+
62
+ - the hierarchical export-scoped form is canonical;
63
+ - each export entry maps constructor dependency names to dependency specifiers;
64
+ - if `__deps__` is omitted, the module has no declared dependencies;
65
+ - dependencies are resolved recursively before instantiation.
66
+
67
+ ## Canonical Container Setup
68
+
69
+ The container is configured in the composition root before the first resolution.
70
+
71
+ ```js
72
+ import path from "node:path";
73
+ import { fileURLToPath } from "node:url";
74
+ import Container from "@teqfw/di";
75
+
76
+ const __filename = fileURLToPath(import.meta.url);
77
+ const __dirname = path.dirname(__filename);
78
+
79
+ const container = new Container();
80
+ container.addNamespaceRoot("App_", path.resolve(__dirname, "./src/App"), ".mjs");
81
+ ```
82
+
83
+ Namespace roots may also use URL-backed module-specifier bases:
84
+
85
+ ```js
86
+ container.addNamespaceRoot("App_", "https://cdn.example.com/app", ".mjs");
87
+ ```
88
+
89
+ ## Resolve Root Dependency
90
+
91
+ Applications typically resolve one root dependency and let the container build the full object graph.
92
+
93
+ ```js
94
+ const root = await container.get("App_Root$");
95
+ console.log(root.configure({name: 123}).name);
96
+ console.log(Object.isFrozen(root));
97
+ ```
98
+
99
+ ## Named Export
100
+
101
+ Named exports are a JavaScript ecosystem compatibility surface. They use the `__ExportName` segment in the Dependency Specifier and the same hierarchical `__deps__` structure.
102
+
103
+ ```js
104
+ export const __deps__ = {
105
+ default: {
106
+ cast: "App_Helper_Cast$",
107
+ },
108
+ Factory: {
109
+ cast: "App_Helper_Cast$",
110
+ },
111
+ };
112
+
113
+ export default class RuntimeWrapper {
114
+ constructor() {
115
+ return {mode: "runtime"};
116
+ }
117
+ }
118
+
119
+ export class Factory {
120
+ constructor({ cast }) {
121
+ this.configure = function (params = {}) {
122
+ return {
123
+ mode: "factory",
124
+ name: cast(params.name ?? "app"),
125
+ };
126
+ };
127
+ }
128
+ }
129
+ ```
130
+
131
+ Resolution examples:
132
+
133
+ ```js
134
+ const runtime = await container.get("App_Module$");
135
+ const factory = await container.get("App_Module__Factory$");
136
+ ```
137
+
138
+ Without a lifecycle marker, the export is resolved as-is — the class or function itself, not an instance:
139
+
140
+ ```js
141
+ const FactoryClass = await container.get("App_Module__Factory");
142
+ const factory = new FactoryClass({cast: resolvedCast});
143
+ ```
144
+
145
+ ## Lifecycle Composition
146
+
147
+ Common lifecycle-based compositions:
148
+
149
+ ```txt
150
+ App_Service$
151
+ App_Task$$
152
+ App_Task$$$
153
+ ```
154
+
155
+ - `$` creates and reuses a singleton instance;
156
+ - `$$` creates a new instance for each request;
157
+ - `$$$` uses direct factory composition: it produces a value for the request without lifecycle caching.
158
+
159
+ ## Wrappers
160
+
161
+ Wrapper exports are selected by dependency specifier suffixes and are applied after postprocess hooks.
162
+
163
+ ```txt
164
+ App_Service$$_wrapLog_wrapTrace
165
+ ```
166
+
167
+ This pattern is useful when runtime behavior should be decorated without changing the service module contract.
168
+
169
+ ## Platform Modules
170
+
171
+ Dependency Specifier may refer to platform modules directly.
172
+
173
+ ```txt
174
+ node:fs
175
+ npm:@humanfs/core
176
+ node:worker_threads
177
+ ```
178
+
179
+ These forms resolve the selected platform module export as-is unless a lifecycle marker is explicitly added.
180
+
181
+ ## Non-Canonical Shorthand
182
+
183
+ A flat `__deps__` object is a supported shorthand for default-export-only modules, but it is not the canonical model.
184
+
185
+ ```js
186
+ export const __deps__ = {
187
+ cast: "App_Helper_Cast$",
188
+ };
189
+
190
+ export default class App_Short {
191
+ constructor({ cast }) {
192
+ this.cast = cast;
193
+ }
194
+ }
195
+ ```
196
+
197
+ Prefer the hierarchical form for new integrations and for any module that exposes named exports.
198
+
199
+ ## Empty Descriptor
200
+
201
+ Modules with no declared dependencies omit `__deps__` entirely.
202
+
203
+ ```js
204
+ export default class App_Empty {
205
+ constructor() {
206
+ this.ready = function () {
207
+ return true;
208
+ };
209
+ }
210
+ }
211
+ ```
212
+
213
+ ## Package-Backed Composition
214
+
215
+ In Node.js, a composition root may import `PackageRegistry` from `@teqfw/di/node/registry/package`, import `NamespaceRegistry` from `@teqfw/di/node/registry/namespace`, use the latter for namespace roots, and independently inspect each immutable package record for application-owned metadata. Package discovery reads only static manifests and transitive dependencies; it does not resolve container values or interpret providers. These imports are forbidden in browser runtime modules. All `src/**` package subpaths are unsupported except the deprecated `@teqfw/di/src/Config/NamespaceRegistry.mjs` compatibility import; new code must use `@teqfw/di/node/registry/namespace` instead.
216
+
217
+ Build namespace roots and register each one before the first `get()`:
218
+
219
+ ```js
220
+ import fs from "node:fs/promises";
221
+ import path from "node:path";
222
+ import Container from "@teqfw/di";
223
+ import NamespaceRegistry from "@teqfw/di/node/registry/namespace";
224
+
225
+ const appRoot = "/absolute/path/to/application";
226
+ const container = new Container();
227
+ const namespaces = await new NamespaceRegistry({fs, path, appRoot}).build();
228
+
229
+ for (const {prefix, dirAbs, ext} of namespaces) {
230
+ container.addNamespaceRoot(prefix, dirAbs, ext);
231
+ }
232
+
233
+ const root = await container.get("App_Root$");
234
+ ```
235
+
236
+ `appRoot` is the absolute application root containing its `package.json`. Do not assume that the current working directory is that root unless the host application guarantees it.
237
+
238
+ ## Test Mode And Mocks
239
+
240
+ Enable test mode and register mocks before the first `get()`. A registered mock is selected by canonical Dependency Specifier identity, bypasses resolution and instantiation, and is frozen before it is returned.
241
+
242
+ ```js
243
+ const container = new Container();
244
+ container.enableTestMode();
245
+ container.register("App_Service$", mockService);
246
+
247
+ const service = await container.get("App_Service$");
248
+ ```
249
+
250
+ Do not use test mode as an application-time substitution mechanism: configuration is locked by the first `get()`.
251
+
252
+ ## Browser Entry Point
253
+
254
+ The container and URL-backed namespace roots can be used in browser ESM code. Node.js registry utilities are excluded from browser runtime code.
255
+
256
+ ```html
257
+ <script type="module">
258
+ import Container from "https://cdn.jsdelivr.net/npm/@teqfw/di@2/+esm";
259
+
260
+ const container = new Container();
261
+ container.addNamespaceRoot("App_", "https://cdn.example.com/app", ".mjs");
262
+ </script>
263
+ ```
264
+
265
+ ## Package Namespace Metadata
266
+
267
+ A package declares namespace mappings in `package.json` using `teqfw.fw.di.namespaces`; the declaration is always an array, including one mapping.
268
+
269
+ ```json
270
+ {
271
+ "teqfw": {
272
+ "fw": {
273
+ "di": {
274
+ "namespaces": [
275
+ {"prefix": "App_", "path": "./src", "ext": ".mjs"}
276
+ ]
277
+ }
278
+ }
279
+ }
280
+ }
281
+ ```
282
+
283
+ Each `path` is a non-empty relative path from its publishing package, must resolve to an existing directory inside that package, and must use an ESM-compatible extension. Canonical metadata takes precedence over the legacy `teqfw.namespaces` array; they are never merged. Build registries and add their roots during composition, before the first `container.get()`.
package/src/AGENTS.md CHANGED
@@ -14,16 +14,12 @@ This file defines only implementation-level obligations. It does not redefine ar
14
14
 
15
15
  The following documents are mandatory:
16
16
 
17
- - `ctx/docs/code/structure.md`
18
- - `ctx/docs/code/container.md`
19
- - `ctx/docs/code/resolver.md`
20
- - `ctx/docs/code/parser.md`
21
- - `ctx/docs/code/depid.md`
22
- - `ctx/docs/code/testing.md`
23
- - `ctx/docs/code/jsdoc-spec.md`
24
- - `ctx/docs/code/convention/es6-modules.md`
25
- - `ctx/docs/code/convention/teqfw/dto.md`
26
- - `ctx/docs/code/convention/teqfw/enum.md`
17
+ - `ctx/docs/code/layout/structure.md`
18
+ - `ctx/docs/code/components/container.md`
19
+ - `ctx/docs/code/components/resolver.md`
20
+ - `ctx/docs/code/components/parser.md`
21
+ - `ctx/docs/code/components/depid.md`
22
+ - `ctx/docs/code/layout/testing.md`
27
23
 
28
24
  Agent MUST read and follow them before generating or modifying code.
29
25
 
@@ -33,7 +29,7 @@ Deviation from these documents constitutes non-compliance.
33
29
 
34
30
  Directory layout, namespace mapping, file naming, dependency direction, and static import rules MUST strictly follow:
35
31
 
36
- `ctx/docs/code/structure.md`
32
+ `ctx/docs/code/layout/structure.md`
37
33
 
38
34
  In particular:
39
35
 
@@ -42,7 +38,7 @@ In particular:
42
38
  - Underscores in file names are prohibited.
43
39
  - Namespace hierarchy MUST be reflected in directory hierarchy.
44
40
  - Static dependency direction MUST follow the allowed layer graph.
45
- - `Container.mjs` is the only public entry point.
41
+ - `Container.mjs` is the primary runtime entry point; the package also exposes canonical Node.js registry subpaths and one deprecated migration-only registry compatibility subpath.
46
42
 
47
43
  Structural violations are execution errors.
48
44
 
@@ -50,7 +46,7 @@ Structural violations are execution errors.
50
46
 
51
47
  JSDoc is mandatory and governed by:
52
48
 
53
- `ctx/docs/code/jsdoc-spec.md`
49
+ `ctx/docs/code/layout/structure.md` (Section 8)
54
50
 
55
51
  All implementation files MUST:
56
52
 
@@ -72,8 +68,8 @@ Absence of required JSDoc constitutes non-compliance.
72
68
 
73
69
  All DTO and Enum implementations MUST strictly follow:
74
70
 
75
- - `ctx/docs/code/convention/teqfw/dto.md`
76
- - `ctx/docs/code/convention/teqfw/enum.md`
71
+ - `ctx/docs/code/components/depid.md`
72
+ - `ctx/docs/code/layout/structure.md`
77
73
 
78
74
  In particular:
79
75
 
@@ -116,7 +112,7 @@ Redundant validation logic constitutes architectural violation.
116
112
 
117
113
  Unit and integration tests MUST follow:
118
114
 
119
- `ctx/docs/code/testing.md`
115
+ `ctx/docs/code/layout/testing.md`
120
116
 
121
117
  For every testable source module, exactly one corresponding unit test MUST exist under `test/unit/`, mirroring directory structure.
122
118
 
@@ -127,6 +123,14 @@ Tests MUST use:
127
123
 
128
124
  Isolation and determinism requirements are mandatory.
129
125
 
126
+ ## Validation Tooling
127
+
128
+ This package follows classical JavaScript module conventions and is a foundational TeqFW package.
129
+
130
+ The `teqfw-esm-validator` skill and CLI MUST NOT be used for this project because their application-module profiles are not applicable to this package.
131
+
132
+ Source changes MUST be verified with the project-provided tests and applicable classical JavaScript checks, including `node --check`, local lint commands, and local type checks when available.
133
+
130
134
  ## Type Declaration Discipline
131
135
 
132
136
  The file `types.d.ts` defines exported structural type aliases corresponding to implementation modules.