@teqfw/di 2.6.1 → 2.6.2

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,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.6.2 - 2026-06-08 - Documentation alignment and container simplification
4
+
5
+ * Removed the legacy `setParser()` extension point and flattened the internal container implementation layout.
6
+ * Updated the `ctx/docs/code` documentation to match the current container pipeline, namespace registry flow, and namespace-mapping model.
7
+ * Refined the public code-facing documentation so it no longer describes parser replacement or wrapper handling in obsolete terms.
8
+ * Updated package version metadata to `2.6.2`.
9
+
3
10
  ## 2.6.1 - 2026-06-07 - Type declarations cleanup
4
11
 
5
12
  * 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()`;
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',
@@ -304,35 +281,28 @@ export const PACKAGE_API: PackageApiContract = {
304
281
  },
305
282
  {
306
283
  alias: 'TeqFw_Di_Container_Instantiate_Instantiator',
307
- source: './src/Container/Instantiate/Instantiator.mjs',
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
289
  alias: 'TeqFw_Di_Container_Lifecycle_Registry',
313
- source: './src/Container/Lifecycle/Registry.mjs',
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
295
  alias: 'TeqFw_Di_Container_Resolve_GraphResolver',
319
- source: './src/Container/Resolve/GraphResolver.mjs',
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
301
  alias: 'TeqFw_Di_Container_Wrapper_Executor',
325
- source: './src/Container/Wrapper/Executor.mjs',
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/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;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 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,R=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},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},O=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},z=function(e){s&&d.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){O(),z("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){O(),z("addPostprocess()."),r.push(e)},this.addNamespaceRoot=function(e,t,r){O(),z(`addNamespaceRoot('${e}').`),o.push({prefix:e,target:t,defaultExt:r})},this.enableTestMode=function(){O(),z("enableTestMode()."),i=!0},this.enableLogging=function(){O(),s||(s=!0,d=new b,"function"==typeof a.setLogger&&a.setLogger(d),d.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(O(),z(`register('${e}').`),!0!==i)throw new Error("Container test mode is disabled.");const r=a.parse(e);n.set(R(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:I,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;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 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,R=w,j=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},I=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},O=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},z=function(e){s&&d.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){O(),z("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){O(),z("addPostprocess()."),r.push(e)},this.addNamespaceRoot=function(e,t,r){O(),z(`addNamespaceRoot('${e}').`),o.push({prefix:e,target:t,defaultExt:r})},this.enableTestMode=function(){O(),z("enableTestMode()."),i=!0},this.enableLogging=function(){O(),s||(s=!0,d=new C,"function"==typeof a.setLogger&&a.setLogger(d),d.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(O(),z(`register('${e}').`),!0!==i)throw new Error("Container test mode is disabled.");const r=a.parse(e);n.set(R(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:j,applyPreprocess:I,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.6.2",
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",
@@ -23,8 +23,8 @@
23
23
  * @typedef {{depId: TeqFw_Di_DepId__DTO, namespace: object}} TeqFw_Di_Container_Resolve_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
29
  export default class TeqFw_Di_Container_Resolve_GraphResolver {
30
30
 
@@ -5,7 +5,7 @@
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.
@@ -5,9 +5,9 @@
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.
@@ -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_Resolve_GraphResolver|undefined} graphResolver
17
+ * @property {TeqFw_Di_Container_Lifecycle_Registry|undefined} lifecycle
18
+ * @property {TeqFw_Di_Container_Instantiate_Instantiator} instantiator
19
+ * @property {TeqFw_Di_Container_Wrapper_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_Resolve_GraphResolver from './Container/GraphResolver.mjs';
12
+ import TeqFw_Di_Container_Instantiate_Instantiator from './Container/Instantiate.mjs';
13
+ import TeqFw_Di_Container_Lifecycle_Registry from './Container/Lifecycle.mjs';
14
+ import TeqFw_Di_Container_Wrapper_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_Resolve_GraphResolver|undefined} */
50
+ let graphResolver;
51
+ /** @type {TeqFw_Di_Container_Lifecycle_Registry|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_Instantiator} */
56
+ const instantiator = new TeqFw_Di_Container_Instantiate_Instantiator();
57
+ /** @type {TeqFw_Di_Container_Wrapper_Executor} */
58
+ const wrapperExecutor = new TeqFw_Di_Container_Wrapper_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_Resolve_GraphResolver({parser, resolver, logger});
108
+ lifecycle = new TeqFw_Di_Container_Lifecycle_Registry(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/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_Instantiator = import("./src/Container/Instantiate.mjs").default;
5
+ type TeqFw_Di_Container_Lifecycle_Registry = import("./src/Container/Lifecycle.mjs").default;
6
+ type TeqFw_Di_Container_Resolve_GraphResolver = import("./src/Container/GraphResolver.mjs").default;
7
+ type TeqFw_Di_Container_Wrapper_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
- }