@teqfw/di 2.6.1 → 2.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.7.0 - 2026-07-10 - As-is injection fix and coverage expansion
4
+
5
+ * Fixed the bug that prevented injecting classes as-is, including named exports resolved without factory wrapping.
6
+ * Aligned container namespace aliases and published type declarations with the actual filesystem layout.
7
+ * Expanded unit and integration coverage for pipeline execution, dependency key generation, dependency declarations, Promise-safe wrappers, and as-is export resolution.
8
+ * Refreshed development dependencies and lockfile metadata before the release.
9
+ * Updated package version metadata to `2.7.0`.
10
+
11
+ ## 2.6.2 - 2026-06-08 - Documentation alignment and container simplification
12
+
13
+ * Removed the legacy `setParser()` extension point and flattened the internal container implementation layout.
14
+ * Updated the `ctx/docs/code` documentation to match the current container pipeline, namespace registry flow, and namespace-mapping model.
15
+ * Refined the public code-facing documentation so it no longer describes parser replacement or wrapper handling in obsolete terms.
16
+ * Updated package version metadata to `2.6.2`.
17
+
3
18
  ## 2.6.1 - 2026-06-07 - Type declarations cleanup
4
19
 
5
20
  * Removed stale `TeqFw_Di_Def_Parser` and `ExportSelector` aliases from `types.d.ts`.
package/README.md CHANGED
@@ -555,7 +555,6 @@ const container = new Container();
555
555
 
556
556
  Configure it before the first `get(...)`:
557
557
 
558
- - `setParser(parser)`
559
558
  - `addNamespaceRoot(prefix, target, defaultExt)`
560
559
  - `addPreprocess(fn)`
561
560
  - `addPostprocess(fn)`
package/ai/container.md CHANGED
@@ -15,7 +15,6 @@ Before the first `get()`, the container is in builder stage.
15
15
  During this stage external code may:
16
16
 
17
17
  - register namespace roots with `addNamespaceRoot()`;
18
- - replace the CDC parser with `setParser()`;
19
18
  - add preprocess hooks with `addPreprocess()`;
20
19
  - add postprocess hooks with `addPostprocess()`;
21
20
  - enable diagnostics with `enableLogging()`;
@@ -1,6 +1,6 @@
1
1
  # dependency-id.md
2
2
 
3
- Version: 20260606
3
+ Version: 20260710
4
4
 
5
5
  ## Purpose
6
6
 
@@ -23,7 +23,7 @@ Component order is fixed.
23
23
 
24
24
  - `PlatformPrefix` — optional source selector such as `node:` or `npm:`.
25
25
  - `ModuleName` — logical module identifier inside a namespace.
26
- - `__ExportName` — optional named export selector. Omission means the default export for composition, or whole-module namespace for as-is resolution.
26
+ - `__ExportName` — optional named export selector. With a lifecycle marker, selects the export for factory composition. Without a lifecycle marker, selects the named export for as-is resolution. Omission without a lifecycle marker means the whole-module namespace is resolved as-is.
27
27
  - `Lifecycle` — optional instantiation marker.
28
28
  - `WrapperSuffixes` — optional ordered wrapper export names appended after the lifecycle marker.
29
29
 
@@ -69,11 +69,15 @@ Named exports are selected with a double underscore separator.
69
69
  Examples:
70
70
 
71
71
  ```txt
72
+ App_Service_User
72
73
  App_Service_User$
74
+ App_Service_User__Factory
73
75
  App_Service_User__Factory$
74
76
  ```
75
77
 
78
+ - `App_Service_User` selects the whole module namespace for as-is resolution (no lifecycle marker, no export name).
76
79
  - `App_Service_User$` selects the default export for lifecycle-based composition.
80
+ - `App_Service_User__Factory` selects the named export `Factory` for as-is resolution (no lifecycle marker).
77
81
  - `App_Service_User__Factory$` selects the named export `Factory` for lifecycle-based composition.
78
82
 
79
83
  ## Lifecycle Markers
@@ -130,6 +134,7 @@ Important rules:
130
134
  ```txt
131
135
  App_Math
132
136
  App_Service$
137
+ App_Service__Factory
133
138
  App_Service__Factory$
134
139
  App_Task$$_wrapLog
135
140
  node:fs
@@ -138,8 +143,9 @@ npm:@humanfs/core
138
143
 
139
144
  These examples cover:
140
145
 
141
- - as-is module or export resolution;
146
+ - as-is module resolution (whole namespace);
142
147
  - singleton composition;
143
- - named export composition;
148
+ - named export as-is resolution;
149
+ - named export singleton composition;
144
150
  - transient composition with wrapper exports;
145
151
  - platform module access.
package/ai/package-api.ts CHANGED
@@ -96,16 +96,6 @@ export const PACKAGE_API: PackageApiContract = {
96
96
  stage: 'builder',
97
97
  summary: 'Creates a container in builder stage. The constructor accepts no arguments.',
98
98
  },
99
- {
100
- name: 'setParser',
101
- signature: 'setParser(parser: { parse(cdc: string): TeqFw_Di_DepId$DTO }): void',
102
- stage: 'builder',
103
- summary: 'Replaces the default CDC parser before the first resolution.',
104
- constraints: [
105
- 'Allowed only before the first get().',
106
- 'The parser contract is structural. The package does not export the default parser runtime class.',
107
- ],
108
- },
109
99
  {
110
100
  name: 'addNamespaceRoot',
111
101
  signature: 'addNamespaceRoot(prefix: string, target: string, defaultExt: string): void',
@@ -232,19 +222,6 @@ export const PACKAGE_API: PackageApiContract = {
232
222
  'Consumers should treat it as a structural protocol, not as a factory import surface.',
233
223
  ],
234
224
  },
235
- {
236
- name: 'Parser Protocol',
237
- kind: 'protocol',
238
- aliases: ['TeqFw_Di_Def_Parser'],
239
- summary: 'Structural contract accepted by Container.setParser().',
240
- fields: {
241
- parse: '(cdc: string) => TeqFw_Di_DepId$DTO',
242
- },
243
- notes: [
244
- 'The package does not export the default parser runtime class through package exports.',
245
- 'External code may provide any object that satisfies the parse() contract.',
246
- ],
247
- },
248
225
  {
249
226
  name: 'DepId Enums',
250
227
  kind: 'enum',
@@ -303,36 +280,29 @@ export const PACKAGE_API: PackageApiContract = {
303
280
  reason: 'Internal immutable-core helper. Not exported via package.json.',
304
281
  },
305
282
  {
306
- alias: 'TeqFw_Di_Container_Instantiate_Instantiator',
307
- source: './src/Container/Instantiate/Instantiator.mjs',
283
+ alias: 'TeqFw_Di_Container_Instantiate',
284
+ source: './src/Container/Instantiate.mjs',
308
285
  exposure: 'internal',
309
286
  reason: 'Internal immutable-core helper. Not exported via package.json.',
310
287
  },
311
288
  {
312
- alias: 'TeqFw_Di_Container_Lifecycle_Registry',
313
- source: './src/Container/Lifecycle/Registry.mjs',
289
+ alias: 'TeqFw_Di_Container_Lifecycle',
290
+ source: './src/Container/Lifecycle.mjs',
314
291
  exposure: 'internal',
315
292
  reason: 'Internal lifecycle cache component. Not exported via package.json.',
316
293
  },
317
294
  {
318
- alias: 'TeqFw_Di_Container_Resolve_GraphResolver',
319
- source: './src/Container/Resolve/GraphResolver.mjs',
295
+ alias: 'TeqFw_Di_Container_GraphResolver',
296
+ source: './src/Container/GraphResolver.mjs',
320
297
  exposure: 'internal',
321
298
  reason: 'Internal graph builder used by Container.get(). Not exported via package.json.',
322
299
  },
323
300
  {
324
- alias: 'TeqFw_Di_Container_Wrapper_Executor',
325
- source: './src/Container/Wrapper/Executor.mjs',
301
+ alias: 'TeqFw_Di_Container_Executor',
302
+ source: './src/Container/Executor.mjs',
326
303
  exposure: 'internal',
327
304
  reason: 'Internal wrapper-stage executor. Not exported via package.json.',
328
305
  },
329
- {
330
- alias: 'TeqFw_Di_Def_Parser',
331
- source: './src/Def/Parser.mjs',
332
- exposure: 'public-structural',
333
- reason: 'Container.setParser() accepts this structural contract, but the runtime class is not importable from the package.',
334
- canonicalUse: 'Typing/protocol alias for custom parser implementations.',
335
- },
336
306
  {
337
307
  alias: 'TeqFw_Di_DepId',
338
308
  source: './src/Dto/DepId.mjs',
@@ -432,7 +402,6 @@ export const PACKAGE_API: PackageApiContract = {
432
402
  'Only two runtime entrypoints are supported by package.json exports: @teqfw/di and @teqfw/di/src/Config/NamespaceRegistry.mjs.',
433
403
  'Resolved values are frozen before being returned.',
434
404
  '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.',
436
405
  'Named wrapper exports are executed after addPostprocess() hooks and before freeze.',
437
406
  'In the current implementation, CDC markers $$ and $$$ both end up as transient/no-cache behavior.',
438
407
  '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,6 +1,6 @@
1
1
  # usage.md
2
2
 
3
- Version: 20260606
3
+ Version: 20260710
4
4
 
5
5
  ## Purpose
6
6
 
@@ -125,6 +125,13 @@ const runtime = await container.get("App_Module$");
125
125
  const factory = await container.get("App_Module__Factory$");
126
126
  ```
127
127
 
128
+ Without a lifecycle marker, the export is resolved as-is — the class or function itself, not an instance:
129
+
130
+ ```js
131
+ const FactoryClass = await container.get("App_Module__Factory");
132
+ const factory = new FactoryClass({cast: resolvedCast});
133
+ ```
134
+
128
135
  ## Singleton And Transient
129
136
 
130
137
  Common lifecycle-based compositions:
