@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 +7 -0
- package/README.md +0 -1
- package/ai/container.md +0 -1
- package/ai/package-api.ts +4 -35
- package/dist/esm.js +1 -1
- package/dist/umd.js +1 -1
- package/package.json +1 -1
- package/src/Container/{Resolve/GraphResolver.mjs → GraphResolver.mjs} +2 -2
- package/src/Container/{Instantiate/Instantiator.mjs → Instantiate.mjs} +1 -1
- package/src/Container/{Lifecycle/Registry.mjs → Lifecycle.mjs} +3 -3
- package/src/Container/Pipeline.mjs +136 -0
- package/src/Container.mjs +187 -18
- package/types.d.ts +4 -4
- package/src/Container/Runtime.mjs +0 -244
- /package/src/Container/{Wrapper/Executor.mjs → Executor.mjs} +0 -0
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
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
|
|
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
|
|
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/
|
|
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/
|
|
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
|
@@ -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 '
|
|
27
|
-
import {readDepsDecl} from '
|
|
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 '
|
|
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 '
|
|
9
|
-
import TeqFw_Di_Enum_Life from '
|
|
10
|
-
import {buildDependencyKey} from '
|
|
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
|
|
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 {
|
|
22
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
|
5
|
-
type TeqFw_Di_Container_Lifecycle_Registry = import("./src/Container/Lifecycle
|
|
6
|
-
type TeqFw_Di_Container_Resolve_GraphResolver = import("./src/Container/
|
|
7
|
-
type TeqFw_Di_Container_Wrapper_Executor = import("./src/Container/
|
|
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
|
-
}
|
|
File without changes
|