package/dist/esm.js CHANGED
@@ -1 +1 @@
1
- var e=Object.freeze({AS_IS:"A",FACTORY:"F"});var t=Object.freeze({SINGLETON:"S",TRANSIENT:"T"});var r=Object.freeze({TEQ:"teq",NODE:"node",NPM:"npm"});const o=r.TEQ,n=e.AS_IS,i=new Set(Object.values(r)),s=new Set(Object.values(e)),a=new Set(Object.values(t));let c=class{moduleName;platform;exportName;composition;life;wrappers;origin},l=class{create(e){const t=e&&"object"==typeof e?e:{},r=new c;r.moduleName="string"==typeof t.moduleName?t.moduleName:"";const l="string"==typeof t.platform?t.platform:void 0;r.platform=l&&i.has(l)?l:o;let p=null;null===t.exportName?p=null:"string"==typeof t.exportName&&(p=t.exportName),r.exportName=p;const f="string"==typeof t.composition?t.composition:void 0;r.composition=f&&s.has(f)?f:n;const u="string"==typeof t.life?t.life:void 0;return r.life=u&&a.has(u)?u:null,r.wrappers=Array.isArray(t.wrappers)?t.wrappers.filter(e=>"string"==typeof e):[],r.origin="string"==typeof t.origin?t.origin:"",Object.freeze(r.wrappers),Object.freeze(r)}};class p{constructor(){const o=new l;let n=null;this.parse=function(i){if(n&&n.log(`Parser.parse: input='${i}'.`),"string"!=typeof i)throw new Error("CDC must be a string.");if(0===i.length)throw new Error("CDC must be non-empty.");if(!/^[\x00-\x7F]+$/.test(i))throw new Error("CDC must be ASCII.");const s=i,a=function(e){let t=r.TEQ;if(e.startsWith("node:"))return t=r.NODE,{platform:t,source:e.slice(5)};if(e.startsWith("npm:"))return t=r.NPM,{platform:t,source:e.slice(4)};if(e.startsWith("teq:"))throw new Error("Explicit teq: prefix is forbidden.");return{platform:t,source:e}}(i),c=a.platform,l=a.source;if(0===l.length)throw new Error("moduleName must be non-empty.");const p=function(e,o){let n=null,i=!1,s=[],a=e;const c=a.match(/(\${1,3})(?:_[a-z][0-9A-Za-z]*)*$/);if(c){const e=c[1],r=c[0].slice(e.length);if(i=!0,"$"===e)n=t.SINGLETON;else if("$$"===e)n=t.TRANSIENT;else{if("$$$"!==e)throw new Error("Lifecycle marker is invalid.");n=null}return a=a.slice(0,c.index),r.length>0&&(s=r.slice(1).split("_")),{core:a,life:n,lifecycleDeclared:i,wrappers:s}}if(e.includes("$"))throw new Error("Invalid lifecycle encoding.");if(o!==r.NODE&&/(?:^|[^_])_[a-z][0-9A-Za-z]*$/.test(e))throw new Error("Wrapper without lifecycle is forbidden.");return{core:a,life:n,lifecycleDeclared:i,wrappers:s}}(l,c),f=function(e){const t=e.indexOf("__"),r=e.lastIndexOf("__");if(-1!==t&&t!==r)throw new Error("Export delimiter must appear at most once.");if(e.startsWith("__")||e.endsWith("__"))throw new Error("Malformed export segment.");let o=e,n=null;if(-1!==t){if(o=e.slice(0,t),n=e.slice(t+2),!n)throw new Error("Export must be non-empty.");if(n.includes("_"))throw new Error("Export must not contain _.");if(n.includes("$"))throw new Error("Export must not contain $.")}return{moduleName:o,exportName:n}}(p.core);!function(e,t){if(!e)throw new Error("moduleName must be non-empty.");if(e.startsWith("_")||e.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(e.includes("__"))throw new Error("moduleName must not contain __.");if(e.includes("$"))throw new Error("moduleName must not contain $.");if(t!==r.NPM){if(t===r.NODE){if(!/^[A-Za-z_][$0-9A-Za-z_/-]*$/.test(e))throw new Error("node moduleName must satisfy the built-in specifier form.")}else if(!/^[A-Za-z_][$0-9A-Za-z_]*$/.test(e))throw new Error("moduleName must satisfy the canonical identifier form.")}else if(!/^[@A-Za-z_][$0-9A-Za-z_./-]*$/.test(e))throw new Error("npm moduleName must satisfy the package specifier form.")}(f.moduleName,c);let u=e.AS_IS,d=f.exportName;null!==d?u=e.FACTORY:p.lifecycleDeclared&&(d="default",u=e.FACTORY);const m=o.create({moduleName:f.moduleName,platform:c,exportName:d,composition:u,life:p.life,wrappers:p.wrappers,origin:s});return n&&n.log(`Parser.parse: produced='${m.platform}::${m.moduleName}'.`),m},this.setLogger=function(e){n=e}}}let f=class{prefix;target;defaultExt},u=class{create(e){const t=e&&"object"==typeof e?e:{},r=new f;return r.prefix="string"==typeof t.prefix?t.prefix:void 0,r.target="string"==typeof t.target?t.target:void 0,r.defaultExt="string"==typeof t.defaultExt?t.defaultExt:void 0,Object.freeze(r)}};class d{namespaces;nodeModulesRoot}class m{constructor(){const e=new u;this.create=function(t){const r=t&&"object"==typeof t?t:{},o=new d,n=Array.isArray(r.namespaces)?r.namespaces:[];return o.namespaces=n.map(t=>e.create(t)),o.nodeModulesRoot="string"==typeof r.nodeModulesRoot?r.nodeModulesRoot:void 0,Object.freeze(o.namespaces),Object.freeze(o)}}}class g{constructor({config:e,importFn:t=e=>import(e),logger:r=null}){const o=new Map,n=e;let i;const s=t,a=r,c=function(e,t){if("node"===e){const e=`node:${t}`;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("npm"===e){const e=t;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("teq"!==e)throw new Error(`Unsupported platform: ${e}`);const r=function(e){let t=null,r=-1;const o=i.namespaces;for(const n of o){const o=e.startsWith(n.prefix);a&&a.log(`Resolver.namespace: prefix='${n.prefix}' match=${String(o)} module='${e}'.`),o&&n.prefix.length>r&&(t=n,r=n.prefix.length)}if(!t)throw new Error(`Namespace rule is not found for '${e}'.`);return t}(t),o=t.slice(r.prefix.length).split("_").join("/"),n=(s=o,(c=r.defaultExt)?s.endsWith(c)?s:`${s}${c}`:s);var s,c;const l=function(e,t){return e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}(r.target,n);return a&&a.log(`Resolver.specifier: module='${t}' -> '${l}'.`),l};this.resolve=async function(e){var t;await Promise.resolve(),i||(i={nodeModulesRoot:(t=n).nodeModulesRoot,namespaces:t.namespaces.map(e=>({prefix:e.prefix,target:e.target,defaultExt:e.defaultExt}))});const r=e.platform,l=e.moduleName,p=`${r}::${l}`;if(o.has(p))return a&&a.log(`Resolver.cache: hit key='${p}'.`),o.get(p);a&&a.log(`Resolver.cache: miss key='${p}'.`);const f=(async()=>{const e=c(r,l);return a&&a.log(`Resolver.import: '${e}'.`),s(e)})();o.set(p,f);try{return await f}catch(e){throw o.delete(p),a&&a.error(`Resolver.cache: evict key='${p}' after failure.`,e),e}}}}function h(e){const t=null===e.exportName?"":e.exportName,r=null===e.life?"":e.life,o=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,t,e.composition,r,o].join("::")}function w(e,t){const r=Reflect.get(e,"__deps__");if(void 0===r)return{};if(null===r||"object"!=typeof r||Array.isArray(r))throw new Error("__deps__ must be a plain object.");const o=null===t.exportName?"default":t.exportName,n=Reflect.get(r,o);if(null!=n&&"object"==typeof n&&!Array.isArray(n)){if(!Object.values(n).every(e=>"string"==typeof e))throw new Error("__deps__ export entries must map dependency names to CDC strings.");return n}if("default"===o){const e=Object.values(r);if(e.every(e=>"string"==typeof e))return r;if(e.every(e=>null!==e&&"object"==typeof e&&!Array.isArray(e)))return{};throw new Error("__deps__ must be either flat or export-scoped.")}return{}}class y{constructor({parser:e,resolver:t,logger:r=null}){const o=r,n=h,i=async function(r,s,a,c){const l=n(r);if(a.has(l)){const e=[...c,l].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const p=n(r);if(!s.has(p)){a.add(l),c.push(l);try{const n=await t.resolve(r);o&&o.log(`GraphResolver.walk: resolved '${p}'.`),s.set(p,{depId:r,namespace:n});const l=w(n,r);for(const t of Object.values(l)){const r=e.parse(t);o&&o.log(`GraphResolver.walk: edge '${p}' -> '${r.platform}::${r.moduleName}'.`),await i(r,s,a,c)}}finally{c.pop(),a.delete(l)}}};this.resolve=async function(e){const t=new Map,r=new Set;return await i(e,t,r,[]),t}}}class ${constructor(){this.instantiate=function(t,r,o){const n=function(e,t){if(null===e.exportName)return t;if(!(e.exportName in t))throw new Error(`Export '${e.exportName}' is not found in module namespace.`);return t[e.exportName]}(t,r);if(t.composition===e.AS_IS)return n;if(t.composition===e.FACTORY){if("function"!=typeof n)throw new Error("Factory composition requires a callable export.");const e=n;let t;if(function(e){try{return Reflect.construct(String,[],e),!0}catch{return!1}}(e)){t=new e(o)}else{t=e(o)}if(t instanceof Promise)throw new Error("Factory composition must return synchronously (non-Promise).");return t}throw new Error(`Unsupported composition mode: ${String(t.composition)}.`)}}}class N{constructor(r=null){const o=new Map,n=r,i=h;this.apply=function(r,s){if(r.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${r.composition}' cache=skip.`),s();if(r.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),s();if(r.life===t.SINGLETON){const e=i(r);if(o.has(e))return n&&n.log(`Lifecycle.cache: hit key='${e}'.`),o.get(e);n&&n.log(`Lifecycle.cache: miss key='${e}', create.`);const t=s();return o.set(e,t),n&&n.log(`Lifecycle.cache: stored key='${e}'.`),t}return n&&n.log("Lifecycle.apply: no lifecycle marker cache=skip."),s()}}}class x{constructor(){const e=function(e){return"function"==typeof e};this.execute=function(t,r,o){let n=r;const i=t.wrappers;for(const t of i){if(!(t in o))throw new Error(`Wrapper '${t}' is not found in module namespace.`);const r=o[t];if(!e(r))throw new Error(`Wrapper '${t}' must be callable.`);if(n=r(n),n instanceof Promise)throw new Error(`Wrapper '${t}' must return synchronously (non-Promise).`)}return n}}}class E{constructor(e="teqfw/di"){const t=`[${e}]`;this.log=function(e){console.debug(`${t} ${e}`)},this.error=function(e,r){console.error(`${t} ${e}`),r instanceof Error?console.error(r.stack??r.message):void 0!==r&&console.error(r)}}}const b=Object.freeze({log(){},error(){}}),C=new WeakMap;function v(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;const r=e;if(C.has(r))return C.get(r);try{return Reflect.get(r,"then"),e}catch{const e=new Proxy(r,{get(e,t,r){if("then"!==t)return Reflect.get(e,t,r)}});return C.set(r,e),e}}class _{constructor(){let e="notConfigured";const t=[],r=[],o=[],n=new Map;let i=!1,s=!1,a=new p;const c=new m;let l,f,u,d=b;const C=new $,_=new x,I=h,j=h,A=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;if("[object Module]"===Object.prototype.toString.call(e))return e;if(Object.isFrozen(e))return e;try{Object.freeze(e)}catch(e){d.log(`Container.freeze: skipped (${String(e)}).`)}return e},R=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},O=function(e){s&&d.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){R(),O("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){R(),O("addPostprocess()."),r.push(e)},this.setParser=function(e){R(),a=e,"function"==typeof a.setLogger&&a.setLogger(s?d:null),O("setParser().")},this.addNamespaceRoot=function(e,t,r){R(),O(`addNamespaceRoot('${e}').`),o.push({prefix:e,target:t,defaultExt:r})},this.enableTestMode=function(){R(),O("enableTestMode()."),i=!0},this.enableLogging=function(){R(),s||(s=!0,d=new E,"function"==typeof a.setLogger&&a.setLogger(d),d.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(R(),O(`register('${e}').`),!0!==i)throw new Error("Container test mode is disabled.");const r=a.parse(e);n.set(j(r),t)},this.get=async function(s){if("failed"===e)throw d.error(`Container.get: rejected in failed state cdc='${s}'.`),new Error("Container is in failed state.");let p="start";try{d.log(`Container.get: cdc='${s}'.`),function(){if("notConfigured"!==e)return;d.log("Container.transition: notConfigured -> operational."),e="operational";const t=c.create({namespaces:o});"function"==typeof a.setLogger&&a.setLogger(d),l=new g({config:t,logger:d}),f=new y({parser:a,resolver:l,logger:d}),u=new N(d)}(),d.log(`Container.state: '${e}'.`),p="parse",d.log("Container.pipeline: parse:entry.");const m=a.parse(s);d.log(`Container.pipeline: parse:exit '${m.platform}::${m.moduleName}'.`),p="preprocess",d.log("Container.pipeline: preprocess:entry.");const h=function(e){let r=e;for(const e of t)r=e(r);return r}(m);if(d.log(`Container.pipeline: preprocess:exit '${h.platform}::${h.moduleName}'.`),!0===i){p="mock",d.log("Container.pipeline: mock-lookup:entry.");const e=j(h);if(n.has(e)){d.log(`Container.pipeline: mock-lookup:hit '${e}'.`),p="freeze",d.log("Container.pipeline: freeze:entry.");const t=A(n.get(e));return d.log("Container.pipeline: freeze:exit."),d.log("Container.pipeline: return:success."),v(t)}d.log(`Container.pipeline: mock-lookup:miss '${e}'.`)}else d.log("Container.pipeline: mock-lookup:disabled.");p="resolve",d.log("Container.pipeline: resolve:entry.");const $=await f.resolve(h);d.log(`Container.pipeline: resolve:exit nodes=${$.size}.`);const x=new Map,E=function(e){if(x.has(e))return x.get(e);if(!$.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=$.get(e);p="lifecycle",d.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const o=u.apply(t.depId,function(){p="instantiate",d.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},o=w(t.namespace,t.depId);for(const[t,r]of Object.entries(o)){const o=a.parse(r);e[t]=E(I(o))}const n=C.instantiate(t.depId,t.namespace,e);d.log(`Container.pipeline: instantiate:exit '${t.depId.platform}::${t.depId.moduleName}'.`),p="postprocess",d.log(`Container.pipeline: postprocess:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const i=function(e){let t=e;for(const e of r)t=e(t);return t}(n);d.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const s=_.execute(t.depId,i,t.namespace);p="freeze",d.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const c=A(s);return d.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),c});return d.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),x.set(e,o),o},b=E(I(h));return d.log("Container.pipeline: return:success."),v(b)}catch(t){throw d.error(`Container.pipeline: failed at stage='${p}'.`,t),d.log("Container.transition: operational -> failed."),e="failed",t}}}}class I{constructor(){const e=new _;this.addPreprocess=function(t){return e.addPreprocess(t)},this.addPostprocess=function(t){return e.addPostprocess(t)},this.setParser=function(t){return e.setParser(t)},this.addNamespaceRoot=function(t,r,o){return e.addNamespaceRoot(t,r,o)},this.enableTestMode=function(){return e.enableTestMode()},this.enableLogging=function(){return e.enableLogging()},this.register=function(t,r){return e.register(t,r)},this.get=function(t){return e.get(t)}}}export{I as default};
1
+ var e=Object.freeze({AS_IS:"A",FACTORY:"F"});var t=Object.freeze({SINGLETON:"S",TRANSIENT:"T"});var r=Object.freeze({TEQ:"teq",NODE:"node",NPM:"npm"});const o=r.TEQ,n=e.AS_IS,i=new Set(Object.values(r)),s=new Set(Object.values(e)),a=new Set(Object.values(t));let c=class{moduleName;platform;exportName;composition;life;wrappers;origin},l=class{create(e){const t=e&&"object"==typeof e?e:{},r=new c;r.moduleName="string"==typeof t.moduleName?t.moduleName:"";const l="string"==typeof t.platform?t.platform:void 0;r.platform=l&&i.has(l)?l:o;let p=null;null===t.exportName?p=null:"string"==typeof t.exportName&&(p=t.exportName),r.exportName=p;const f="string"==typeof t.composition?t.composition:void 0;r.composition=f&&s.has(f)?f:n;const u="string"==typeof t.life?t.life:void 0;return r.life=u&&a.has(u)?u:null,r.wrappers=Array.isArray(t.wrappers)?t.wrappers.filter(e=>"string"==typeof e):[],r.origin="string"==typeof t.origin?t.origin:"",Object.freeze(r.wrappers),Object.freeze(r)}};class p{constructor(){const o=new l;let n=null;this.parse=function(i){if(n&&n.log(`Parser.parse: input='${i}'.`),"string"!=typeof i)throw new Error("CDC must be a string.");if(0===i.length)throw new Error("CDC must be non-empty.");if(!/^[\x00-\x7F]+$/.test(i))throw new Error("CDC must be ASCII.");const s=i,a=function(e){let t=r.TEQ;if(e.startsWith("node:"))return t=r.NODE,{platform:t,source:e.slice(5)};if(e.startsWith("npm:"))return t=r.NPM,{platform:t,source:e.slice(4)};if(e.startsWith("teq:"))throw new Error("Explicit teq: prefix is forbidden.");return{platform:t,source:e}}(i),c=a.platform,l=a.source;if(0===l.length)throw new Error("moduleName must be non-empty.");const p=function(e,o){let n=null,i=!1,s=[],a=e;const c=a.match(/(\${1,3})(?:_[a-z][0-9A-Za-z]*)*$/);if(c){const e=c[1],r=c[0].slice(e.length);if(i=!0,"$"===e)n=t.SINGLETON;else if("$$"===e)n=t.TRANSIENT;else{if("$$$"!==e)throw new Error("Lifecycle marker is invalid.");n=null}return a=a.slice(0,c.index),r.length>0&&(s=r.slice(1).split("_")),{core:a,life:n,lifecycleDeclared:i,wrappers:s}}if(e.includes("$"))throw new Error("Invalid lifecycle encoding.");if(o!==r.NODE&&/(?:^|[^_])_[a-z][0-9A-Za-z]*$/.test(e))throw new Error("Wrapper without lifecycle is forbidden.");return{core:a,life:n,lifecycleDeclared:i,wrappers:s}}(l,c),f=function(e){const t=e.indexOf("__"),r=e.lastIndexOf("__");if(-1!==t&&t!==r)throw new Error("Export delimiter must appear at most once.");if(e.startsWith("__")||e.endsWith("__"))throw new Error("Malformed export segment.");let o=e,n=null;if(-1!==t){if(o=e.slice(0,t),n=e.slice(t+2),!n)throw new Error("Export must be non-empty.");if(n.includes("_"))throw new Error("Export must not contain _.");if(n.includes("$"))throw new Error("Export must not contain $.")}return{moduleName:o,exportName:n}}(p.core);!function(e,t){if(!e)throw new Error("moduleName must be non-empty.");if(e.startsWith("_")||e.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(e.includes("__"))throw new Error("moduleName must not contain __.");if(e.includes("$"))throw new Error("moduleName must not contain $.");if(t!==r.NPM){if(t===r.NODE){if(!/^[A-Za-z_][$0-9A-Za-z_/-]*$/.test(e))throw new Error("node moduleName must satisfy the built-in specifier form.")}else if(!/^[A-Za-z_][$0-9A-Za-z_]*$/.test(e))throw new Error("moduleName must satisfy the canonical identifier form.")}else if(!/^[@A-Za-z_][$0-9A-Za-z_./-]*$/.test(e))throw new Error("npm moduleName must satisfy the package specifier form.")}(f.moduleName,c);let u=e.AS_IS,d=f.exportName;p.lifecycleDeclared&&(null===d&&(d="default"),u=e.FACTORY);const m=o.create({moduleName:f.moduleName,platform:c,exportName:d,composition:u,life:p.life,wrappers:p.wrappers,origin:s});return n&&n.log(`Parser.parse: produced='${m.platform}::${m.moduleName}'.`),m},this.setLogger=function(e){n=e}}}let f=class{prefix;target;defaultExt},u=class{create(e){const t=e&&"object"==typeof e?e:{},r=new f;return r.prefix="string"==typeof t.prefix?t.prefix:void 0,r.target="string"==typeof t.target?t.target:void 0,r.defaultExt="string"==typeof t.defaultExt?t.defaultExt:void 0,Object.freeze(r)}};class d{namespaces;nodeModulesRoot}class m{constructor(){const e=new u;this.create=function(t){const r=t&&"object"==typeof t?t:{},o=new d,n=Array.isArray(r.namespaces)?r.namespaces:[];return o.namespaces=n.map(t=>e.create(t)),o.nodeModulesRoot="string"==typeof r.nodeModulesRoot?r.nodeModulesRoot:void 0,Object.freeze(o.namespaces),Object.freeze(o)}}}class g{constructor({config:e,importFn:t=e=>import(e),logger:r=null}){const o=new Map,n=e;let i;const s=t,a=r,c=function(e,t){if("node"===e){const e=`node:${t}`;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("npm"===e){const e=t;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("teq"!==e)throw new Error(`Unsupported platform: ${e}`);const r=function(e){let t=null,r=-1;const o=i.namespaces;for(const n of o){const o=e.startsWith(n.prefix);a&&a.log(`Resolver.namespace: prefix='${n.prefix}' match=${String(o)} module='${e}'.`),o&&n.prefix.length>r&&(t=n,r=n.prefix.length)}if(!t)throw new Error(`Namespace rule is not found for '${e}'.`);return t}(t),o=t.slice(r.prefix.length).split("_").join("/"),n=(s=o,(c=r.defaultExt)?s.endsWith(c)?s:`${s}${c}`:s);var s,c;const l=function(e,t){return e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}(r.target,n);return a&&a.log(`Resolver.specifier: module='${t}' -> '${l}'.`),l};this.resolve=async function(e){var t;await Promise.resolve(),i||(i={nodeModulesRoot:(t=n).nodeModulesRoot,namespaces:t.namespaces.map(e=>({prefix:e.prefix,target:e.target,defaultExt:e.defaultExt}))});const r=e.platform,l=e.moduleName,p=`${r}::${l}`;if(o.has(p))return a&&a.log(`Resolver.cache: hit key='${p}'.`),o.get(p);a&&a.log(`Resolver.cache: miss key='${p}'.`);const f=(async()=>{const e=c(r,l);return a&&a.log(`Resolver.import: '${e}'.`),s(e)})();o.set(p,f);try{return await f}catch(e){throw o.delete(p),a&&a.error(`Resolver.cache: evict key='${p}' after failure.`,e),e}}}}function w(e){const t=null===e.exportName?"":e.exportName,r=null===e.life?"":e.life,o=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,t,e.composition,r,o].join("::")}function h(e,t){const r=Reflect.get(e,"__deps__");if(void 0===r)return{};if(null===r||"object"!=typeof r||Array.isArray(r))throw new Error("__deps__ must be a plain object.");const o=null===t.exportName?"default":t.exportName,n=Reflect.get(r,o);if(null!=n&&"object"==typeof n&&!Array.isArray(n)){if(!Object.values(n).every(e=>"string"==typeof e))throw new Error("__deps__ export entries must map dependency names to CDC strings.");return n}if("default"===o){const e=Object.values(r);if(e.every(e=>"string"==typeof e))return r;if(e.every(e=>null!==e&&"object"==typeof e&&!Array.isArray(e)))return{};throw new Error("__deps__ must be either flat or export-scoped.")}return{}}class y{constructor({parser:e,resolver:t,logger:r=null}){const o=r,n=w,i=async function(r,s,a,c){const l=n(r);if(a.has(l)){const e=[...c,l].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const p=n(r);if(!s.has(p)){a.add(l),c.push(l);try{const n=await t.resolve(r);o&&o.log(`GraphResolver.walk: resolved '${p}'.`),s.set(p,{depId:r,namespace:n});const l=h(n,r);for(const t of Object.values(l)){const r=e.parse(t);o&&o.log(`GraphResolver.walk: edge '${p}' -> '${r.platform}::${r.moduleName}'.`),await i(r,s,a,c)}}finally{c.pop(),a.delete(l)}}};this.resolve=async function(e){const t=new Map,r=new Set;return await i(e,t,r,[]),t}}}class ${constructor(){this.instantiate=function(t,r,o){const n=function(e,t){if(null===e.exportName)return t;if(!(e.exportName in t))throw new Error(`Export '${e.exportName}' is not found in module namespace.`);return t[e.exportName]}(t,r);if(t.composition===e.AS_IS)return n;if(t.composition===e.FACTORY){if("function"!=typeof n)throw new Error("Factory composition requires a callable export.");const e=n;let t;if(function(e){try{return Reflect.construct(String,[],e),!0}catch{return!1}}(e)){t=new e(o)}else{t=e(o)}if(t instanceof Promise)throw new Error("Factory composition must return synchronously (non-Promise).");return t}throw new Error(`Unsupported composition mode: ${String(t.composition)}.`)}}}class E{constructor(r=null){const o=new Map,n=r,i=w;this.apply=function(r,s){if(r.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${r.composition}' cache=skip.`),s();if(r.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),s();if(r.life===t.SINGLETON){const e=i(r);if(o.has(e))return n&&n.log(`Lifecycle.cache: hit key='${e}'.`),o.get(e);n&&n.log(`Lifecycle.cache: miss key='${e}', create.`);const t=s();return o.set(e,t),n&&n.log(`Lifecycle.cache: stored key='${e}'.`),t}return n&&n.log("Lifecycle.apply: no lifecycle marker cache=skip."),s()}}}class N{constructor(){const e=function(e){return"function"==typeof e};this.execute=function(t,r,o){let n=r;const i=t.wrappers;for(const t of i){if(!(t in o))throw new Error(`Wrapper '${t}' is not found in module namespace.`);const r=o[t];if(!e(r))throw new Error(`Wrapper '${t}' must be callable.`);if(n=r(n),n instanceof Promise)throw new Error(`Wrapper '${t}' must return synchronously (non-Promise).`)}return n}}}const x=new WeakMap;function C(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;const r=e;if(x.has(r))return x.get(r);try{return Reflect.get(r,"then"),e}catch{const e=new Proxy(r,{get(e,t,r){if("then"!==t)return Reflect.get(e,t,r)}});return x.set(r,e),e}}class b{constructor(e="teqfw/di"){const t=`[${e}]`;this.log=function(e){console.debug(`${t} ${e}`)},this.error=function(e,r){console.error(`${t} ${e}`),r instanceof Error?console.error(r.stack??r.message):void 0!==r&&console.error(r)}}}const v=Object.freeze({log(){},error(){}});class _{constructor(){let e="notConfigured";const t=[],r=[],o=[],n=new Map;let i=!1,s=!1,a=new p;const c=new m;let l,f,u,d=v;const x=new $,_=new N,I=w,R=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;if("[object Module]"===Object.prototype.toString.call(e))return e;if(Object.isFrozen(e))return e;try{Object.freeze(e)}catch(e){d.log(`Container.freeze: skipped (${String(e)}).`)}return e},j=function(e){let r=e;for(const e of t)r=e(r);return r},A=function(e){let t=e;for(const e of r)t=e(t);return t},z=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},O=function(e){s&&d.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){z(),O("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){z(),O("addPostprocess()."),r.push(e)},this.addNamespaceRoot=function(e,t,r){z(),O(`addNamespaceRoot('${e}').`),o.push({prefix:e,target:t,defaultExt:r})},this.enableTestMode=function(){z(),O("enableTestMode()."),i=!0},this.enableLogging=function(){z(),s||(s=!0,d=new b,"function"==typeof a.setLogger&&a.setLogger(d),d.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(z(),O(`register('${e}').`),!0!==i)throw new Error("Container test mode is disabled.");const r=a.parse(e);n.set(I(r),t)},this.get=async function(t){if("failed"===e)throw d.error(`Container.get: rejected in failed state cdc='${t}'.`),new Error("Container is in failed state.");try{return function(){if("notConfigured"!==e)return;d.log("Container.transition: notConfigured -> operational."),e="operational";const t=c.create({namespaces:o});"function"==typeof a.setLogger&&a.setLogger(d),l=new g({config:t,logger:d}),f=new y({parser:a,resolver:l,logger:d}),u=new E(d)}(),d.log(`Container.state: '${e}'.`),await async function(e,t){const{parser:r,resolver:o,graphResolver:n,lifecycle:i,instantiator:s,wrapperExecutor:a,logger:c,testMode:l,mockRegistry:p,freeze:f,applyPreprocess:u,applyPostprocess:d}=e;if(!o||!n||!i)throw new Error("Container infrastructure is not initialized.");const m=w,g=w;let y="start";try{c.log(`Container.get: cdc='${t}'.`),y="parse",c.log("Container.pipeline: parse:entry.");const e=r.parse(t);c.log(`Container.pipeline: parse:exit '${e.platform}::${e.moduleName}'.`),y="preprocess",c.log("Container.pipeline: preprocess:entry.");const o=u(e);if(c.log(`Container.pipeline: preprocess:exit '${o.platform}::${o.moduleName}'.`),!0===l){y="mock",c.log("Container.pipeline: mock-lookup:entry.");const e=g(o);if(p.has(e)){c.log(`Container.pipeline: mock-lookup:hit '${e}'.`),y="freeze",c.log("Container.pipeline: freeze:entry.");const t=f(p.get(e));return c.log("Container.pipeline: freeze:exit."),c.log("Container.pipeline: return:success."),C(t)}c.log(`Container.pipeline: mock-lookup:miss '${e}'.`)}else c.log("Container.pipeline: mock-lookup:disabled.");y="resolve",c.log("Container.pipeline: resolve:entry.");const w=await n.resolve(o);c.log(`Container.pipeline: resolve:exit nodes=${w.size}.`);const $=new Map,E=function(e){if($.has(e))return $.get(e);if(!w.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=w.get(e);y="lifecycle",c.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const o=i.apply(t.depId,function(){y="instantiate",c.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},o=h(t.namespace,t.depId);for(const[t,n]of Object.entries(o)){const o=r.parse(n);e[t]=E(m(o))}const n=s.instantiate(t.depId,t.namespace,e);c.log(`Container.pipeline: instantiate:exit '${t.depId.platform}::${t.depId.moduleName}'.`),y="postprocess",c.log(`Container.pipeline: postprocess:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const i=d(n);c.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const l=a.execute(t.depId,i,t.namespace);y="freeze",c.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const p=f(l);return c.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),p});return c.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),$.set(e,o),o},N=E(m(o));return c.log("Container.pipeline: return:success."),C(N)}catch(e){throw c.error(`Container.pipeline: failed at stage='${y}'.`,e),e}}({parser:a,resolver:l,graphResolver:f,lifecycle:u,instantiator:x,wrapperExecutor:_,logger:d,testMode:i,mockRegistry:n,freeze:R,applyPreprocess:j,applyPostprocess:A},t)}catch(t){throw d.error("Container.transition: operational -> failed.",t),e="failed",t}}}}export{_ as default};
package/dist/umd.js CHANGED
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).TeqFw_Di_Container=t()}(this,function(){"use strict";var e=Object.freeze({AS_IS:"A",FACTORY:"F"});var t=Object.freeze({SINGLETON:"S",TRANSIENT:"T"});var r=Object.freeze({TEQ:"teq",NODE:"node",NPM:"npm"});const o=r.TEQ,n=e.AS_IS,i=new Set(Object.values(r)),s=new Set(Object.values(e)),a=new Set(Object.values(t));let c=class{moduleName;platform;exportName;composition;life;wrappers;origin},l=class{create(e){const t=e&&"object"==typeof e?e:{},r=new c;r.moduleName="string"==typeof t.moduleName?t.moduleName:"";const l="string"==typeof t.platform?t.platform:void 0;r.platform=l&&i.has(l)?l:o;let p=null;null===t.exportName?p=null:"string"==typeof t.exportName&&(p=t.exportName),r.exportName=p;const f="string"==typeof t.composition?t.composition:void 0;r.composition=f&&s.has(f)?f:n;const u="string"==typeof t.life?t.life:void 0;return r.life=u&&a.has(u)?u:null,r.wrappers=Array.isArray(t.wrappers)?t.wrappers.filter(e=>"string"==typeof e):[],r.origin="string"==typeof t.origin?t.origin:"",Object.freeze(r.wrappers),Object.freeze(r)}};class p{constructor(){const o=new l;let n=null;this.parse=function(i){if(n&&n.log(`Parser.parse: input='${i}'.`),"string"!=typeof i)throw new Error("CDC must be a string.");if(0===i.length)throw new Error("CDC must be non-empty.");if(!/^[\x00-\x7F]+$/.test(i))throw new Error("CDC must be ASCII.");const s=i,a=function(e){let t=r.TEQ;if(e.startsWith("node:"))return t=r.NODE,{platform:t,source:e.slice(5)};if(e.startsWith("npm:"))return t=r.NPM,{platform:t,source:e.slice(4)};if(e.startsWith("teq:"))throw new Error("Explicit teq: prefix is forbidden.");return{platform:t,source:e}}(i),c=a.platform,l=a.source;if(0===l.length)throw new Error("moduleName must be non-empty.");const p=function(e,o){let n=null,i=!1,s=[],a=e;const c=a.match(/(\${1,3})(?:_[a-z][0-9A-Za-z]*)*$/);if(c){const e=c[1],r=c[0].slice(e.length);if(i=!0,"$"===e)n=t.SINGLETON;else if("$$"===e)n=t.TRANSIENT;else{if("$$$"!==e)throw new Error("Lifecycle marker is invalid.");n=null}return a=a.slice(0,c.index),r.length>0&&(s=r.slice(1).split("_")),{core:a,life:n,lifecycleDeclared:i,wrappers:s}}if(e.includes("$"))throw new Error("Invalid lifecycle encoding.");if(o!==r.NODE&&/(?:^|[^_])_[a-z][0-9A-Za-z]*$/.test(e))throw new Error("Wrapper without lifecycle is forbidden.");return{core:a,life:n,lifecycleDeclared:i,wrappers:s}}(l,c),f=function(e){const t=e.indexOf("__"),r=e.lastIndexOf("__");if(-1!==t&&t!==r)throw new Error("Export delimiter must appear at most once.");if(e.startsWith("__")||e.endsWith("__"))throw new Error("Malformed export segment.");let o=e,n=null;if(-1!==t){if(o=e.slice(0,t),n=e.slice(t+2),!n)throw new Error("Export must be non-empty.");if(n.includes("_"))throw new Error("Export must not contain _.");if(n.includes("$"))throw new Error("Export must not contain $.")}return{moduleName:o,exportName:n}}(p.core);!function(e,t){if(!e)throw new Error("moduleName must be non-empty.");if(e.startsWith("_")||e.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(e.includes("__"))throw new Error("moduleName must not contain __.");if(e.includes("$"))throw new Error("moduleName must not contain $.");if(t!==r.NPM){if(t===r.NODE){if(!/^[A-Za-z_][$0-9A-Za-z_/-]*$/.test(e))throw new Error("node moduleName must satisfy the built-in specifier form.")}else if(!/^[A-Za-z_][$0-9A-Za-z_]*$/.test(e))throw new Error("moduleName must satisfy the canonical identifier form.")}else if(!/^[@A-Za-z_][$0-9A-Za-z_./-]*$/.test(e))throw new Error("npm moduleName must satisfy the package specifier form.")}(f.moduleName,c);let u=e.AS_IS,d=f.exportName;null!==d?u=e.FACTORY:p.lifecycleDeclared&&(d="default",u=e.FACTORY);const m=o.create({moduleName:f.moduleName,platform:c,exportName:d,composition:u,life:p.life,wrappers:p.wrappers,origin:s});return n&&n.log(`Parser.parse: produced='${m.platform}::${m.moduleName}'.`),m},this.setLogger=function(e){n=e}}}let f=class{prefix;target;defaultExt},u=class{create(e){const t=e&&"object"==typeof e?e:{},r=new f;return r.prefix="string"==typeof t.prefix?t.prefix:void 0,r.target="string"==typeof t.target?t.target:void 0,r.defaultExt="string"==typeof t.defaultExt?t.defaultExt:void 0,Object.freeze(r)}};class d{namespaces;nodeModulesRoot}class m{constructor(){const e=new u;this.create=function(t){const r=t&&"object"==typeof t?t:{},o=new d,n=Array.isArray(r.namespaces)?r.namespaces:[];return o.namespaces=n.map(t=>e.create(t)),o.nodeModulesRoot="string"==typeof r.nodeModulesRoot?r.nodeModulesRoot:void 0,Object.freeze(o.namespaces),Object.freeze(o)}}}class g{constructor({config:e,importFn:t=e=>import(e),logger:r=null}){const o=new Map,n=e;let i;const s=t,a=r,c=function(e,t){if("node"===e){const e=`node:${t}`;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("npm"===e){const e=t;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("teq"!==e)throw new Error(`Unsupported platform: ${e}`);const r=function(e){let t=null,r=-1;const o=i.namespaces;for(const n of o){const o=e.startsWith(n.prefix);a&&a.log(`Resolver.namespace: prefix='${n.prefix}' match=${String(o)} module='${e}'.`),o&&n.prefix.length>r&&(t=n,r=n.prefix.length)}if(!t)throw new Error(`Namespace rule is not found for '${e}'.`);return t}(t),o=t.slice(r.prefix.length).split("_").join("/"),n=(s=o,(c=r.defaultExt)?s.endsWith(c)?s:`${s}${c}`:s);var s,c;const l=function(e,t){return e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}(r.target,n);return a&&a.log(`Resolver.specifier: module='${t}' -> '${l}'.`),l};this.resolve=async function(e){var t;await Promise.resolve(),i||(i={nodeModulesRoot:(t=n).nodeModulesRoot,namespaces:t.namespaces.map(e=>({prefix:e.prefix,target:e.target,defaultExt:e.defaultExt}))});const r=e.platform,l=e.moduleName,p=`${r}::${l}`;if(o.has(p))return a&&a.log(`Resolver.cache: hit key='${p}'.`),o.get(p);a&&a.log(`Resolver.cache: miss key='${p}'.`);const f=(async()=>{const e=c(r,l);return a&&a.log(`Resolver.import: '${e}'.`),s(e)})();o.set(p,f);try{return await f}catch(e){throw o.delete(p),a&&a.error(`Resolver.cache: evict key='${p}' after failure.`,e),e}}}}function h(e){const t=null===e.exportName?"":e.exportName,r=null===e.life?"":e.life,o=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,t,e.composition,r,o].join("::")}function w(e,t){const r=Reflect.get(e,"__deps__");if(void 0===r)return{};if(null===r||"object"!=typeof r||Array.isArray(r))throw new Error("__deps__ must be a plain object.");const o=null===t.exportName?"default":t.exportName,n=Reflect.get(r,o);if(null!=n&&"object"==typeof n&&!Array.isArray(n)){if(!Object.values(n).every(e=>"string"==typeof e))throw new Error("__deps__ export entries must map dependency names to CDC strings.");return n}if("default"===o){const e=Object.values(r);if(e.every(e=>"string"==typeof e))return r;if(e.every(e=>null!==e&&"object"==typeof e&&!Array.isArray(e)))return{};throw new Error("__deps__ must be either flat or export-scoped.")}return{}}class y{constructor({parser:e,resolver:t,logger:r=null}){const o=r,n=h,i=async function(r,s,a,c){const l=n(r);if(a.has(l)){const e=[...c,l].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const p=n(r);if(!s.has(p)){a.add(l),c.push(l);try{const n=await t.resolve(r);o&&o.log(`GraphResolver.walk: resolved '${p}'.`),s.set(p,{depId:r,namespace:n});const l=w(n,r);for(const t of Object.values(l)){const r=e.parse(t);o&&o.log(`GraphResolver.walk: edge '${p}' -> '${r.platform}::${r.moduleName}'.`),await i(r,s,a,c)}}finally{c.pop(),a.delete(l)}}};this.resolve=async function(e){const t=new Map,r=new Set;return await i(e,t,r,[]),t}}}class ${constructor(){this.instantiate=function(t,r,o){const n=function(e,t){if(null===e.exportName)return t;if(!(e.exportName in t))throw new Error(`Export '${e.exportName}' is not found in module namespace.`);return t[e.exportName]}(t,r);if(t.composition===e.AS_IS)return n;if(t.composition===e.FACTORY){if("function"!=typeof n)throw new Error("Factory composition requires a callable export.");const e=n;let t;if(function(e){try{return Reflect.construct(String,[],e),!0}catch{return!1}}(e)){t=new e(o)}else{t=e(o)}if(t instanceof Promise)throw new Error("Factory composition must return synchronously (non-Promise).");return t}throw new Error(`Unsupported composition mode: ${String(t.composition)}.`)}}}class N{constructor(r=null){const o=new Map,n=r,i=h;this.apply=function(r,s){if(r.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${r.composition}' cache=skip.`),s();if(r.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),s();if(r.life===t.SINGLETON){const e=i(r);if(o.has(e))return n&&n.log(`Lifecycle.cache: hit key='${e}'.`),o.get(e);n&&n.log(`Lifecycle.cache: miss key='${e}', create.`);const t=s();return o.set(e,t),n&&n.log(`Lifecycle.cache: stored key='${e}'.`),t}return n&&n.log("Lifecycle.apply: no lifecycle marker cache=skip."),s()}}}class x{constructor(){const e=function(e){return"function"==typeof e};this.execute=function(t,r,o){let n=r;const i=t.wrappers;for(const t of i){if(!(t in o))throw new Error(`Wrapper '${t}' is not found in module namespace.`);const r=o[t];if(!e(r))throw new Error(`Wrapper '${t}' must be callable.`);if(n=r(n),n instanceof Promise)throw new Error(`Wrapper '${t}' must return synchronously (non-Promise).`)}return n}}}class E{constructor(e="teqfw/di"){const t=`[${e}]`;this.log=function(e){console.debug(`${t} ${e}`)},this.error=function(e,r){console.error(`${t} ${e}`),r instanceof Error?console.error(r.stack??r.message):void 0!==r&&console.error(r)}}}const b=Object.freeze({log(){},error(){}}),C=new WeakMap;function _(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;const r=e;if(C.has(r))return C.get(r);try{return Reflect.get(r,"then"),e}catch{const e=new Proxy(r,{get(e,t,r){if("then"!==t)return Reflect.get(e,t,r)}});return C.set(r,e),e}}class v{constructor(){let e="notConfigured";const t=[],r=[],o=[],n=new Map;let i=!1,s=!1,a=new p;const c=new m;let l,f,u,d=b;const C=new $,v=new x,j=h,I=h,A=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;if("[object Module]"===Object.prototype.toString.call(e))return e;if(Object.isFrozen(e))return e;try{Object.freeze(e)}catch(e){d.log(`Container.freeze: skipped (${String(e)}).`)}return e},R=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},O=function(e){s&&d.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){R(),O("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){R(),O("addPostprocess()."),r.push(e)},this.setParser=function(e){R(),a=e,"function"==typeof a.setLogger&&a.setLogger(s?d:null),O("setParser().")},this.addNamespaceRoot=function(e,t,r){R(),O(`addNamespaceRoot('${e}').`),o.push({prefix:e,target:t,defaultExt:r})},this.enableTestMode=function(){R(),O("enableTestMode()."),i=!0},this.enableLogging=function(){R(),s||(s=!0,d=new E,"function"==typeof a.setLogger&&a.setLogger(d),d.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(R(),O(`register('${e}').`),!0!==i)throw new Error("Container test mode is disabled.");const r=a.parse(e);n.set(I(r),t)},this.get=async function(s){if("failed"===e)throw d.error(`Container.get: rejected in failed state cdc='${s}'.`),new Error("Container is in failed state.");let p="start";try{d.log(`Container.get: cdc='${s}'.`),function(){if("notConfigured"!==e)return;d.log("Container.transition: notConfigured -> operational."),e="operational";const t=c.create({namespaces:o});"function"==typeof a.setLogger&&a.setLogger(d),l=new g({config:t,logger:d}),f=new y({parser:a,resolver:l,logger:d}),u=new N(d)}(),d.log(`Container.state: '${e}'.`),p="parse",d.log("Container.pipeline: parse:entry.");const m=a.parse(s);d.log(`Container.pipeline: parse:exit '${m.platform}::${m.moduleName}'.`),p="preprocess",d.log("Container.pipeline: preprocess:entry.");const h=function(e){let r=e;for(const e of t)r=e(r);return r}(m);if(d.log(`Container.pipeline: preprocess:exit '${h.platform}::${h.moduleName}'.`),!0===i){p="mock",d.log("Container.pipeline: mock-lookup:entry.");const e=I(h);if(n.has(e)){d.log(`Container.pipeline: mock-lookup:hit '${e}'.`),p="freeze",d.log("Container.pipeline: freeze:entry.");const t=A(n.get(e));return d.log("Container.pipeline: freeze:exit."),d.log("Container.pipeline: return:success."),_(t)}d.log(`Container.pipeline: mock-lookup:miss '${e}'.`)}else d.log("Container.pipeline: mock-lookup:disabled.");p="resolve",d.log("Container.pipeline: resolve:entry.");const $=await f.resolve(h);d.log(`Container.pipeline: resolve:exit nodes=${$.size}.`);const x=new Map,E=function(e){if(x.has(e))return x.get(e);if(!$.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=$.get(e);p="lifecycle",d.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const o=u.apply(t.depId,function(){p="instantiate",d.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},o=w(t.namespace,t.depId);for(const[t,r]of Object.entries(o)){const o=a.parse(r);e[t]=E(j(o))}const n=C.instantiate(t.depId,t.namespace,e);d.log(`Container.pipeline: instantiate:exit '${t.depId.platform}::${t.depId.moduleName}'.`),p="postprocess",d.log(`Container.pipeline: postprocess:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const i=function(e){let t=e;for(const e of r)t=e(t);return t}(n);d.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const s=v.execute(t.depId,i,t.namespace);p="freeze",d.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const c=A(s);return d.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),c});return d.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),x.set(e,o),o},b=E(j(h));return d.log("Container.pipeline: return:success."),_(b)}catch(t){throw d.error(`Container.pipeline: failed at stage='${p}'.`,t),d.log("Container.transition: operational -> failed."),e="failed",t}}}}return class{constructor(){const e=new v;this.addPreprocess=function(t){return e.addPreprocess(t)},this.addPostprocess=function(t){return e.addPostprocess(t)},this.setParser=function(t){return e.setParser(t)},this.addNamespaceRoot=function(t,r,o){return e.addNamespaceRoot(t,r,o)},this.enableTestMode=function(){return e.enableTestMode()},this.enableLogging=function(){return e.enableLogging()},this.register=function(t,r){return e.register(t,r)},this.get=function(t){return e.get(t)}}}});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).TeqFw_Di_Container=t()}(this,function(){"use strict";var e=Object.freeze({AS_IS:"A",FACTORY:"F"});var t=Object.freeze({SINGLETON:"S",TRANSIENT:"T"});var r=Object.freeze({TEQ:"teq",NODE:"node",NPM:"npm"});const o=r.TEQ,n=e.AS_IS,i=new Set(Object.values(r)),s=new Set(Object.values(e)),a=new Set(Object.values(t));let c=class{moduleName;platform;exportName;composition;life;wrappers;origin},l=class{create(e){const t=e&&"object"==typeof e?e:{},r=new c;r.moduleName="string"==typeof t.moduleName?t.moduleName:"";const l="string"==typeof t.platform?t.platform:void 0;r.platform=l&&i.has(l)?l:o;let p=null;null===t.exportName?p=null:"string"==typeof t.exportName&&(p=t.exportName),r.exportName=p;const f="string"==typeof t.composition?t.composition:void 0;r.composition=f&&s.has(f)?f:n;const u="string"==typeof t.life?t.life:void 0;return r.life=u&&a.has(u)?u:null,r.wrappers=Array.isArray(t.wrappers)?t.wrappers.filter(e=>"string"==typeof e):[],r.origin="string"==typeof t.origin?t.origin:"",Object.freeze(r.wrappers),Object.freeze(r)}};class p{constructor(){const o=new l;let n=null;this.parse=function(i){if(n&&n.log(`Parser.parse: input='${i}'.`),"string"!=typeof i)throw new Error("CDC must be a string.");if(0===i.length)throw new Error("CDC must be non-empty.");if(!/^[\x00-\x7F]+$/.test(i))throw new Error("CDC must be ASCII.");const s=i,a=function(e){let t=r.TEQ;if(e.startsWith("node:"))return t=r.NODE,{platform:t,source:e.slice(5)};if(e.startsWith("npm:"))return t=r.NPM,{platform:t,source:e.slice(4)};if(e.startsWith("teq:"))throw new Error("Explicit teq: prefix is forbidden.");return{platform:t,source:e}}(i),c=a.platform,l=a.source;if(0===l.length)throw new Error("moduleName must be non-empty.");const p=function(e,o){let n=null,i=!1,s=[],a=e;const c=a.match(/(\${1,3})(?:_[a-z][0-9A-Za-z]*)*$/);if(c){const e=c[1],r=c[0].slice(e.length);if(i=!0,"$"===e)n=t.SINGLETON;else if("$$"===e)n=t.TRANSIENT;else{if("$$$"!==e)throw new Error("Lifecycle marker is invalid.");n=null}return a=a.slice(0,c.index),r.length>0&&(s=r.slice(1).split("_")),{core:a,life:n,lifecycleDeclared:i,wrappers:s}}if(e.includes("$"))throw new Error("Invalid lifecycle encoding.");if(o!==r.NODE&&/(?:^|[^_])_[a-z][0-9A-Za-z]*$/.test(e))throw new Error("Wrapper without lifecycle is forbidden.");return{core:a,life:n,lifecycleDeclared:i,wrappers:s}}(l,c),f=function(e){const t=e.indexOf("__"),r=e.lastIndexOf("__");if(-1!==t&&t!==r)throw new Error("Export delimiter must appear at most once.");if(e.startsWith("__")||e.endsWith("__"))throw new Error("Malformed export segment.");let o=e,n=null;if(-1!==t){if(o=e.slice(0,t),n=e.slice(t+2),!n)throw new Error("Export must be non-empty.");if(n.includes("_"))throw new Error("Export must not contain _.");if(n.includes("$"))throw new Error("Export must not contain $.")}return{moduleName:o,exportName:n}}(p.core);!function(e,t){if(!e)throw new Error("moduleName must be non-empty.");if(e.startsWith("_")||e.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(e.includes("__"))throw new Error("moduleName must not contain __.");if(e.includes("$"))throw new Error("moduleName must not contain $.");if(t!==r.NPM){if(t===r.NODE){if(!/^[A-Za-z_][$0-9A-Za-z_/-]*$/.test(e))throw new Error("node moduleName must satisfy the built-in specifier form.")}else if(!/^[A-Za-z_][$0-9A-Za-z_]*$/.test(e))throw new Error("moduleName must satisfy the canonical identifier form.")}else if(!/^[@A-Za-z_][$0-9A-Za-z_./-]*$/.test(e))throw new Error("npm moduleName must satisfy the package specifier form.")}(f.moduleName,c);let u=e.AS_IS,d=f.exportName;p.lifecycleDeclared&&(null===d&&(d="default"),u=e.FACTORY);const m=o.create({moduleName:f.moduleName,platform:c,exportName:d,composition:u,life:p.life,wrappers:p.wrappers,origin:s});return n&&n.log(`Parser.parse: produced='${m.platform}::${m.moduleName}'.`),m},this.setLogger=function(e){n=e}}}let f=class{prefix;target;defaultExt},u=class{create(e){const t=e&&"object"==typeof e?e:{},r=new f;return r.prefix="string"==typeof t.prefix?t.prefix:void 0,r.target="string"==typeof t.target?t.target:void 0,r.defaultExt="string"==typeof t.defaultExt?t.defaultExt:void 0,Object.freeze(r)}};class d{namespaces;nodeModulesRoot}class m{constructor(){const e=new u;this.create=function(t){const r=t&&"object"==typeof t?t:{},o=new d,n=Array.isArray(r.namespaces)?r.namespaces:[];return o.namespaces=n.map(t=>e.create(t)),o.nodeModulesRoot="string"==typeof r.nodeModulesRoot?r.nodeModulesRoot:void 0,Object.freeze(o.namespaces),Object.freeze(o)}}}class g{constructor({config:e,importFn:t=e=>import(e),logger:r=null}){const o=new Map,n=e;let i;const s=t,a=r,c=function(e,t){if("node"===e){const e=`node:${t}`;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("npm"===e){const e=t;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("teq"!==e)throw new Error(`Unsupported platform: ${e}`);const r=function(e){let t=null,r=-1;const o=i.namespaces;for(const n of o){const o=e.startsWith(n.prefix);a&&a.log(`Resolver.namespace: prefix='${n.prefix}' match=${String(o)} module='${e}'.`),o&&n.prefix.length>r&&(t=n,r=n.prefix.length)}if(!t)throw new Error(`Namespace rule is not found for '${e}'.`);return t}(t),o=t.slice(r.prefix.length).split("_").join("/"),n=(s=o,(c=r.defaultExt)?s.endsWith(c)?s:`${s}${c}`:s);var s,c;const l=function(e,t){return e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}(r.target,n);return a&&a.log(`Resolver.specifier: module='${t}' -> '${l}'.`),l};this.resolve=async function(e){var t;await Promise.resolve(),i||(i={nodeModulesRoot:(t=n).nodeModulesRoot,namespaces:t.namespaces.map(e=>({prefix:e.prefix,target:e.target,defaultExt:e.defaultExt}))});const r=e.platform,l=e.moduleName,p=`${r}::${l}`;if(o.has(p))return a&&a.log(`Resolver.cache: hit key='${p}'.`),o.get(p);a&&a.log(`Resolver.cache: miss key='${p}'.`);const f=(async()=>{const e=c(r,l);return a&&a.log(`Resolver.import: '${e}'.`),s(e)})();o.set(p,f);try{return await f}catch(e){throw o.delete(p),a&&a.error(`Resolver.cache: evict key='${p}' after failure.`,e),e}}}}function w(e){const t=null===e.exportName?"":e.exportName,r=null===e.life?"":e.life,o=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,t,e.composition,r,o].join("::")}function h(e,t){const r=Reflect.get(e,"__deps__");if(void 0===r)return{};if(null===r||"object"!=typeof r||Array.isArray(r))throw new Error("__deps__ must be a plain object.");const o=null===t.exportName?"default":t.exportName,n=Reflect.get(r,o);if(null!=n&&"object"==typeof n&&!Array.isArray(n)){if(!Object.values(n).every(e=>"string"==typeof e))throw new Error("__deps__ export entries must map dependency names to CDC strings.");return n}if("default"===o){const e=Object.values(r);if(e.every(e=>"string"==typeof e))return r;if(e.every(e=>null!==e&&"object"==typeof e&&!Array.isArray(e)))return{};throw new Error("__deps__ must be either flat or export-scoped.")}return{}}class y{constructor({parser:e,resolver:t,logger:r=null}){const o=r,n=w,i=async function(r,s,a,c){const l=n(r);if(a.has(l)){const e=[...c,l].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const p=n(r);if(!s.has(p)){a.add(l),c.push(l);try{const n=await t.resolve(r);o&&o.log(`GraphResolver.walk: resolved '${p}'.`),s.set(p,{depId:r,namespace:n});const l=h(n,r);for(const t of Object.values(l)){const r=e.parse(t);o&&o.log(`GraphResolver.walk: edge '${p}' -> '${r.platform}::${r.moduleName}'.`),await i(r,s,a,c)}}finally{c.pop(),a.delete(l)}}};this.resolve=async function(e){const t=new Map,r=new Set;return await i(e,t,r,[]),t}}}class ${constructor(){this.instantiate=function(t,r,o){const n=function(e,t){if(null===e.exportName)return t;if(!(e.exportName in t))throw new Error(`Export '${e.exportName}' is not found in module namespace.`);return t[e.exportName]}(t,r);if(t.composition===e.AS_IS)return n;if(t.composition===e.FACTORY){if("function"!=typeof n)throw new Error("Factory composition requires a callable export.");const e=n;let t;if(function(e){try{return Reflect.construct(String,[],e),!0}catch{return!1}}(e)){t=new e(o)}else{t=e(o)}if(t instanceof Promise)throw new Error("Factory composition must return synchronously (non-Promise).");return t}throw new Error(`Unsupported composition mode: ${String(t.composition)}.`)}}}class x{constructor(r=null){const o=new Map,n=r,i=w;this.apply=function(r,s){if(r.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${r.composition}' cache=skip.`),s();if(r.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),s();if(r.life===t.SINGLETON){const e=i(r);if(o.has(e))return n&&n.log(`Lifecycle.cache: hit key='${e}'.`),o.get(e);n&&n.log(`Lifecycle.cache: miss key='${e}', create.`);const t=s();return o.set(e,t),n&&n.log(`Lifecycle.cache: stored key='${e}'.`),t}return n&&n.log("Lifecycle.apply: no lifecycle marker cache=skip."),s()}}}class E{constructor(){const e=function(e){return"function"==typeof e};this.execute=function(t,r,o){let n=r;const i=t.wrappers;for(const t of i){if(!(t in o))throw new Error(`Wrapper '${t}' is not found in module namespace.`);const r=o[t];if(!e(r))throw new Error(`Wrapper '${t}' must be callable.`);if(n=r(n),n instanceof Promise)throw new Error(`Wrapper '${t}' must return synchronously (non-Promise).`)}return n}}}const N=new WeakMap;function b(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;const r=e;if(N.has(r))return N.get(r);try{return Reflect.get(r,"then"),e}catch{const e=new Proxy(r,{get(e,t,r){if("then"!==t)return Reflect.get(e,t,r)}});return N.set(r,e),e}}class C{constructor(e="teqfw/di"){const t=`[${e}]`;this.log=function(e){console.debug(`${t} ${e}`)},this.error=function(e,r){console.error(`${t} ${e}`),r instanceof Error?console.error(r.stack??r.message):void 0!==r&&console.error(r)}}}const v=Object.freeze({log(){},error(){}});return class{constructor(){let e="notConfigured";const t=[],r=[],o=[],n=new Map;let i=!1,s=!1,a=new p;const c=new m;let l,f,u,d=v;const N=new $,_=new E,j=w,I=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;if("[object Module]"===Object.prototype.toString.call(e))return e;if(Object.isFrozen(e))return e;try{Object.freeze(e)}catch(e){d.log(`Container.freeze: skipped (${String(e)}).`)}return e},R=function(e){let r=e;for(const e of t)r=e(r);return r},A=function(e){let t=e;for(const e of r)t=e(t);return t},z=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},O=function(e){s&&d.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){z(),O("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){z(),O("addPostprocess()."),r.push(e)},this.addNamespaceRoot=function(e,t,r){z(),O(`addNamespaceRoot('${e}').`),o.push({prefix:e,target:t,defaultExt:r})},this.enableTestMode=function(){z(),O("enableTestMode()."),i=!0},this.enableLogging=function(){z(),s||(s=!0,d=new C,"function"==typeof a.setLogger&&a.setLogger(d),d.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(z(),O(`register('${e}').`),!0!==i)throw new Error("Container test mode is disabled.");const r=a.parse(e);n.set(j(r),t)},this.get=async function(t){if("failed"===e)throw d.error(`Container.get: rejected in failed state cdc='${t}'.`),new Error("Container is in failed state.");try{return function(){if("notConfigured"!==e)return;d.log("Container.transition: notConfigured -> operational."),e="operational";const t=c.create({namespaces:o});"function"==typeof a.setLogger&&a.setLogger(d),l=new g({config:t,logger:d}),f=new y({parser:a,resolver:l,logger:d}),u=new x(d)}(),d.log(`Container.state: '${e}'.`),await async function(e,t){const{parser:r,resolver:o,graphResolver:n,lifecycle:i,instantiator:s,wrapperExecutor:a,logger:c,testMode:l,mockRegistry:p,freeze:f,applyPreprocess:u,applyPostprocess:d}=e;if(!o||!n||!i)throw new Error("Container infrastructure is not initialized.");const m=w,g=w;let y="start";try{c.log(`Container.get: cdc='${t}'.`),y="parse",c.log("Container.pipeline: parse:entry.");const e=r.parse(t);c.log(`Container.pipeline: parse:exit '${e.platform}::${e.moduleName}'.`),y="preprocess",c.log("Container.pipeline: preprocess:entry.");const o=u(e);if(c.log(`Container.pipeline: preprocess:exit '${o.platform}::${o.moduleName}'.`),!0===l){y="mock",c.log("Container.pipeline: mock-lookup:entry.");const e=g(o);if(p.has(e)){c.log(`Container.pipeline: mock-lookup:hit '${e}'.`),y="freeze",c.log("Container.pipeline: freeze:entry.");const t=f(p.get(e));return c.log("Container.pipeline: freeze:exit."),c.log("Container.pipeline: return:success."),b(t)}c.log(`Container.pipeline: mock-lookup:miss '${e}'.`)}else c.log("Container.pipeline: mock-lookup:disabled.");y="resolve",c.log("Container.pipeline: resolve:entry.");const w=await n.resolve(o);c.log(`Container.pipeline: resolve:exit nodes=${w.size}.`);const $=new Map,x=function(e){if($.has(e))return $.get(e);if(!w.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=w.get(e);y="lifecycle",c.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const o=i.apply(t.depId,function(){y="instantiate",c.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},o=h(t.namespace,t.depId);for(const[t,n]of Object.entries(o)){const o=r.parse(n);e[t]=x(m(o))}const n=s.instantiate(t.depId,t.namespace,e);c.log(`Container.pipeline: instantiate:exit '${t.depId.platform}::${t.depId.moduleName}'.`),y="postprocess",c.log(`Container.pipeline: postprocess:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const i=d(n);c.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const l=a.execute(t.depId,i,t.namespace);y="freeze",c.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const p=f(l);return c.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),p});return c.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),$.set(e,o),o},E=x(m(o));return c.log("Container.pipeline: return:success."),b(E)}catch(e){throw c.error(`Container.pipeline: failed at stage='${y}'.`,e),e}}({parser:a,resolver:l,graphResolver:f,lifecycle:u,instantiator:N,wrapperExecutor:_,logger:d,testMode:i,mockRegistry:n,freeze:I,applyPreprocess:R,applyPostprocess:A},t)}catch(t){throw d.error("Container.transition: operational -> failed.",t),e="failed",t}}}}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teqfw/di",
3
- "version": "2.6.1",
3
+ "version": "2.7.0",
4
4
  "description": "Dependency Injection container for ES6 modules that works in both browser and Node.js apps.",
5
5
  "keywords": [
6
6
  "dependency injection",
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
 
@@ -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
 
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  /**
4
- * @namespace TeqFw_Di_Container_Wrapper_Executor
4
+ * @namespace TeqFw_Di_Container_Executor
5
5
  * @description Applies wrapper pipeline to resolved values.
6
6
  */
7
7
 
@@ -11,7 +11,7 @@
11
11
  * Executes wrapper pipeline declared in `depId.wrappers` using functions
12
12
  * exported by the resolved module namespace.
13
13
  */
14
- export default class TeqFw_Di_Container_Wrapper_Executor {
14
+ export default class TeqFw_Di_Container_Executor {
15
15
 
16
16
  /**
17
17
  * Creates wrapper executor instance.
@@ -1,7 +1,7 @@
1
1
  // @ts-check
2
2
 
3
3
  /**
4
- * @namespace TeqFw_Di_Container_Resolve_GraphResolver
4
+ * @namespace TeqFw_Di_Container_GraphResolver
5
5
  * @description Dependency graph resolver for container preloading.
6
6
  */
7
7
 
@@ -13,23 +13,23 @@
13
13
  */
14
14
 
15
15
  /**
16
- * @typedef {object} TeqFw_Di_Container_Resolve_GraphResolver_Dependencies
16
+ * @typedef {object} TeqFw_Di_Container_GraphResolver_Dependencies
17
17
  * @property {TeqFw_Di_Parser} parser
18
18
  * @property {TeqFw_Di_Resolver} resolver
19
19
  * @property {{log(message: string): void}|null} [logger]
20
20
  */
21
21
 
22
22
  /**
23
- * @typedef {{depId: TeqFw_Di_DepId__DTO, namespace: object}} TeqFw_Di_Container_Resolve_GraphResolver_Node
23
+ * @typedef {{depId: TeqFw_Di_DepId__DTO, namespace: object}} TeqFw_Di_Container_GraphResolver_Node
24
24
  */
25
25
 
26
- import {buildDependencyKey} from '../../Internal/DependencyKey.mjs';
27
- import {readDepsDecl} from '../../Internal/DepsDecl.mjs';
26
+ import {buildDependencyKey} from '../Internal/DependencyKey.mjs';
27
+ import {readDepsDecl} from '../Internal/DepsDecl.mjs';
28
28
 
29
- export default class TeqFw_Di_Container_Resolve_GraphResolver {
29
+ export default class TeqFw_Di_Container_GraphResolver {
30
30
 
31
31
  /**
32
- * @param {TeqFw_Di_Container_Resolve_GraphResolver_Dependencies} deps
32
+ * @param {TeqFw_Di_Container_GraphResolver_Dependencies} deps
33
33
  */
34
34
  constructor({parser, resolver, logger = null}) {
35
35
  /** @type {{log(message: string): void}|null} */
@@ -43,7 +43,7 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
43
43
 
44
44
  /**
45
45
  * @param {TeqFw_Di_DepId__DTO} depId
46
- * @param {Map<string, TeqFw_Di_Container_Resolve_GraphResolver_Node>} out
46
+ * @param {Map<string, TeqFw_Di_Container_GraphResolver_Node>} out
47
47
  * @param {Set<string>} stack
48
48
  * @param {string[]} chain
49
49
  * @returns {Promise<void>}
@@ -87,10 +87,10 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
87
87
  * Resolves full dependency graph for a root depId.
88
88
  *
89
89
  * @param {TeqFw_Di_DepId__DTO} depId
90
- * @returns {Promise<Map<string, TeqFw_Di_Container_Resolve_GraphResolver_Node>>}
90
+ * @returns {Promise<Map<string, TeqFw_Di_Container_GraphResolver_Node>>}
91
91
  */
92
92
  this.resolve = async function (depId) {
93
- /** @type {Map<string, TeqFw_Di_Container_Resolve_GraphResolver_Node>} */
93
+ /** @type {Map<string, TeqFw_Di_Container_GraphResolver_Node>} */
94
94
  const out = new Map();
95
95
  /** @type {Set<string>} */
96
96
  const stack = new Set();
@@ -1,11 +1,11 @@
1
1
  // @ts-check
2
2
 
3
3
  /**
4
- * @namespace TeqFw_Di_Container_Instantiate_Instantiator
4
+ * @namespace TeqFw_Di_Container_Instantiate
5
5
  * @description Instantiates selected exports using composition rules.
6
6
  */
7
7
 
8
- import TeqFw_Di_Enum_Composition from '../../Enum/Composition.mjs';
8
+ import TeqFw_Di_Enum_Composition from '../Enum/Composition.mjs';
9
9
 
10
10
  /**
11
11
  * Instantiate-stage immutable core executor.
@@ -25,7 +25,7 @@ import TeqFw_Di_Enum_Composition from '../../Enum/Composition.mjs';
25
25
  /**
26
26
  * @typedef {CallableFactory | ConstructableFactory} Factory
27
27
  */
28
- export default class TeqFw_Di_Container_Instantiate_Instantiator {
28
+ export default class TeqFw_Di_Container_Instantiate {
29
29
 
30
30
  /**
31
31
  * Creates instantiator instance.
@@ -1,13 +1,13 @@
1
1
  // @ts-check
2
2
 
3
3
  /**
4
- * @namespace TeqFw_Di_Container_Lifecycle_Registry
4
+ * @namespace TeqFw_Di_Container_Lifecycle
5
5
  * @description Lifecycle policy cache for produced values.
6
6
  */
7
7
 
8
- import TeqFw_Di_Enum_Composition from '../../Enum/Composition.mjs';
9
- import TeqFw_Di_Enum_Life from '../../Enum/Life.mjs';
10
- import {buildDependencyKey} from '../../Internal/DependencyKey.mjs';
8
+ import TeqFw_Di_Enum_Composition from '../Enum/Composition.mjs';
9
+ import TeqFw_Di_Enum_Life from '../Enum/Life.mjs';
10
+ import {buildDependencyKey} from '../Internal/DependencyKey.mjs';
11
11
 
12
12
  /**
13
13
  * Lifecycle-stage registry for produced dependency values.
@@ -17,7 +17,7 @@ import {buildDependencyKey} from '../../Internal/DependencyKey.mjs';
17
17
  * - transient values are never cached;
18
18
  * - as-is composition is returned as produced without lifecycle caching.
19
19
  */
20
- export default class TeqFw_Di_Container_Lifecycle_Registry {
20
+ export default class TeqFw_Di_Container_Lifecycle {
21
21
 
22
22
  /**
23
23
  * Creates lifecycle registry instance.
@@ -0,0 +1,136 @@
1
+ // @ts-check
2
+
3
+ /**
4
+ * @namespace TeqFw_Di_Container_Pipeline
5
+ * @description Internal resolution pipeline executor for the container entry point.
6
+ */
7
+
8
+ import {buildDependencyKey} from '../Internal/DependencyKey.mjs';
9
+ import {readDepsDecl} from '../Internal/DepsDecl.mjs';
10
+ import {makePromiseSafe} from '../Internal/PromiseSafe.mjs';
11
+
12
+ /**
13
+ * @typedef {object} TeqFw_Di_Container_Pipeline_Context
14
+ * @property {TeqFw_Di_Parser} parser
15
+ * @property {TeqFw_Di_Resolver|undefined} resolver
16
+ * @property {TeqFw_Di_Container_GraphResolver|undefined} graphResolver
17
+ * @property {TeqFw_Di_Container_Lifecycle|undefined} lifecycle
18
+ * @property {TeqFw_Di_Container_Instantiate} instantiator
19
+ * @property {TeqFw_Di_Container_Executor} wrapperExecutor
20
+ * @property {{log(message: string): void, error(message: string, error?: unknown): void}} logger
21
+ * @property {boolean} testMode
22
+ * @property {Map<string, unknown>} mockRegistry
23
+ * @property {(value: unknown) => unknown} freeze
24
+ * @property {(depId: TeqFw_Di_DepId__DTO) => TeqFw_Di_DepId__DTO} applyPreprocess
25
+ * @property {(value: unknown) => unknown} applyPostprocess
26
+ */
27
+
28
+ /**
29
+ * Executes full container get pipeline for a CDC.
30
+ *
31
+ * @param {TeqFw_Di_Container_Pipeline_Context} ctx
32
+ * @param {string} cdc
33
+ * @returns {Promise<any>}
34
+ */
35
+ export async function executeContainerPipeline(ctx, cdc) {
36
+ const {
37
+ parser,
38
+ resolver,
39
+ graphResolver,
40
+ lifecycle,
41
+ instantiator,
42
+ wrapperExecutor,
43
+ logger,
44
+ testMode,
45
+ mockRegistry,
46
+ freeze,
47
+ applyPreprocess,
48
+ applyPostprocess,
49
+ } = ctx;
50
+
51
+ if (!resolver || !graphResolver || !lifecycle) {
52
+ throw new Error('Container infrastructure is not initialized.');
53
+ }
54
+
55
+ const getKey = buildDependencyKey;
56
+ const getMockKey = buildDependencyKey;
57
+
58
+ /** @type {string} */
59
+ let stage = 'start';
60
+ try {
61
+ logger.log(`Container.get: cdc='${cdc}'.`);
62
+ stage = 'parse';
63
+ logger.log('Container.pipeline: parse:entry.');
64
+ const parsed = parser.parse(cdc);
65
+ logger.log(`Container.pipeline: parse:exit '${parsed.platform}::${parsed.moduleName}'.`);
66
+ stage = 'preprocess';
67
+ logger.log('Container.pipeline: preprocess:entry.');
68
+ const root = applyPreprocess(parsed);
69
+ logger.log(`Container.pipeline: preprocess:exit '${root.platform}::${root.moduleName}'.`);
70
+ if (testMode === true) {
71
+ stage = 'mock';
72
+ logger.log('Container.pipeline: mock-lookup:entry.');
73
+ const key = getMockKey(root);
74
+ if (mockRegistry.has(key)) {
75
+ logger.log(`Container.pipeline: mock-lookup:hit '${key}'.`);
76
+ stage = 'freeze';
77
+ logger.log('Container.pipeline: freeze:entry.');
78
+ const frozenMock = freeze(mockRegistry.get(key));
79
+ logger.log('Container.pipeline: freeze:exit.');
80
+ logger.log('Container.pipeline: return:success.');
81
+ return makePromiseSafe(frozenMock);
82
+ }
83
+ logger.log(`Container.pipeline: mock-lookup:miss '${key}'.`);
84
+ } else {
85
+ logger.log('Container.pipeline: mock-lookup:disabled.');
86
+ }
87
+ stage = 'resolve';
88
+ logger.log('Container.pipeline: resolve:entry.');
89
+ const graph = await graphResolver.resolve(root);
90
+ logger.log(`Container.pipeline: resolve:exit nodes=${graph.size}.`);
91
+ /** @type {Map<string, unknown>} */
92
+ const built = new Map();
93
+
94
+ const build = function (key) {
95
+ if (built.has(key)) return built.get(key);
96
+ if (!graph.has(key)) throw new Error(`Resolved graph node is missing for '${key}'.`);
97
+ const node = graph.get(key);
98
+ stage = 'lifecycle';
99
+ logger.log(`Container.pipeline: lifecycle:entry '${node.depId.platform}::${node.depId.moduleName}'.`);
100
+ const value = lifecycle.apply(node.depId, function () {
101
+ stage = 'instantiate';
102
+ logger.log(`Container.pipeline: instantiate:entry '${node.depId.platform}::${node.depId.moduleName}'.`);
103
+ /** @type {Record<string, unknown>} */
104
+ const deps = {};
105
+ /** @type {Record<string, unknown>} */
106
+ const depsDecl = readDepsDecl(node.namespace, node.depId);
107
+ for (const [name, cdcValue] of Object.entries(depsDecl)) {
108
+ const childDepId = parser.parse(/** @type {string} */ (cdcValue));
109
+ deps[name] = build(getKey(childDepId));
110
+ }
111
+ const instantiated = instantiator.instantiate(node.depId, node.namespace, deps);
112
+ logger.log(`Container.pipeline: instantiate:exit '${node.depId.platform}::${node.depId.moduleName}'.`);
113
+ stage = 'postprocess';
114
+ logger.log(`Container.pipeline: postprocess:entry '${node.depId.platform}::${node.depId.moduleName}'.`);
115
+ const postprocessed = applyPostprocess(instantiated);
116
+ logger.log(`Container.pipeline: postprocess:exit '${node.depId.platform}::${node.depId.moduleName}'.`);
117
+ const wrapped = wrapperExecutor.execute(node.depId, postprocessed, node.namespace);
118
+ stage = 'freeze';
119
+ logger.log(`Container.pipeline: freeze:entry '${node.depId.platform}::${node.depId.moduleName}'.`);
120
+ const frozen = freeze(wrapped);
121
+ logger.log(`Container.pipeline: freeze:exit '${node.depId.platform}::${node.depId.moduleName}'.`);
122
+ return frozen;
123
+ });
124
+ logger.log(`Container.pipeline: lifecycle:exit '${node.depId.platform}::${node.depId.moduleName}'.`);
125
+ built.set(key, value);
126
+ return value;
127
+ };
128
+
129
+ const result = build(getKey(root));
130
+ logger.log('Container.pipeline: return:success.');
131
+ return makePromiseSafe(result);
132
+ } catch (error) {
133
+ logger.error(`Container.pipeline: failed at stage='${stage}'.`, error);
134
+ throw error;
135
+ }
136
+ }
package/src/Container.mjs CHANGED
@@ -5,52 +5,221 @@
5
5
  * @description DI container orchestration entry point.
6
6
  */
7
7
 
8
- import TeqFw_Di_Container_Runtime from './Container/Runtime.mjs';
8
+ import TeqFw_Di_Parser from './Parser.mjs';
9
+ import {Factory as TeqFw_Di_Dto_Resolver_Config_Factory} from './Dto/Resolver/Config.mjs';
10
+ import TeqFw_Di_Resolver from './Container/Resolver.mjs';
11
+ import TeqFw_Di_Container_GraphResolver from './Container/GraphResolver.mjs';
12
+ import TeqFw_Di_Container_Instantiate from './Container/Instantiate.mjs';
13
+ import TeqFw_Di_Container_Lifecycle from './Container/Lifecycle.mjs';
14
+ import TeqFw_Di_Container_Executor from './Container/Executor.mjs';
15
+ import {executeContainerPipeline} from './Container/Pipeline.mjs';
16
+ import TeqFw_Di_Internal_Logger, {TeqFw_Di_Internal_Logger_Noop} from './Internal/Logger.mjs';
17
+ import {buildDependencyKey} from './Internal/DependencyKey.mjs';
18
+
19
+ /**
20
+ * @typedef {'notConfigured'|'operational'|'failed'} TeqFw_Di_Container_State
21
+ */
9
22
 
10
23
  /**
11
24
  * Container orchestration boundary.
12
25
  *
13
- * Delegates all runtime behavior to an internal runtime coordinator while
14
- * preserving the public API and contract surface.
15
- *
16
26
  * @LLM-DOC
17
27
  * Spec: ./ctx/docs/code/components/container.md
18
28
  */
19
29
  export default class TeqFw_Di_Container {
20
30
  constructor() {
21
- /** @type {TeqFw_Di_Container_Runtime} */
22
- const runtime = new TeqFw_Di_Container_Runtime();
31
+ /** @type {TeqFw_Di_Container_State} */
32
+ let state = 'notConfigured';
33
+ /** @type {((depId: TeqFw_Di_DepId__DTO) => TeqFw_Di_DepId__DTO)[]} */
34
+ const preprocess = [];
35
+ /** @type {((value: unknown) => unknown)[]} */
36
+ const postprocess = [];
37
+ /** @type {TeqFw_Di_Dto_Resolver_Config_Namespace__DTO[]} */
38
+ const namespaceRoots = [];
39
+ /** @type {Map<string, unknown>} */
40
+ const mockRegistry = new Map();
41
+ let testMode = false;
42
+ let loggingEnabled = false;
43
+ /** @type {TeqFw_Di_Parser} */
44
+ let parser = new TeqFw_Di_Parser();
45
+ /** @type {TeqFw_Di_Dto_Resolver_Config__Factory} */
46
+ const configFactory = new TeqFw_Di_Dto_Resolver_Config_Factory();
47
+ /** @type {TeqFw_Di_Resolver|undefined} */
48
+ let resolver;
49
+ /** @type {TeqFw_Di_Container_GraphResolver|undefined} */
50
+ let graphResolver;
51
+ /** @type {TeqFw_Di_Container_Lifecycle|undefined} */
52
+ let lifecycle;
53
+ /** @type {{log(message: string): void, error(message: string, error?: unknown): void}} */
54
+ let logger = TeqFw_Di_Internal_Logger_Noop;
55
+ /** @type {TeqFw_Di_Container_Instantiate} */
56
+ const instantiator = new TeqFw_Di_Container_Instantiate();
57
+ /** @type {TeqFw_Di_Container_Executor} */
58
+ const wrapperExecutor = new TeqFw_Di_Container_Executor();
23
59
 
24
- this.addPreprocess = function (fn) {
25
- return runtime.addPreprocess(fn);
60
+ const getKey = buildDependencyKey;
61
+ const getMockKey = buildDependencyKey;
62
+
63
+ const freeze = function (value) {
64
+ if ((value === null) || (value === undefined)) return value;
65
+ const type = typeof value;
66
+ if ((type !== 'object') && (type !== 'function')) return value;
67
+ if (Object.prototype.toString.call(value) === '[object Module]') return value;
68
+ if (Object.isFrozen(value)) return value;
69
+ try {
70
+ Object.freeze(value);
71
+ } catch (error) {
72
+ logger.log(`Container.freeze: skipped (${String(error)}).`);
73
+ }
74
+ return value;
26
75
  };
27
76
 
28
- this.addPostprocess = function (fn) {
29
- return runtime.addPostprocess(fn);
77
+ const applyPreprocess = function (depId) {
78
+ /** @type {TeqFw_Di_DepId__DTO} */
79
+ let current = depId;
80
+ for (const fn of preprocess) current = fn(current);
81
+ return current;
82
+ };
83
+
84
+ const applyPostprocess = function (value) {
85
+ /** @type {unknown} */
86
+ let current = value;
87
+ for (const fn of postprocess) current = fn(current);
88
+ return current;
89
+ };
90
+
91
+ const assertBuilderStage = function () {
92
+ if (state !== 'notConfigured') throw new Error('Container configuration is locked.');
93
+ };
94
+
95
+ const logBuilder = function (message) {
96
+ if (!loggingEnabled) return;
97
+ logger.log(`Container.builder: ${message}`);
98
+ };
99
+
100
+ const initializeInfrastructure = function () {
101
+ if (state !== 'notConfigured') return;
102
+ logger.log('Container.transition: notConfigured -> operational.');
103
+ state = 'operational';
104
+ const resolverConfig = configFactory.create({namespaces: namespaceRoots});
105
+ if (typeof parser.setLogger === 'function') parser.setLogger(logger);
106
+ resolver = new TeqFw_Di_Resolver({config: resolverConfig, logger});
107
+ graphResolver = new TeqFw_Di_Container_GraphResolver({parser, resolver, logger});
108
+ lifecycle = new TeqFw_Di_Container_Lifecycle(logger);
30
109
  };
31
110
 
32
- this.setParser = function (next) {
33
- return runtime.setParser(next);
111
+ /**
112
+ * Adds a preprocessing hook.
113
+ *
114
+ * @param {(depId: TeqFw_Di_DepId__DTO) => TeqFw_Di_DepId__DTO} fn
115
+ * @returns {void}
116
+ */
117
+ this.addPreprocess = function (fn) {
118
+ assertBuilderStage();
119
+ logBuilder('addPreprocess().');
120
+ preprocess.push(fn);
34
121
  };
35
122
 
123
+ /**
124
+ * Adds a postprocessing hook.
125
+ *
126
+ * @param {(value: unknown) => unknown} fn
127
+ * @returns {void}
128
+ */
129
+ this.addPostprocess = function (fn) {
130
+ assertBuilderStage();
131
+ logBuilder('addPostprocess().');
132
+ postprocess.push(fn);
133
+ };
134
+
135
+ /**
136
+ * Registers namespace root mapping.
137
+ *
138
+ * @param {string} prefix
139
+ * @param {string} target
140
+ * @param {string} defaultExt
141
+ * @returns {void}
142
+ */
36
143
  this.addNamespaceRoot = function (prefix, target, defaultExt) {
37
- return runtime.addNamespaceRoot(prefix, target, defaultExt);
144
+ assertBuilderStage();
145
+ logBuilder(`addNamespaceRoot('${prefix}').`);
146
+ namespaceRoots.push({prefix, target, defaultExt});
38
147
  };
39
148
 
149
+ /**
150
+ * Enables test mode.
151
+ *
152
+ * @returns {void}
153
+ */
40
154
  this.enableTestMode = function () {
41
- return runtime.enableTestMode();
155
+ assertBuilderStage();
156
+ logBuilder('enableTestMode().');
157
+ testMode = true;
42
158
  };
43
159
 
160
+ /**
161
+ * Enables diagnostic logging.
162
+ *
163
+ * @returns {void}
164
+ */
44
165
  this.enableLogging = function () {
45
- return runtime.enableLogging();
166
+ assertBuilderStage();
167
+ if (loggingEnabled) return;
168
+ loggingEnabled = true;
169
+ logger = new TeqFw_Di_Internal_Logger();
170
+ if (typeof parser.setLogger === 'function') parser.setLogger(logger);
171
+ logger.log('Container.builder: enableLogging().');
46
172
  };
47
173
 
174
+ /**
175
+ * Registers a mock value for a CDC.
176
+ *
177
+ * @param {string} cdc
178
+ * @param {any} mock
179
+ * @returns {void}
180
+ */
48
181
  this.register = function (cdc, mock) {
49
- return runtime.register(cdc, mock);
182
+ assertBuilderStage();
183
+ logBuilder(`register('${cdc}').`);
184
+ if (testMode !== true) throw new Error('Container test mode is disabled.');
185
+ const depId = parser.parse(cdc);
186
+ mockRegistry.set(getMockKey(depId), mock);
50
187
  };
51
188
 
52
- this.get = function (cdc) {
53
- return runtime.get(cdc);
189
+ /**
190
+ * Resolves a CDC into a frozen linked instance.
191
+ *
192
+ * @param {string} cdc
193
+ * @returns {Promise<any>}
194
+ */
195
+ this.get = async function (cdc) {
196
+ if (state === 'failed') {
197
+ logger.error(`Container.get: rejected in failed state cdc='${cdc}'.`);
198
+ throw new Error('Container is in failed state.');
199
+ }
200
+
201
+ try {
202
+ initializeInfrastructure();
203
+ logger.log(`Container.state: '${state}'.`);
204
+ return await executeContainerPipeline({
205
+ parser,
206
+ resolver,
207
+ graphResolver,
208
+ lifecycle,
209
+ instantiator,
210
+ wrapperExecutor,
211
+ logger,
212
+ testMode,
213
+ mockRegistry,
214
+ freeze,
215
+ applyPreprocess,
216
+ applyPostprocess,
217
+ }, cdc);
218
+ } catch (error) {
219
+ logger.error(`Container.transition: operational -> failed.`, error);
220
+ state = 'failed';
221
+ throw error;
222
+ }
54
223
  };
55
224
  }
56
225
  }
package/src/Parser.mjs CHANGED
@@ -163,10 +163,10 @@ export default class TeqFw_Di_Parser {
163
163
  /** @type {typeof TeqFw_Di_Enum_Composition[keyof typeof TeqFw_Di_Enum_Composition]} */
164
164
  let composition = TeqFw_Di_Enum_Composition.AS_IS;
165
165
  let exportName = split.exportName;
166
- if (exportName !== null) {
167
- composition = TeqFw_Di_Enum_Composition.FACTORY;
168
- } else if (lifecycle.lifecycleDeclared) {
169
- exportName = 'default';
166
+ if (lifecycle.lifecycleDeclared) {
167
+ if (exportName === null) {
168
+ exportName = 'default';
169
+ }
170
170
  composition = TeqFw_Di_Enum_Composition.FACTORY;
171
171
  }
172
172
 
package/types.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  declare global {
2
2
  type TeqFw_Di_Config_NamespaceRegistry = import("./src/Config/NamespaceRegistry.mjs").default;
3
3
  type TeqFw_Di_Container = import("./src/Container.mjs").default;
4
- type TeqFw_Di_Container_Instantiate_Instantiator = import("./src/Container/Instantiate/Instantiator.mjs").default;
5
- type TeqFw_Di_Container_Lifecycle_Registry = import("./src/Container/Lifecycle/Registry.mjs").default;
6
- type TeqFw_Di_Container_Resolve_GraphResolver = import("./src/Container/Resolve/GraphResolver.mjs").default;
7
- type TeqFw_Di_Container_Wrapper_Executor = import("./src/Container/Wrapper/Executor.mjs").default;
4
+ type TeqFw_Di_Container_Instantiate = import("./src/Container/Instantiate.mjs").default;
5
+ type TeqFw_Di_Container_Lifecycle = import("./src/Container/Lifecycle.mjs").default;
6
+ type TeqFw_Di_Container_GraphResolver = import("./src/Container/GraphResolver.mjs").default;
7
+ type TeqFw_Di_Container_Executor = import("./src/Container/Executor.mjs").default;
8
8
  type TeqFw_Di_Parser = import("./src/Parser.mjs").default;
9
9
  type TeqFw_Di_DepId = import("./src/Dto/DepId.mjs").default;
10
10
  type TeqFw_Di_DepId__DTO = import("./src/Dto/DepId.mjs").default;
@@ -1,244 +0,0 @@
1
- // @ts-check
2
-
3
- /**
4
- * @namespace TeqFw_Di_Container_Runtime
5
- * @description Internal runtime coordinator for container orchestration.
6
- */
7
-
8
- import TeqFw_Di_Parser from '../Parser.mjs';
9
- import {Factory as TeqFw_Di_Dto_Resolver_Config_Factory} from '../Dto/Resolver/Config.mjs';
10
- import TeqFw_Di_Resolver from './Resolver.mjs';
11
- import TeqFw_Di_Container_Resolve_GraphResolver from './Resolve/GraphResolver.mjs';
12
- import TeqFw_Di_Container_Instantiate_Instantiator from './Instantiate/Instantiator.mjs';
13
- import TeqFw_Di_Container_Lifecycle_Registry from './Lifecycle/Registry.mjs';
14
- import TeqFw_Di_Container_Wrapper_Executor from './Wrapper/Executor.mjs';
15
- import TeqFw_Di_Internal_Logger, {TeqFw_Di_Internal_Logger_Noop} from '../Internal/Logger.mjs';
16
- import {buildDependencyKey} from '../Internal/DependencyKey.mjs';
17
- import {readDepsDecl} from '../Internal/DepsDecl.mjs';
18
- import {makePromiseSafe} from '../Internal/PromiseSafe.mjs';
19
-
20
- /**
21
- * @typedef {'notConfigured'|'operational'|'failed'} TeqFw_Di_Container_State
22
- */
23
-
24
- export default class TeqFw_Di_Container_Runtime {
25
- constructor() {
26
- /** @type {TeqFw_Di_Container_State} */
27
- let state = 'notConfigured';
28
- /** @type {((depId: TeqFw_Di_DepId__DTO) => TeqFw_Di_DepId__DTO)[]} */
29
- const preprocess = [];
30
- /** @type {((value: unknown) => unknown)[]} */
31
- const postprocess = [];
32
- /** @type {TeqFw_Di_Dto_Resolver_Config_Namespace__DTO[]} */
33
- const namespaceRoots = [];
34
- /** @type {Map<string, unknown>} */
35
- const mockRegistry = new Map();
36
- let testMode = false;
37
- let loggingEnabled = false;
38
- /** @type {TeqFw_Di_Parser} */
39
- let parser = new TeqFw_Di_Parser();
40
- /** @type {TeqFw_Di_Dto_Resolver_Config__Factory} */
41
- const configFactory = new TeqFw_Di_Dto_Resolver_Config_Factory();
42
- /** @type {TeqFw_Di_Resolver|undefined} */
43
- let resolver;
44
- /** @type {TeqFw_Di_Container_Resolve_GraphResolver|undefined} */
45
- let graphResolver;
46
- /** @type {TeqFw_Di_Container_Lifecycle_Registry|undefined} */
47
- let lifecycle;
48
- /** @type {{log(message: string): void, error(message: string, error?: unknown): void}} */
49
- let logger = TeqFw_Di_Internal_Logger_Noop;
50
- /** @type {TeqFw_Di_Container_Instantiate_Instantiator} */
51
- const instantiator = new TeqFw_Di_Container_Instantiate_Instantiator();
52
- /** @type {TeqFw_Di_Container_Wrapper_Executor} */
53
- const wrapperExecutor = new TeqFw_Di_Container_Wrapper_Executor();
54
-
55
- const getKey = buildDependencyKey;
56
- const getMockKey = buildDependencyKey;
57
-
58
- const freeze = function (value) {
59
- if ((value === null) || (value === undefined)) return value;
60
- const type = typeof value;
61
- if ((type !== 'object') && (type !== 'function')) return value;
62
- if (Object.prototype.toString.call(value) === '[object Module]') return value;
63
- if (Object.isFrozen(value)) return value;
64
- try {
65
- Object.freeze(value);
66
- } catch (error) {
67
- logger.log(`Container.freeze: skipped (${String(error)}).`);
68
- }
69
- return value;
70
- };
71
-
72
- const applyPreprocess = function (depId) {
73
- /** @type {TeqFw_Di_DepId__DTO} */
74
- let current = depId;
75
- for (const fn of preprocess) current = fn(current);
76
- return current;
77
- };
78
-
79
- const applyPostprocess = function (value) {
80
- /** @type {unknown} */
81
- let current = value;
82
- for (const fn of postprocess) current = fn(current);
83
- return current;
84
- };
85
-
86
- const assertBuilderStage = function () {
87
- if (state !== 'notConfigured') throw new Error('Container configuration is locked.');
88
- };
89
-
90
- const logBuilder = function (message) {
91
- if (!loggingEnabled) return;
92
- logger.log(`Container.builder: ${message}`);
93
- };
94
-
95
- const initializeInfrastructure = function () {
96
- if (state !== 'notConfigured') return;
97
- logger.log('Container.transition: notConfigured -> operational.');
98
- state = 'operational';
99
- const resolverConfig = configFactory.create({namespaces: namespaceRoots});
100
- if (typeof parser.setLogger === 'function') parser.setLogger(logger);
101
- resolver = new TeqFw_Di_Resolver({config: resolverConfig, logger});
102
- graphResolver = new TeqFw_Di_Container_Resolve_GraphResolver({parser, resolver, logger});
103
- lifecycle = new TeqFw_Di_Container_Lifecycle_Registry(logger);
104
- };
105
-
106
- this.addPreprocess = function (fn) {
107
- assertBuilderStage();
108
- logBuilder('addPreprocess().');
109
- preprocess.push(fn);
110
- };
111
-
112
- this.addPostprocess = function (fn) {
113
- assertBuilderStage();
114
- logBuilder('addPostprocess().');
115
- postprocess.push(fn);
116
- };
117
-
118
- this.setParser = function (next) {
119
- assertBuilderStage();
120
- parser = next;
121
- if (typeof parser.setLogger === 'function') parser.setLogger(loggingEnabled ? logger : null);
122
- logBuilder('setParser().');
123
- };
124
-
125
- this.addNamespaceRoot = function (prefix, target, defaultExt) {
126
- assertBuilderStage();
127
- logBuilder(`addNamespaceRoot('${prefix}').`);
128
- namespaceRoots.push({prefix, target, defaultExt});
129
- };
130
-
131
- this.enableTestMode = function () {
132
- assertBuilderStage();
133
- logBuilder('enableTestMode().');
134
- testMode = true;
135
- };
136
-
137
- this.enableLogging = function () {
138
- assertBuilderStage();
139
- if (loggingEnabled) return;
140
- loggingEnabled = true;
141
- logger = new TeqFw_Di_Internal_Logger();
142
- if (typeof parser.setLogger === 'function') parser.setLogger(logger);
143
- logger.log('Container.builder: enableLogging().');
144
- };
145
-
146
- this.register = function (cdc, mock) {
147
- assertBuilderStage();
148
- logBuilder(`register('${cdc}').`);
149
- if (testMode !== true) throw new Error('Container test mode is disabled.');
150
- const depId = parser.parse(cdc);
151
- mockRegistry.set(getMockKey(depId), mock);
152
- };
153
-
154
- this.get = async function (cdc) {
155
- if (state === 'failed') {
156
- logger.error(`Container.get: rejected in failed state cdc='${cdc}'.`);
157
- throw new Error('Container is in failed state.');
158
- }
159
-
160
- /** @type {string} */
161
- let stage = 'start';
162
- try {
163
- logger.log(`Container.get: cdc='${cdc}'.`);
164
- initializeInfrastructure();
165
- logger.log(`Container.state: '${state}'.`);
166
- stage = 'parse';
167
- logger.log('Container.pipeline: parse:entry.');
168
- const parsed = parser.parse(cdc);
169
- logger.log(`Container.pipeline: parse:exit '${parsed.platform}::${parsed.moduleName}'.`);
170
- stage = 'preprocess';
171
- logger.log('Container.pipeline: preprocess:entry.');
172
- const root = applyPreprocess(parsed);
173
- logger.log(`Container.pipeline: preprocess:exit '${root.platform}::${root.moduleName}'.`);
174
- if (testMode === true) {
175
- stage = 'mock';
176
- logger.log('Container.pipeline: mock-lookup:entry.');
177
- const key = getMockKey(root);
178
- if (mockRegistry.has(key)) {
179
- logger.log(`Container.pipeline: mock-lookup:hit '${key}'.`);
180
- stage = 'freeze';
181
- logger.log('Container.pipeline: freeze:entry.');
182
- const frozenMock = freeze(mockRegistry.get(key));
183
- logger.log('Container.pipeline: freeze:exit.');
184
- logger.log('Container.pipeline: return:success.');
185
- return makePromiseSafe(frozenMock);
186
- }
187
- logger.log(`Container.pipeline: mock-lookup:miss '${key}'.`);
188
- } else {
189
- logger.log('Container.pipeline: mock-lookup:disabled.');
190
- }
191
- stage = 'resolve';
192
- logger.log('Container.pipeline: resolve:entry.');
193
- const graph = await graphResolver.resolve(root);
194
- logger.log(`Container.pipeline: resolve:exit nodes=${graph.size}.`);
195
- /** @type {Map<string, unknown>} */
196
- const built = new Map();
197
-
198
- const build = function (key) {
199
- if (built.has(key)) return built.get(key);
200
- if (!graph.has(key)) throw new Error(`Resolved graph node is missing for '${key}'.`);
201
- const node = graph.get(key);
202
- stage = 'lifecycle';
203
- logger.log(`Container.pipeline: lifecycle:entry '${node.depId.platform}::${node.depId.moduleName}'.`);
204
- const value = lifecycle.apply(node.depId, function () {
205
- stage = 'instantiate';
206
- logger.log(`Container.pipeline: instantiate:entry '${node.depId.platform}::${node.depId.moduleName}'.`);
207
- /** @type {Record<string, unknown>} */
208
- const deps = {};
209
- /** @type {Record<string, unknown>} */
210
- const depsDecl = readDepsDecl(node.namespace, node.depId);
211
- for (const [name, cdcValue] of Object.entries(depsDecl)) {
212
- const childDepId = parser.parse(/** @type {string} */ (cdcValue));
213
- deps[name] = build(getKey(childDepId));
214
- }
215
- const instantiated = instantiator.instantiate(node.depId, node.namespace, deps);
216
- logger.log(`Container.pipeline: instantiate:exit '${node.depId.platform}::${node.depId.moduleName}'.`);
217
- stage = 'postprocess';
218
- logger.log(`Container.pipeline: postprocess:entry '${node.depId.platform}::${node.depId.moduleName}'.`);
219
- const postprocessed = applyPostprocess(instantiated);
220
- logger.log(`Container.pipeline: postprocess:exit '${node.depId.platform}::${node.depId.moduleName}'.`);
221
- const wrapped = wrapperExecutor.execute(node.depId, postprocessed, node.namespace);
222
- stage = 'freeze';
223
- logger.log(`Container.pipeline: freeze:entry '${node.depId.platform}::${node.depId.moduleName}'.`);
224
- const frozen = freeze(wrapped);
225
- logger.log(`Container.pipeline: freeze:exit '${node.depId.platform}::${node.depId.moduleName}'.`);
226
- return frozen;
227
- });
228
- logger.log(`Container.pipeline: lifecycle:exit '${node.depId.platform}::${node.depId.moduleName}'.`);
229
- built.set(key, value);
230
- return value;
231
- };
232
-
233
- const result = build(getKey(root));
234
- logger.log('Container.pipeline: return:success.');
235
- return makePromiseSafe(result);
236
- } catch (error) {
237
- logger.error(`Container.pipeline: failed at stage='${stage}'.`, error);
238
- logger.log('Container.transition: operational -> failed.');
239
- state = 'failed';
240
- throw error;
241
- }
242
- };
243
- }
244
- }