@teqfw/di 2.0.5 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/ai/container.md +2 -2
- package/ai/dependency-id.md +2 -2
- package/dist/esm.js +1 -1
- package/dist/umd.js +1 -1
- package/package.json +2 -2
- package/src/Container/Instantiate/Instantiator.mjs +2 -25
- package/src/Container/Resolve/GraphResolver.mjs +2 -10
- package/src/Container/Wrapper/Executor.mjs +2 -17
- package/src/Container.mjs +46 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.0 - 2026-03-16 - Agent interface alignment release
|
|
4
|
+
|
|
5
|
+
- Clarified lifecycle marker semantics in the agent interface documentation to match parser behavior (`$`, `$$`, and `$$$`).
|
|
6
|
+
- Updated container state model terminology in agent documentation to align with runtime state names.
|
|
7
|
+
- Updated package version metadata to `2.2.0`.
|
|
8
|
+
|
|
9
|
+
## 2.1.0 - 2026-03-16 - Protected proxy lifecycle handling
|
|
10
|
+
|
|
11
|
+
- Fixed container lifecycle handling for protected proxy components that reject `.then` probing or freezing.
|
|
12
|
+
- Clarified and documented the sync-only composition contract for factories and wrappers.
|
|
13
|
+
- Simplified Promise contract checks and aligned related tests with the updated runtime behavior.
|
|
14
|
+
- Updated package version metadata to `2.1.0`.
|
|
15
|
+
|
|
3
16
|
## 2.0.5 - 2026-03-13 - Convention alignment release
|
|
4
17
|
|
|
5
18
|
- Fixed the `Container.get` JSDoc return typing.
|
package/ai/container.md
CHANGED
|
@@ -53,8 +53,8 @@ The exact semantics of dependency identifiers are defined in **dependency-id.md*
|
|
|
53
53
|
|
|
54
54
|
The container operates in three states:
|
|
55
55
|
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
56
|
+
- **notConfigured** — the container is being configured and dependencies may be registered.
|
|
57
|
+
- **operational** — the container resolves dependencies and produces linked objects.
|
|
58
58
|
- **failed** — the container has encountered an unrecoverable error.
|
|
59
59
|
|
|
60
60
|
If an error occurs during dependency resolution or any pipeline stage, the container transitions to the **failed** state. Once the container enters this state, all subsequent dependency requests are rejected.
|
package/ai/dependency-id.md
CHANGED
|
@@ -84,8 +84,8 @@ Lifecycle markers define how the container instantiates and returns objects.
|
|
|
84
84
|
The following markers are supported:
|
|
85
85
|
|
|
86
86
|
- **`$`** — singleton lifecycle; the container creates the object once and returns the same instance for subsequent requests.
|
|
87
|
-
- **`$$`** —
|
|
88
|
-
- **`$$$`** — transient lifecycle; the
|
|
87
|
+
- **`$$`** — transient lifecycle; a new instance is created for each request.
|
|
88
|
+
- **`$$$`** — transient lifecycle alias; behaves the same as `$$`.
|
|
89
89
|
|
|
90
90
|
Lifecycle markers are appended at the end of the identifier.
|
|
91
91
|
|
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 o=Object.freeze({TEQ:"teq",NODE:"node",NPM:"npm"});const r=o.TEQ,n=e.AS_IS,i=new Set(Object.values(o)),s=new Set(Object.values(e)),a=new Set(Object.values(t));let l=class{moduleName;platform;exportName;composition;life;wrappers;origin},c=class{create(e){const t=e&&"object"==typeof e?e:{},o=new l;o.moduleName="string"==typeof t.moduleName?t.moduleName:"";const c="string"==typeof t.platform?t.platform:void 0;o.platform=c&&i.has(c)?c:r;let p=null;null===t.exportName?p=null:"string"==typeof t.exportName&&(p=t.exportName),o.exportName=p;const f="string"==typeof t.composition?t.composition:void 0;o.composition=f&&s.has(f)?f:n;const u="string"==typeof t.life?t.life:void 0;return o.life=u&&a.has(u)?u:null,o.wrappers=Array.isArray(t.wrappers)?t.wrappers.filter(e=>"string"==typeof e):[],o.origin="string"==typeof t.origin?t.origin:"",Object.freeze(o.wrappers),Object.freeze(o)}};class p{constructor(){const r=new c;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(!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(i))throw new Error("CDC must satisfy AsciiCdcIdentifier.");const s=i;let a=i,l=o.TEQ;if(a.startsWith("node_"))l=o.NODE,a=a.slice(5);else if(a.startsWith("npm_"))l=o.NPM,a=a.slice(4);else if(a.startsWith("teq_"))throw new Error("Explicit teq_ prefix is forbidden.");if(0===a.length)throw new Error("moduleName must be non-empty.");const c=a.match(/(\${1,3})(?:_([A-Za-z0-9]+(?:_[A-Za-z0-9]+)*))?$/);let p=null,f=[],u=a;if(c){const e=c[1],o=c[2];if("$"===e)p=t.SINGLETON;else{if("$$"!==e&&"$$$"!==e)throw new Error("Lifecycle marker overflow.");p=t.TRANSIENT}if(u=a.slice(0,c.index),o){f=o.split("_");for(const e of f){if(!e)throw new Error("Wrapper must be non-empty.");if(e.includes("$"))throw new Error("Wrapper must not contain $.");if(e.includes("_"))throw new Error("Wrapper must not contain _.")}}}else{if(a.includes("$"))throw new Error("Invalid lifecycle marker.");if(a.match(/(?:^|[^_])_([a-z][A-Za-z0-9]*)$/))throw new Error("Wrapper without lifecycle is forbidden.")}if(u.includes("$$$$"))throw new Error("Lifecycle marker overflow.");const d=u.indexOf("__"),m=u.lastIndexOf("__");if(-1!==d&&d!==m)throw new Error("Export delimiter must appear at most once.");if(u.startsWith("__")||u.endsWith("__"))throw new Error("Malformed export segment.");let g=u,w=null;if(-1!==d){if(g=u.slice(0,d),w=u.slice(d+2),!w)throw new Error("Export must be non-empty.");if(w.includes("_"))throw new Error("Export must not contain _.");if(w.includes("$"))throw new Error("Export must not contain $.")}if(!g)throw new Error("moduleName must be non-empty.");if(g.startsWith("_")||g.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(g.includes("__"))throw new Error("moduleName must not contain __.");if(g.includes("$"))throw new Error("moduleName must not contain $.");let h=e.AS_IS;null!==w?h=e.FACTORY:p===t.SINGLETON?(w="default",h=e.FACTORY):p===t.TRANSIENT&&(h=e.FACTORY,null===w&&(w="default"));const $=r.create({moduleName:g,platform:l,exportName:w,composition:h,life:p,wrappers:f,origin:s});return n&&n.log(`Parser.parse: produced='${$.platform}::${$.moduleName}'.`),$},this.setLogger=function(e){n=e}}}let f=class{prefix;target;defaultExt},u=class{create(e){const t=e&&"object"==typeof e?e:{},o=new f;return o.prefix="string"==typeof t.prefix?t.prefix:void 0,o.target="string"==typeof t.target?t.target:void 0,o.defaultExt="string"==typeof t.defaultExt?t.defaultExt:void 0,Object.freeze(o)}};class d{namespaces;nodeModulesRoot}class m{constructor(){const e=new u;this.create=function(t){const o=t&&"object"==typeof t?t:{},r=new d,n=Array.isArray(o.namespaces)?o.namespaces:[];return r.namespaces=n.map(t=>e.create(t)),r.nodeModulesRoot="string"==typeof o.nodeModulesRoot?o.nodeModulesRoot:void 0,Object.freeze(r.namespaces),Object.freeze(r)}}}class g{constructor({config:e,importFn:t=e=>import(e),logger:o=null}){const r=new Map,n=e;let i;const s=t,a=o,l=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 o=function(e){let t=null,o=-1;const r=i.namespaces;for(const n of r){const r=e.startsWith(n.prefix);a&&a.log(`Resolver.namespace: prefix='${n.prefix}' match=${String(r)} module='${e}'.`),r&&n.prefix.length>o&&(t=n,o=n.prefix.length)}if(!t)throw new Error(`Namespace rule is not found for '${e}'.`);return t}(t),r=t.slice(o.prefix.length).split("_").join("/"),n=(s=r,(l=o.defaultExt)?s.endsWith(l)?s:`${s}${l}`:s);var s,l;const c=function(e,t){return e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}(o.target,n);return a&&a.log(`Resolver.specifier: module='${t}' -> '${c}'.`),c};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 o=e.platform,c=e.moduleName,p=`${o}::${c}`;if(r.has(p))return a&&a.log(`Resolver.cache: hit key='${p}'.`),r.get(p);a&&a.log(`Resolver.cache: miss key='${p}'.`);const f=(async()=>{const e=l(o,c);return a&&a.log(`Resolver.import: '${e}'.`),s(e)})();r.set(p,f);try{return await f}catch(e){throw r.delete(p),a&&a.error(`Resolver.cache: evict key='${p}' after failure.`,e),e}}}}class w{constructor({parser:e,resolver:t,logger:o=null}){const r=o,n=async function(o,i,s,a){const l=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")}(o);if(s.has(l)){const e=[...a,l].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const c=function(e){return`${e.platform}::${e.moduleName}`}(o);if(!i.has(c)){s.add(l),a.push(l);try{const l=await t.resolve(o);r&&r.log(`GraphResolver.walk: resolved '${c}'.`),i.set(c,{depId:o,namespace:l});const p=Reflect.get(l,"__deps__");if(void 0===p)return;const f=p;for(const[,t]of Object.entries(f)){const o=t,l=e.parse(o);r&&r.log(`GraphResolver.walk: edge '${c}' -> '${l.platform}::${l.moduleName}'.`),await n(l,i,s,a)}}finally{a.pop(),s.delete(l)}}};this.resolve=async function(e){const t=new Map,o=new Set;return await n(e,t,o,[]),t}}}class h{constructor(){this.instantiate=function(t,o,r){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,o);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(r)}else{t=e(r)}if(function(e){if(null==e)return!1;const t=typeof e;return("object"===t||"function"===t)&&"function"==typeof e.then}(t))throw new Error("Factory composition must return synchronously (non-thenable).");return t}throw new Error(`Unsupported composition mode: ${String(t.composition)}.`)}}}class ${constructor(o=null){const r=new Map,n=o;this.apply=function(o,i){if(o.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${o.composition}' cache=skip.`),i();if(o.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),i();if(o.life===t.SINGLETON){const e=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")}(o);if(r.has(e))return n&&n.log(`Lifecycle.cache: hit key='${e}'.`),r.get(e);n&&n.log(`Lifecycle.cache: miss key='${e}', create.`);const t=i();return r.set(e,t),n&&n.log(`Lifecycle.cache: stored key='${e}'.`),t}return n&&n.log("Lifecycle.apply: no lifecycle marker cache=skip."),i()}}}class y{constructor(){const e=function(e){if(null==e)return!1;const t=typeof e;if("object"!==t&&"function"!==t)return!1;return"function"==typeof e.then},t=function(e){return"function"==typeof e};this.execute=function(o,r,n){let i=r;const s=o.wrappers;for(const o of s){if(!(o in n))throw new Error(`Wrapper '${o}' is not found in module namespace.`);const r=n[o];if(!t(r))throw new Error(`Wrapper '${o}' must be callable.`);if(i=r(i),e(i))throw new Error(`Wrapper '${o}' must return synchronously (non-thenable).`)}return i}}}class N{constructor(e="teqfw/di"){const t=`[${e}]`;this.log=function(e){console.debug(`${t} ${e}`)},this.error=function(e,o){console.error(`${t} ${e}`),o instanceof Error?console.error(o.stack??o.message):void 0!==o&&console.error(o)}}}const E=Object.freeze({log(){},error(){}});class x{constructor(){let e="notConfigured";const t=[],o=[],r=[],n=new Map;let i=!1,s=!1,a=new p;const l=new m;let c,f,u,d=E;const x=new h,C=new y,b=function(e){return`${e.platform}::${e.moduleName}`},v=function(e){const t=null===e.exportName?"":e.exportName,o=null===e.life?"":e.life,r=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,t,e.composition,o,r].join("::")},_=function(e){if(null==e)return e;const t=typeof e;return"object"!==t&&"function"!==t||"[object Module]"===Object.prototype.toString.call(e)||Object.isFrozen(e)||Object.freeze(e),e},I=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},j=function(e){s&&d.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){I(),j("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){I(),j("addPostprocess()."),o.push(e)},this.setParser=function(e){I(),a=e,"function"==typeof a.setLogger&&a.setLogger(s?d:null),j("setParser().")},this.addNamespaceRoot=function(e,t,o){I(),j(`addNamespaceRoot('${e}').`),r.push({prefix:e,target:t,defaultExt:o})},this.enableTestMode=function(){I(),j("enableTestMode()."),i=!0},this.enableLogging=function(){I(),s||(s=!0,d=new N,"function"==typeof a.setLogger&&a.setLogger(d),d.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(I(),j(`register('${e}').`),!0!==i)throw new Error("Container test mode is disabled.");const o=a.parse(e);n.set(v(o),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=l.create({namespaces:r});"function"==typeof a.setLogger&&a.setLogger(d),c=new g({config:t,logger:d}),f=new w({parser:a,resolver:c,logger:d}),u=new $(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 o=e;for(const e of t)o=e(o);return o}(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=v(h);if(n.has(e)){d.log(`Container.pipeline: mock-lookup:hit '${e}'.`),p="freeze",d.log("Container.pipeline: freeze:entry.");const t=_(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 y=await f.resolve(h);d.log(`Container.pipeline: resolve:exit nodes=${y.size}.`);const N=new Map,E=function(e){if(N.has(e))return N.get(e);if(!y.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=y.get(e);p="lifecycle",d.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const r=u.apply(t.depId,function(){p="instantiate",d.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},r=function(e){const t=Reflect.get(e,"__deps__");return void 0===t?{}:t}(t.namespace);for(const[t,o]of Object.entries(r)){const r=o,n=a.parse(r);e[t]=E(b(n))}const n=x.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 o)t=e(t);return t}(n);d.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const s=C.execute(t.depId,i,t.namespace);p="freeze",d.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const l=_(s);return d.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),l});return d.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),N.set(e,r),r},I=E(b(h));return d.log("Container.pipeline: return:success."),I}catch(t){throw d.error(`Container.pipeline: failed at stage='${p}'.`,t),d.log("Container.transition: operational -> failed."),e="failed",t}}}}export{x as default};
|
|
1
|
+
var e=Object.freeze({AS_IS:"A",FACTORY:"F"});var t=Object.freeze({SINGLETON:"S",TRANSIENT:"T"});var o=Object.freeze({TEQ:"teq",NODE:"node",NPM:"npm"});const r=o.TEQ,n=e.AS_IS,i=new Set(Object.values(o)),s=new Set(Object.values(e)),a=new Set(Object.values(t));let l=class{moduleName;platform;exportName;composition;life;wrappers;origin},c=class{create(e){const t=e&&"object"==typeof e?e:{},o=new l;o.moduleName="string"==typeof t.moduleName?t.moduleName:"";const c="string"==typeof t.platform?t.platform:void 0;o.platform=c&&i.has(c)?c:r;let p=null;null===t.exportName?p=null:"string"==typeof t.exportName&&(p=t.exportName),o.exportName=p;const f="string"==typeof t.composition?t.composition:void 0;o.composition=f&&s.has(f)?f:n;const u="string"==typeof t.life?t.life:void 0;return o.life=u&&a.has(u)?u:null,o.wrappers=Array.isArray(t.wrappers)?t.wrappers.filter(e=>"string"==typeof e):[],o.origin="string"==typeof t.origin?t.origin:"",Object.freeze(o.wrappers),Object.freeze(o)}};class p{constructor(){const r=new c;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(!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(i))throw new Error("CDC must satisfy AsciiCdcIdentifier.");const s=i;let a=i,l=o.TEQ;if(a.startsWith("node_"))l=o.NODE,a=a.slice(5);else if(a.startsWith("npm_"))l=o.NPM,a=a.slice(4);else if(a.startsWith("teq_"))throw new Error("Explicit teq_ prefix is forbidden.");if(0===a.length)throw new Error("moduleName must be non-empty.");const c=a.match(/(\${1,3})(?:_([A-Za-z0-9]+(?:_[A-Za-z0-9]+)*))?$/);let p=null,f=[],u=a;if(c){const e=c[1],o=c[2];if("$"===e)p=t.SINGLETON;else{if("$$"!==e&&"$$$"!==e)throw new Error("Lifecycle marker overflow.");p=t.TRANSIENT}if(u=a.slice(0,c.index),o){f=o.split("_");for(const e of f){if(!e)throw new Error("Wrapper must be non-empty.");if(e.includes("$"))throw new Error("Wrapper must not contain $.");if(e.includes("_"))throw new Error("Wrapper must not contain _.")}}}else{if(a.includes("$"))throw new Error("Invalid lifecycle marker.");if(a.match(/(?:^|[^_])_([a-z][A-Za-z0-9]*)$/))throw new Error("Wrapper without lifecycle is forbidden.")}if(u.includes("$$$$"))throw new Error("Lifecycle marker overflow.");const d=u.indexOf("__"),m=u.lastIndexOf("__");if(-1!==d&&d!==m)throw new Error("Export delimiter must appear at most once.");if(u.startsWith("__")||u.endsWith("__"))throw new Error("Malformed export segment.");let g=u,w=null;if(-1!==d){if(g=u.slice(0,d),w=u.slice(d+2),!w)throw new Error("Export must be non-empty.");if(w.includes("_"))throw new Error("Export must not contain _.");if(w.includes("$"))throw new Error("Export must not contain $.")}if(!g)throw new Error("moduleName must be non-empty.");if(g.startsWith("_")||g.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(g.includes("__"))throw new Error("moduleName must not contain __.");if(g.includes("$"))throw new Error("moduleName must not contain $.");let h=e.AS_IS;null!==w?h=e.FACTORY:p===t.SINGLETON?(w="default",h=e.FACTORY):p===t.TRANSIENT&&(h=e.FACTORY,null===w&&(w="default"));const $=r.create({moduleName:g,platform:l,exportName:w,composition:h,life:p,wrappers:f,origin:s});return n&&n.log(`Parser.parse: produced='${$.platform}::${$.moduleName}'.`),$},this.setLogger=function(e){n=e}}}let f=class{prefix;target;defaultExt},u=class{create(e){const t=e&&"object"==typeof e?e:{},o=new f;return o.prefix="string"==typeof t.prefix?t.prefix:void 0,o.target="string"==typeof t.target?t.target:void 0,o.defaultExt="string"==typeof t.defaultExt?t.defaultExt:void 0,Object.freeze(o)}};class d{namespaces;nodeModulesRoot}class m{constructor(){const e=new u;this.create=function(t){const o=t&&"object"==typeof t?t:{},r=new d,n=Array.isArray(o.namespaces)?o.namespaces:[];return r.namespaces=n.map(t=>e.create(t)),r.nodeModulesRoot="string"==typeof o.nodeModulesRoot?o.nodeModulesRoot:void 0,Object.freeze(r.namespaces),Object.freeze(r)}}}class g{constructor({config:e,importFn:t=e=>import(e),logger:o=null}){const r=new Map,n=e;let i;const s=t,a=o,l=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 o=function(e){let t=null,o=-1;const r=i.namespaces;for(const n of r){const r=e.startsWith(n.prefix);a&&a.log(`Resolver.namespace: prefix='${n.prefix}' match=${String(r)} module='${e}'.`),r&&n.prefix.length>o&&(t=n,o=n.prefix.length)}if(!t)throw new Error(`Namespace rule is not found for '${e}'.`);return t}(t),r=t.slice(o.prefix.length).split("_").join("/"),n=(s=r,(l=o.defaultExt)?s.endsWith(l)?s:`${s}${l}`:s);var s,l;const c=function(e,t){return e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}(o.target,n);return a&&a.log(`Resolver.specifier: module='${t}' -> '${c}'.`),c};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 o=e.platform,c=e.moduleName,p=`${o}::${c}`;if(r.has(p))return a&&a.log(`Resolver.cache: hit key='${p}'.`),r.get(p);a&&a.log(`Resolver.cache: miss key='${p}'.`);const f=(async()=>{const e=l(o,c);return a&&a.log(`Resolver.import: '${e}'.`),s(e)})();r.set(p,f);try{return await f}catch(e){throw r.delete(p),a&&a.error(`Resolver.cache: evict key='${p}' after failure.`,e),e}}}}class w{constructor({parser:e,resolver:t,logger:o=null}){const r=o,n=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")},i=async function(o,s,a,l){const c=n(o);if(a.has(c)){const e=[...l,c].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const p=n(o);if(!s.has(p)){a.add(c),l.push(c);try{const n=await t.resolve(o);r&&r.log(`GraphResolver.walk: resolved '${p}'.`),s.set(p,{depId:o,namespace:n});const c=Reflect.get(n,"__deps__");if(void 0===c)return;const f=c;for(const[,t]of Object.entries(f)){const o=t,n=e.parse(o);r&&r.log(`GraphResolver.walk: edge '${p}' -> '${n.platform}::${n.moduleName}'.`),await i(n,s,a,l)}}finally{l.pop(),a.delete(c)}}};this.resolve=async function(e){const t=new Map,o=new Set;return await i(e,t,o,[]),t}}}class h{constructor(){this.instantiate=function(t,o,r){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,o);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(r)}else{t=e(r)}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 ${constructor(o=null){const r=new Map,n=o;this.apply=function(o,i){if(o.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${o.composition}' cache=skip.`),i();if(o.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),i();if(o.life===t.SINGLETON){const e=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")}(o);if(r.has(e))return n&&n.log(`Lifecycle.cache: hit key='${e}'.`),r.get(e);n&&n.log(`Lifecycle.cache: miss key='${e}', create.`);const t=i();return r.set(e,t),n&&n.log(`Lifecycle.cache: stored key='${e}'.`),t}return n&&n.log("Lifecycle.apply: no lifecycle marker cache=skip."),i()}}}class y{constructor(){const e=function(e){return"function"==typeof e};this.execute=function(t,o,r){let n=o;const i=t.wrappers;for(const t of i){if(!(t in r))throw new Error(`Wrapper '${t}' is not found in module namespace.`);const o=r[t];if(!e(o))throw new Error(`Wrapper '${t}' must be callable.`);if(n=o(n),n instanceof Promise)throw new Error(`Wrapper '${t}' must return synchronously (non-Promise).`)}return n}}}class N{constructor(e="teqfw/di"){const t=`[${e}]`;this.log=function(e){console.debug(`${t} ${e}`)},this.error=function(e,o){console.error(`${t} ${e}`),o instanceof Error?console.error(o.stack??o.message):void 0!==o&&console.error(o)}}}const E=Object.freeze({log(){},error(){}});class x{constructor(){let e="notConfigured";const t=[],o=[],r=[],n=new Map,i=new WeakMap;let s=!1,a=!1,l=new p;const c=new m;let f,u,d,x=E;const C=new h,v=new y,b=function(e){const t=null===e.exportName?"":e.exportName,o=null===e.life?"":e.life,r=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,t,e.composition,o,r].join("::")},_=function(e){return b(e)},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){x.log(`Container.freeze: skipped (${String(e)}).`)}return e},R=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;const o=e;if(i.has(o))return i.get(o);try{return Reflect.get(o,"then"),e}catch{const e=new Proxy(o,{get(e,t,o){if("then"!==t)return Reflect.get(e,t,o)}});return i.set(o,e),e}},j=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},A=function(e){a&&x.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){j(),A("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){j(),A("addPostprocess()."),o.push(e)},this.setParser=function(e){j(),l=e,"function"==typeof l.setLogger&&l.setLogger(a?x:null),A("setParser().")},this.addNamespaceRoot=function(e,t,o){j(),A(`addNamespaceRoot('${e}').`),r.push({prefix:e,target:t,defaultExt:o})},this.enableTestMode=function(){j(),A("enableTestMode()."),s=!0},this.enableLogging=function(){j(),a||(a=!0,x=new N,"function"==typeof l.setLogger&&l.setLogger(x),x.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(j(),A(`register('${e}').`),!0!==s)throw new Error("Container test mode is disabled.");const o=l.parse(e);n.set(_(o),t)},this.get=async function(i){if("failed"===e)throw x.error(`Container.get: rejected in failed state cdc='${i}'.`),new Error("Container is in failed state.");let a="start";try{x.log(`Container.get: cdc='${i}'.`),function(){if("notConfigured"!==e)return;x.log("Container.transition: notConfigured -> operational."),e="operational";const t=c.create({namespaces:r});"function"==typeof l.setLogger&&l.setLogger(x),f=new g({config:t,logger:x}),u=new w({parser:l,resolver:f,logger:x}),d=new $(x)}(),x.log(`Container.state: '${e}'.`),a="parse",x.log("Container.pipeline: parse:entry.");const p=l.parse(i);x.log(`Container.pipeline: parse:exit '${p.platform}::${p.moduleName}'.`),a="preprocess",x.log("Container.pipeline: preprocess:entry.");const m=function(e){let o=e;for(const e of t)o=e(o);return o}(p);if(x.log(`Container.pipeline: preprocess:exit '${m.platform}::${m.moduleName}'.`),!0===s){a="mock",x.log("Container.pipeline: mock-lookup:entry.");const e=_(m);if(n.has(e)){x.log(`Container.pipeline: mock-lookup:hit '${e}'.`),a="freeze",x.log("Container.pipeline: freeze:entry.");const t=I(n.get(e));return x.log("Container.pipeline: freeze:exit."),x.log("Container.pipeline: return:success."),R(t)}x.log(`Container.pipeline: mock-lookup:miss '${e}'.`)}else x.log("Container.pipeline: mock-lookup:disabled.");a="resolve",x.log("Container.pipeline: resolve:entry.");const h=await u.resolve(m);x.log(`Container.pipeline: resolve:exit nodes=${h.size}.`);const y=new Map,N=function(e){if(y.has(e))return y.get(e);if(!h.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=h.get(e);a="lifecycle",x.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const r=d.apply(t.depId,function(){a="instantiate",x.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},r=function(e){const t=Reflect.get(e,"__deps__");return void 0===t?{}:t}(t.namespace);for(const[t,o]of Object.entries(r)){const r=o,n=l.parse(r);e[t]=N(b(n))}const n=C.instantiate(t.depId,t.namespace,e);x.log(`Container.pipeline: instantiate:exit '${t.depId.platform}::${t.depId.moduleName}'.`),a="postprocess",x.log(`Container.pipeline: postprocess:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const i=function(e){let t=e;for(const e of o)t=e(t);return t}(n);x.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const s=v.execute(t.depId,i,t.namespace);a="freeze",x.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const c=I(s);return x.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),c});return x.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),y.set(e,r),r},E=N(b(m));return x.log("Container.pipeline: return:success."),R(E)}catch(t){throw x.error(`Container.pipeline: failed at stage='${a}'.`,t),x.log("Container.transition: operational -> failed."),e="failed",t}}}}export{x 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 o=Object.freeze({TEQ:"teq",NODE:"node",NPM:"npm"});const r=o.TEQ,n=e.AS_IS,i=new Set(Object.values(o)),s=new Set(Object.values(e)),a=new Set(Object.values(t));let l=class{moduleName;platform;exportName;composition;life;wrappers;origin},c=class{create(e){const t=e&&"object"==typeof e?e:{},o=new l;o.moduleName="string"==typeof t.moduleName?t.moduleName:"";const c="string"==typeof t.platform?t.platform:void 0;o.platform=c&&i.has(c)?c:r;let p=null;null===t.exportName?p=null:"string"==typeof t.exportName&&(p=t.exportName),o.exportName=p;const f="string"==typeof t.composition?t.composition:void 0;o.composition=f&&s.has(f)?f:n;const u="string"==typeof t.life?t.life:void 0;return o.life=u&&a.has(u)?u:null,o.wrappers=Array.isArray(t.wrappers)?t.wrappers.filter(e=>"string"==typeof e):[],o.origin="string"==typeof t.origin?t.origin:"",Object.freeze(o.wrappers),Object.freeze(o)}};class p{constructor(){const r=new c;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(!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(i))throw new Error("CDC must satisfy AsciiCdcIdentifier.");const s=i;let a=i,l=o.TEQ;if(a.startsWith("node_"))l=o.NODE,a=a.slice(5);else if(a.startsWith("npm_"))l=o.NPM,a=a.slice(4);else if(a.startsWith("teq_"))throw new Error("Explicit teq_ prefix is forbidden.");if(0===a.length)throw new Error("moduleName must be non-empty.");const c=a.match(/(\${1,3})(?:_([A-Za-z0-9]+(?:_[A-Za-z0-9]+)*))?$/);let p=null,f=[],u=a;if(c){const e=c[1],o=c[2];if("$"===e)p=t.SINGLETON;else{if("$$"!==e&&"$$$"!==e)throw new Error("Lifecycle marker overflow.");p=t.TRANSIENT}if(u=a.slice(0,c.index),o){f=o.split("_");for(const e of f){if(!e)throw new Error("Wrapper must be non-empty.");if(e.includes("$"))throw new Error("Wrapper must not contain $.");if(e.includes("_"))throw new Error("Wrapper must not contain _.")}}}else{if(a.includes("$"))throw new Error("Invalid lifecycle marker.");if(a.match(/(?:^|[^_])_([a-z][A-Za-z0-9]*)$/))throw new Error("Wrapper without lifecycle is forbidden.")}if(u.includes("$$$$"))throw new Error("Lifecycle marker overflow.");const d=u.indexOf("__"),m=u.lastIndexOf("__");if(-1!==d&&d!==m)throw new Error("Export delimiter must appear at most once.");if(u.startsWith("__")||u.endsWith("__"))throw new Error("Malformed export segment.");let g=u,w=null;if(-1!==d){if(g=u.slice(0,d),w=u.slice(d+2),!w)throw new Error("Export must be non-empty.");if(w.includes("_"))throw new Error("Export must not contain _.");if(w.includes("$"))throw new Error("Export must not contain $.")}if(!g)throw new Error("moduleName must be non-empty.");if(g.startsWith("_")||g.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(g.includes("__"))throw new Error("moduleName must not contain __.");if(g.includes("$"))throw new Error("moduleName must not contain $.");let h=e.AS_IS;null!==w?h=e.FACTORY:p===t.SINGLETON?(w="default",h=e.FACTORY):p===t.TRANSIENT&&(h=e.FACTORY,null===w&&(w="default"));const $=r.create({moduleName:g,platform:l,exportName:w,composition:h,life:p,wrappers:f,origin:s});return n&&n.log(`Parser.parse: produced='${$.platform}::${$.moduleName}'.`),$},this.setLogger=function(e){n=e}}}let f=class{prefix;target;defaultExt},u=class{create(e){const t=e&&"object"==typeof e?e:{},o=new f;return o.prefix="string"==typeof t.prefix?t.prefix:void 0,o.target="string"==typeof t.target?t.target:void 0,o.defaultExt="string"==typeof t.defaultExt?t.defaultExt:void 0,Object.freeze(o)}};class d{namespaces;nodeModulesRoot}class m{constructor(){const e=new u;this.create=function(t){const o=t&&"object"==typeof t?t:{},r=new d,n=Array.isArray(o.namespaces)?o.namespaces:[];return r.namespaces=n.map(t=>e.create(t)),r.nodeModulesRoot="string"==typeof o.nodeModulesRoot?o.nodeModulesRoot:void 0,Object.freeze(r.namespaces),Object.freeze(r)}}}class g{constructor({config:e,importFn:t=e=>import(e),logger:o=null}){const r=new Map,n=e;let i;const s=t,a=o,l=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 o=function(e){let t=null,o=-1;const r=i.namespaces;for(const n of r){const r=e.startsWith(n.prefix);a&&a.log(`Resolver.namespace: prefix='${n.prefix}' match=${String(r)} module='${e}'.`),r&&n.prefix.length>o&&(t=n,o=n.prefix.length)}if(!t)throw new Error(`Namespace rule is not found for '${e}'.`);return t}(t),r=t.slice(o.prefix.length).split("_").join("/"),n=(s=r,(l=o.defaultExt)?s.endsWith(l)?s:`${s}${l}`:s);var s,l;const c=function(e,t){return e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}(o.target,n);return a&&a.log(`Resolver.specifier: module='${t}' -> '${c}'.`),c};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 o=e.platform,c=e.moduleName,p=`${o}::${c}`;if(r.has(p))return a&&a.log(`Resolver.cache: hit key='${p}'.`),r.get(p);a&&a.log(`Resolver.cache: miss key='${p}'.`);const f=(async()=>{const e=l(o,c);return a&&a.log(`Resolver.import: '${e}'.`),s(e)})();r.set(p,f);try{return await f}catch(e){throw r.delete(p),a&&a.error(`Resolver.cache: evict key='${p}' after failure.`,e),e}}}}class w{constructor({parser:e,resolver:t,logger:o=null}){const r=o,n=async function(o,i,s,a){const l=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")}(o);if(s.has(l)){const e=[...a,l].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const c=function(e){return`${e.platform}::${e.moduleName}`}(o);if(!i.has(c)){s.add(l),a.push(l);try{const l=await t.resolve(o);r&&r.log(`GraphResolver.walk: resolved '${c}'.`),i.set(c,{depId:o,namespace:l});const p=Reflect.get(l,"__deps__");if(void 0===p)return;const f=p;for(const[,t]of Object.entries(f)){const o=t,l=e.parse(o);r&&r.log(`GraphResolver.walk: edge '${c}' -> '${l.platform}::${l.moduleName}'.`),await n(l,i,s,a)}}finally{a.pop(),s.delete(l)}}};this.resolve=async function(e){const t=new Map,o=new Set;return await n(e,t,o,[]),t}}}class h{constructor(){this.instantiate=function(t,o,r){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,o);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(r)}else{t=e(r)}if(function(e){if(null==e)return!1;const t=typeof e;return("object"===t||"function"===t)&&"function"==typeof e.then}(t))throw new Error("Factory composition must return synchronously (non-thenable).");return t}throw new Error(`Unsupported composition mode: ${String(t.composition)}.`)}}}class ${constructor(o=null){const r=new Map,n=o;this.apply=function(o,i){if(o.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${o.composition}' cache=skip.`),i();if(o.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),i();if(o.life===t.SINGLETON){const e=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")}(o);if(r.has(e))return n&&n.log(`Lifecycle.cache: hit key='${e}'.`),r.get(e);n&&n.log(`Lifecycle.cache: miss key='${e}', create.`);const t=i();return r.set(e,t),n&&n.log(`Lifecycle.cache: stored key='${e}'.`),t}return n&&n.log("Lifecycle.apply: no lifecycle marker cache=skip."),i()}}}class y{constructor(){const e=function(e){if(null==e)return!1;const t=typeof e;if("object"!==t&&"function"!==t)return!1;return"function"==typeof e.then},t=function(e){return"function"==typeof e};this.execute=function(o,r,n){let i=r;const s=o.wrappers;for(const o of s){if(!(o in n))throw new Error(`Wrapper '${o}' is not found in module namespace.`);const r=n[o];if(!t(r))throw new Error(`Wrapper '${o}' must be callable.`);if(i=r(i),e(i))throw new Error(`Wrapper '${o}' must return synchronously (non-thenable).`)}return i}}}class N{constructor(e="teqfw/di"){const t=`[${e}]`;this.log=function(e){console.debug(`${t} ${e}`)},this.error=function(e,o){console.error(`${t} ${e}`),o instanceof Error?console.error(o.stack??o.message):void 0!==o&&console.error(o)}}}const E=Object.freeze({log(){},error(){}});return class{constructor(){let e="notConfigured";const t=[],o=[],r=[],n=new Map;let i=!1,s=!1,a=new p;const l=new m;let c,f,u,d=E;const x=new h,C=new y,b=function(e){return`${e.platform}::${e.moduleName}`},v=function(e){const t=null===e.exportName?"":e.exportName,o=null===e.life?"":e.life,r=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,t,e.composition,o,r].join("::")},_=function(e){if(null==e)return e;const t=typeof e;return"object"!==t&&"function"!==t||"[object Module]"===Object.prototype.toString.call(e)||Object.isFrozen(e)||Object.freeze(e),e},I=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},j=function(e){s&&d.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){I(),j("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){I(),j("addPostprocess()."),o.push(e)},this.setParser=function(e){I(),a=e,"function"==typeof a.setLogger&&a.setLogger(s?d:null),j("setParser().")},this.addNamespaceRoot=function(e,t,o){I(),j(`addNamespaceRoot('${e}').`),r.push({prefix:e,target:t,defaultExt:o})},this.enableTestMode=function(){I(),j("enableTestMode()."),i=!0},this.enableLogging=function(){I(),s||(s=!0,d=new N,"function"==typeof a.setLogger&&a.setLogger(d),d.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(I(),j(`register('${e}').`),!0!==i)throw new Error("Container test mode is disabled.");const o=a.parse(e);n.set(v(o),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=l.create({namespaces:r});"function"==typeof a.setLogger&&a.setLogger(d),c=new g({config:t,logger:d}),f=new w({parser:a,resolver:c,logger:d}),u=new $(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 o=e;for(const e of t)o=e(o);return o}(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=v(h);if(n.has(e)){d.log(`Container.pipeline: mock-lookup:hit '${e}'.`),p="freeze",d.log("Container.pipeline: freeze:entry.");const t=_(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 y=await f.resolve(h);d.log(`Container.pipeline: resolve:exit nodes=${y.size}.`);const N=new Map,E=function(e){if(N.has(e))return N.get(e);if(!y.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=y.get(e);p="lifecycle",d.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const r=u.apply(t.depId,function(){p="instantiate",d.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},r=function(e){const t=Reflect.get(e,"__deps__");return void 0===t?{}:t}(t.namespace);for(const[t,o]of Object.entries(r)){const r=o,n=a.parse(r);e[t]=E(b(n))}const n=x.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 o)t=e(t);return t}(n);d.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const s=C.execute(t.depId,i,t.namespace);p="freeze",d.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const l=_(s);return d.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),l});return d.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),N.set(e,r),r},I=E(b(h));return d.log("Container.pipeline: return:success."),I}catch(t){throw d.error(`Container.pipeline: failed at stage='${p}'.`,t),d.log("Container.transition: operational -> failed."),e="failed",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 o=Object.freeze({TEQ:"teq",NODE:"node",NPM:"npm"});const r=o.TEQ,n=e.AS_IS,i=new Set(Object.values(o)),s=new Set(Object.values(e)),a=new Set(Object.values(t));let l=class{moduleName;platform;exportName;composition;life;wrappers;origin},c=class{create(e){const t=e&&"object"==typeof e?e:{},o=new l;o.moduleName="string"==typeof t.moduleName?t.moduleName:"";const c="string"==typeof t.platform?t.platform:void 0;o.platform=c&&i.has(c)?c:r;let p=null;null===t.exportName?p=null:"string"==typeof t.exportName&&(p=t.exportName),o.exportName=p;const f="string"==typeof t.composition?t.composition:void 0;o.composition=f&&s.has(f)?f:n;const u="string"==typeof t.life?t.life:void 0;return o.life=u&&a.has(u)?u:null,o.wrappers=Array.isArray(t.wrappers)?t.wrappers.filter(e=>"string"==typeof e):[],o.origin="string"==typeof t.origin?t.origin:"",Object.freeze(o.wrappers),Object.freeze(o)}};class p{constructor(){const r=new c;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(!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(i))throw new Error("CDC must satisfy AsciiCdcIdentifier.");const s=i;let a=i,l=o.TEQ;if(a.startsWith("node_"))l=o.NODE,a=a.slice(5);else if(a.startsWith("npm_"))l=o.NPM,a=a.slice(4);else if(a.startsWith("teq_"))throw new Error("Explicit teq_ prefix is forbidden.");if(0===a.length)throw new Error("moduleName must be non-empty.");const c=a.match(/(\${1,3})(?:_([A-Za-z0-9]+(?:_[A-Za-z0-9]+)*))?$/);let p=null,f=[],u=a;if(c){const e=c[1],o=c[2];if("$"===e)p=t.SINGLETON;else{if("$$"!==e&&"$$$"!==e)throw new Error("Lifecycle marker overflow.");p=t.TRANSIENT}if(u=a.slice(0,c.index),o){f=o.split("_");for(const e of f){if(!e)throw new Error("Wrapper must be non-empty.");if(e.includes("$"))throw new Error("Wrapper must not contain $.");if(e.includes("_"))throw new Error("Wrapper must not contain _.")}}}else{if(a.includes("$"))throw new Error("Invalid lifecycle marker.");if(a.match(/(?:^|[^_])_([a-z][A-Za-z0-9]*)$/))throw new Error("Wrapper without lifecycle is forbidden.")}if(u.includes("$$$$"))throw new Error("Lifecycle marker overflow.");const d=u.indexOf("__"),m=u.lastIndexOf("__");if(-1!==d&&d!==m)throw new Error("Export delimiter must appear at most once.");if(u.startsWith("__")||u.endsWith("__"))throw new Error("Malformed export segment.");let g=u,w=null;if(-1!==d){if(g=u.slice(0,d),w=u.slice(d+2),!w)throw new Error("Export must be non-empty.");if(w.includes("_"))throw new Error("Export must not contain _.");if(w.includes("$"))throw new Error("Export must not contain $.")}if(!g)throw new Error("moduleName must be non-empty.");if(g.startsWith("_")||g.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(g.includes("__"))throw new Error("moduleName must not contain __.");if(g.includes("$"))throw new Error("moduleName must not contain $.");let h=e.AS_IS;null!==w?h=e.FACTORY:p===t.SINGLETON?(w="default",h=e.FACTORY):p===t.TRANSIENT&&(h=e.FACTORY,null===w&&(w="default"));const $=r.create({moduleName:g,platform:l,exportName:w,composition:h,life:p,wrappers:f,origin:s});return n&&n.log(`Parser.parse: produced='${$.platform}::${$.moduleName}'.`),$},this.setLogger=function(e){n=e}}}let f=class{prefix;target;defaultExt},u=class{create(e){const t=e&&"object"==typeof e?e:{},o=new f;return o.prefix="string"==typeof t.prefix?t.prefix:void 0,o.target="string"==typeof t.target?t.target:void 0,o.defaultExt="string"==typeof t.defaultExt?t.defaultExt:void 0,Object.freeze(o)}};class d{namespaces;nodeModulesRoot}class m{constructor(){const e=new u;this.create=function(t){const o=t&&"object"==typeof t?t:{},r=new d,n=Array.isArray(o.namespaces)?o.namespaces:[];return r.namespaces=n.map(t=>e.create(t)),r.nodeModulesRoot="string"==typeof o.nodeModulesRoot?o.nodeModulesRoot:void 0,Object.freeze(r.namespaces),Object.freeze(r)}}}class g{constructor({config:e,importFn:t=e=>import(e),logger:o=null}){const r=new Map,n=e;let i;const s=t,a=o,l=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 o=function(e){let t=null,o=-1;const r=i.namespaces;for(const n of r){const r=e.startsWith(n.prefix);a&&a.log(`Resolver.namespace: prefix='${n.prefix}' match=${String(r)} module='${e}'.`),r&&n.prefix.length>o&&(t=n,o=n.prefix.length)}if(!t)throw new Error(`Namespace rule is not found for '${e}'.`);return t}(t),r=t.slice(o.prefix.length).split("_").join("/"),n=(s=r,(l=o.defaultExt)?s.endsWith(l)?s:`${s}${l}`:s);var s,l;const c=function(e,t){return e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}(o.target,n);return a&&a.log(`Resolver.specifier: module='${t}' -> '${c}'.`),c};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 o=e.platform,c=e.moduleName,p=`${o}::${c}`;if(r.has(p))return a&&a.log(`Resolver.cache: hit key='${p}'.`),r.get(p);a&&a.log(`Resolver.cache: miss key='${p}'.`);const f=(async()=>{const e=l(o,c);return a&&a.log(`Resolver.import: '${e}'.`),s(e)})();r.set(p,f);try{return await f}catch(e){throw r.delete(p),a&&a.error(`Resolver.cache: evict key='${p}' after failure.`,e),e}}}}class w{constructor({parser:e,resolver:t,logger:o=null}){const r=o,n=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")},i=async function(o,s,a,l){const c=n(o);if(a.has(c)){const e=[...l,c].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const p=n(o);if(!s.has(p)){a.add(c),l.push(c);try{const n=await t.resolve(o);r&&r.log(`GraphResolver.walk: resolved '${p}'.`),s.set(p,{depId:o,namespace:n});const c=Reflect.get(n,"__deps__");if(void 0===c)return;const f=c;for(const[,t]of Object.entries(f)){const o=t,n=e.parse(o);r&&r.log(`GraphResolver.walk: edge '${p}' -> '${n.platform}::${n.moduleName}'.`),await i(n,s,a,l)}}finally{l.pop(),a.delete(c)}}};this.resolve=async function(e){const t=new Map,o=new Set;return await i(e,t,o,[]),t}}}class h{constructor(){this.instantiate=function(t,o,r){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,o);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(r)}else{t=e(r)}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 ${constructor(o=null){const r=new Map,n=o;this.apply=function(o,i){if(o.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${o.composition}' cache=skip.`),i();if(o.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),i();if(o.life===t.SINGLETON){const e=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")}(o);if(r.has(e))return n&&n.log(`Lifecycle.cache: hit key='${e}'.`),r.get(e);n&&n.log(`Lifecycle.cache: miss key='${e}', create.`);const t=i();return r.set(e,t),n&&n.log(`Lifecycle.cache: stored key='${e}'.`),t}return n&&n.log("Lifecycle.apply: no lifecycle marker cache=skip."),i()}}}class y{constructor(){const e=function(e){return"function"==typeof e};this.execute=function(t,o,r){let n=o;const i=t.wrappers;for(const t of i){if(!(t in r))throw new Error(`Wrapper '${t}' is not found in module namespace.`);const o=r[t];if(!e(o))throw new Error(`Wrapper '${t}' must be callable.`);if(n=o(n),n instanceof Promise)throw new Error(`Wrapper '${t}' must return synchronously (non-Promise).`)}return n}}}class N{constructor(e="teqfw/di"){const t=`[${e}]`;this.log=function(e){console.debug(`${t} ${e}`)},this.error=function(e,o){console.error(`${t} ${e}`),o instanceof Error?console.error(o.stack??o.message):void 0!==o&&console.error(o)}}}const E=Object.freeze({log(){},error(){}});return class{constructor(){let e="notConfigured";const t=[],o=[],r=[],n=new Map,i=new WeakMap;let s=!1,a=!1,l=new p;const c=new m;let f,u,d,x=E;const C=new h,b=new y,v=function(e){const t=null===e.exportName?"":e.exportName,o=null===e.life?"":e.life,r=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,t,e.composition,o,r].join("::")},_=function(e){return v(e)},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){x.log(`Container.freeze: skipped (${String(e)}).`)}return e},R=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;const o=e;if(i.has(o))return i.get(o);try{return Reflect.get(o,"then"),e}catch{const e=new Proxy(o,{get(e,t,o){if("then"!==t)return Reflect.get(e,t,o)}});return i.set(o,e),e}},j=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},A=function(e){a&&x.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){j(),A("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){j(),A("addPostprocess()."),o.push(e)},this.setParser=function(e){j(),l=e,"function"==typeof l.setLogger&&l.setLogger(a?x:null),A("setParser().")},this.addNamespaceRoot=function(e,t,o){j(),A(`addNamespaceRoot('${e}').`),r.push({prefix:e,target:t,defaultExt:o})},this.enableTestMode=function(){j(),A("enableTestMode()."),s=!0},this.enableLogging=function(){j(),a||(a=!0,x=new N,"function"==typeof l.setLogger&&l.setLogger(x),x.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(j(),A(`register('${e}').`),!0!==s)throw new Error("Container test mode is disabled.");const o=l.parse(e);n.set(_(o),t)},this.get=async function(i){if("failed"===e)throw x.error(`Container.get: rejected in failed state cdc='${i}'.`),new Error("Container is in failed state.");let a="start";try{x.log(`Container.get: cdc='${i}'.`),function(){if("notConfigured"!==e)return;x.log("Container.transition: notConfigured -> operational."),e="operational";const t=c.create({namespaces:r});"function"==typeof l.setLogger&&l.setLogger(x),f=new g({config:t,logger:x}),u=new w({parser:l,resolver:f,logger:x}),d=new $(x)}(),x.log(`Container.state: '${e}'.`),a="parse",x.log("Container.pipeline: parse:entry.");const p=l.parse(i);x.log(`Container.pipeline: parse:exit '${p.platform}::${p.moduleName}'.`),a="preprocess",x.log("Container.pipeline: preprocess:entry.");const m=function(e){let o=e;for(const e of t)o=e(o);return o}(p);if(x.log(`Container.pipeline: preprocess:exit '${m.platform}::${m.moduleName}'.`),!0===s){a="mock",x.log("Container.pipeline: mock-lookup:entry.");const e=_(m);if(n.has(e)){x.log(`Container.pipeline: mock-lookup:hit '${e}'.`),a="freeze",x.log("Container.pipeline: freeze:entry.");const t=I(n.get(e));return x.log("Container.pipeline: freeze:exit."),x.log("Container.pipeline: return:success."),R(t)}x.log(`Container.pipeline: mock-lookup:miss '${e}'.`)}else x.log("Container.pipeline: mock-lookup:disabled.");a="resolve",x.log("Container.pipeline: resolve:entry.");const h=await u.resolve(m);x.log(`Container.pipeline: resolve:exit nodes=${h.size}.`);const y=new Map,N=function(e){if(y.has(e))return y.get(e);if(!h.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=h.get(e);a="lifecycle",x.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const r=d.apply(t.depId,function(){a="instantiate",x.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},r=function(e){const t=Reflect.get(e,"__deps__");return void 0===t?{}:t}(t.namespace);for(const[t,o]of Object.entries(r)){const r=o,n=l.parse(r);e[t]=N(v(n))}const n=C.instantiate(t.depId,t.namespace,e);x.log(`Container.pipeline: instantiate:exit '${t.depId.platform}::${t.depId.moduleName}'.`),a="postprocess",x.log(`Container.pipeline: postprocess:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const i=function(e){let t=e;for(const e of o)t=e(t);return t}(n);x.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const s=b.execute(t.depId,i,t.namespace);a="freeze",x.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const c=I(s);return x.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),c});return x.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),y.set(e,r),r},E=N(v(m));return x.log("Container.pipeline: return:success."),R(E)}catch(t){throw x.error(`Container.pipeline: failed at stage='${a}'.`,t),x.log("Container.transition: operational -> failed."),e="failed",t}}}}});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teqfw/di",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Dependency Injection container for ES6 modules that works in both browser and Node.js apps.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dependency injection",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@eslint/js": "^10.0.1",
|
|
60
60
|
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
61
|
-
"@rollup/plugin-terser": "^0.
|
|
61
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
62
62
|
"eslint": "^10.0.2",
|
|
63
63
|
"rollup": "^4.59.0"
|
|
64
64
|
},
|
|
@@ -63,29 +63,6 @@ export default class TeqFw_Di_Container_Instantiate_Instantiator {
|
|
|
63
63
|
}
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
-
/**
|
|
67
|
-
* Detects Promise-like asynchronous return values.
|
|
68
|
-
*
|
|
69
|
-
* @param {unknown} value
|
|
70
|
-
* @returns {boolean}
|
|
71
|
-
*/
|
|
72
|
-
const isThenable = function (value) {
|
|
73
|
-
if ((value === null) || (value === undefined)) {
|
|
74
|
-
return false;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const type = typeof value;
|
|
78
|
-
|
|
79
|
-
if ((type !== 'object') && (type !== 'function')) {
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** @type {{ then?: unknown }} */
|
|
84
|
-
const maybeThenable = value;
|
|
85
|
-
|
|
86
|
-
return (typeof maybeThenable.then === 'function');
|
|
87
|
-
};
|
|
88
|
-
|
|
89
66
|
/**
|
|
90
67
|
* Produces a value from a resolved module namespace and dependency map.
|
|
91
68
|
*
|
|
@@ -126,9 +103,9 @@ export default class TeqFw_Di_Container_Instantiate_Instantiator {
|
|
|
126
103
|
result = Fn(resolvedDeps);
|
|
127
104
|
}
|
|
128
105
|
|
|
129
|
-
if (
|
|
106
|
+
if (result instanceof Promise) {
|
|
130
107
|
throw new Error(
|
|
131
|
-
'Factory composition must return synchronously (non-
|
|
108
|
+
'Factory composition must return synchronously (non-Promise).'
|
|
132
109
|
);
|
|
133
110
|
}
|
|
134
111
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Resolve-stage graph builder.
|
|
5
5
|
*
|
|
6
6
|
* Recursively resolves module namespaces and their declared `__deps__`
|
|
7
|
-
* into a deterministic map keyed by
|
|
7
|
+
* into a deterministic map keyed by structural DepId identity.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -32,14 +32,6 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
|
32
32
|
* @returns {string}
|
|
33
33
|
*/
|
|
34
34
|
const makeNodeKey = function (depId) {
|
|
35
|
-
return `${depId.platform}::${depId.moduleName}`;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @param {TeqFw_Di_DepId$DTO} depId
|
|
40
|
-
* @returns {string}
|
|
41
|
-
*/
|
|
42
|
-
const makeDepIdentity = function (depId) {
|
|
43
35
|
/** @type {string} */
|
|
44
36
|
const wrappers = Array.isArray(depId.wrappers) ? depId.wrappers.join('|') : '';
|
|
45
37
|
return [
|
|
@@ -61,7 +53,7 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
|
61
53
|
*/
|
|
62
54
|
const walk = async function (depId, out, stack, chain) {
|
|
63
55
|
/** @type {string} */
|
|
64
|
-
const identity =
|
|
56
|
+
const identity = makeNodeKey(depId);
|
|
65
57
|
if (stack.has(identity)) {
|
|
66
58
|
/** @type {string} */
|
|
67
59
|
const cycle = [...chain, identity].join(' -> ');
|
|
@@ -12,21 +12,6 @@ export default class TeqFw_Di_Container_Wrapper_Executor {
|
|
|
12
12
|
* Creates wrapper executor instance.
|
|
13
13
|
*/
|
|
14
14
|
constructor() {
|
|
15
|
-
/**
|
|
16
|
-
* Detects Promise-like asynchronous return values.
|
|
17
|
-
*
|
|
18
|
-
* @param {unknown} value
|
|
19
|
-
* @returns {boolean}
|
|
20
|
-
*/
|
|
21
|
-
const isThenable = function (value) {
|
|
22
|
-
if ((value === null) || (value === undefined)) return false;
|
|
23
|
-
const type = typeof value;
|
|
24
|
-
if ((type !== 'object') && (type !== 'function')) return false;
|
|
25
|
-
/** @type {{ then?: unknown }} */
|
|
26
|
-
const maybeThenable = value;
|
|
27
|
-
return (typeof maybeThenable.then === 'function');
|
|
28
|
-
};
|
|
29
|
-
|
|
30
15
|
/**
|
|
31
16
|
* Narrows unknown export to unary wrapper callable.
|
|
32
17
|
*
|
|
@@ -60,8 +45,8 @@ export default class TeqFw_Di_Container_Wrapper_Executor {
|
|
|
60
45
|
throw new Error(`Wrapper '${name}' must be callable.`);
|
|
61
46
|
}
|
|
62
47
|
current = candidate(current);
|
|
63
|
-
if (
|
|
64
|
-
throw new Error(`Wrapper '${name}' must return synchronously (non-
|
|
48
|
+
if (current instanceof Promise) {
|
|
49
|
+
throw new Error(`Wrapper '${name}' must return synchronously (non-Promise).`);
|
|
65
50
|
}
|
|
66
51
|
}
|
|
67
52
|
|
package/src/Container.mjs
CHANGED
|
@@ -38,6 +38,8 @@ export default class TeqFw_Di_Container {
|
|
|
38
38
|
const namespaceRoots = [];
|
|
39
39
|
/** @type {Map<string, unknown>} */
|
|
40
40
|
const mockRegistry = new Map();
|
|
41
|
+
/** @type {WeakMap<object, object>} */
|
|
42
|
+
const promiseSafeCache = new WeakMap();
|
|
41
43
|
let testMode = false;
|
|
42
44
|
let loggingEnabled = false;
|
|
43
45
|
|
|
@@ -63,16 +65,6 @@ export default class TeqFw_Di_Container {
|
|
|
63
65
|
* @returns {string}
|
|
64
66
|
*/
|
|
65
67
|
const getKey = function (depId) {
|
|
66
|
-
return `${depId.platform}::${depId.moduleName}`;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Canonical structural identity excluding `origin`.
|
|
71
|
-
*
|
|
72
|
-
* @param {TeqFw_Di_DepId$DTO} depId
|
|
73
|
-
* @returns {string}
|
|
74
|
-
*/
|
|
75
|
-
const getMockKey = function (depId) {
|
|
76
68
|
const exportName = depId.exportName === null ? '' : depId.exportName;
|
|
77
69
|
const life = depId.life === null ? '' : depId.life;
|
|
78
70
|
const wrappers = Array.isArray(depId.wrappers) ? depId.wrappers.join('|') : '';
|
|
@@ -86,6 +78,16 @@ export default class TeqFw_Di_Container {
|
|
|
86
78
|
].join('::');
|
|
87
79
|
};
|
|
88
80
|
|
|
81
|
+
/**
|
|
82
|
+
* Canonical structural identity excluding `origin`.
|
|
83
|
+
*
|
|
84
|
+
* @param {TeqFw_Di_DepId$DTO} depId
|
|
85
|
+
* @returns {string}
|
|
86
|
+
*/
|
|
87
|
+
const getMockKey = function (depId) {
|
|
88
|
+
return getKey(depId);
|
|
89
|
+
};
|
|
90
|
+
|
|
89
91
|
/**
|
|
90
92
|
* Freezes values that support freezing.
|
|
91
93
|
*
|
|
@@ -98,10 +100,41 @@ export default class TeqFw_Di_Container {
|
|
|
98
100
|
if ((type !== 'object') && (type !== 'function')) return value;
|
|
99
101
|
if (Object.prototype.toString.call(value) === '[object Module]') return value;
|
|
100
102
|
if (Object.isFrozen(value)) return value;
|
|
101
|
-
|
|
103
|
+
try {
|
|
104
|
+
Object.freeze(value);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
logger.log(`Container.freeze: skipped (${String(error)}).`);
|
|
107
|
+
}
|
|
102
108
|
return value;
|
|
103
109
|
};
|
|
104
110
|
|
|
111
|
+
/**
|
|
112
|
+
* Ensures async `get()` can resolve objects whose proxy `get('then')` throws.
|
|
113
|
+
*
|
|
114
|
+
* @param {unknown} value
|
|
115
|
+
* @returns {unknown}
|
|
116
|
+
*/
|
|
117
|
+
const asPromiseSafe = function (value) {
|
|
118
|
+
if ((value === null) || (value === undefined)) return value;
|
|
119
|
+
const type = typeof value;
|
|
120
|
+
if ((type !== 'object') && (type !== 'function')) return value;
|
|
121
|
+
const obj = /** @type {object} */ (value);
|
|
122
|
+
if (promiseSafeCache.has(obj)) return promiseSafeCache.get(obj);
|
|
123
|
+
try {
|
|
124
|
+
void Reflect.get(obj, 'then');
|
|
125
|
+
return value;
|
|
126
|
+
} catch {
|
|
127
|
+
const wrapped = new Proxy(obj, {
|
|
128
|
+
get(target, property, receiver) {
|
|
129
|
+
if (property === 'then') return undefined;
|
|
130
|
+
return Reflect.get(target, property, receiver);
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
promiseSafeCache.set(obj, wrapped);
|
|
134
|
+
return wrapped;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
105
138
|
/**
|
|
106
139
|
* Applies ordered preprocess pipeline.
|
|
107
140
|
*
|
|
@@ -309,7 +342,7 @@ export default class TeqFw_Di_Container {
|
|
|
309
342
|
const frozenMock = freeze(mockRegistry.get(key));
|
|
310
343
|
logger.log('Container.pipeline: freeze:exit.');
|
|
311
344
|
logger.log('Container.pipeline: return:success.');
|
|
312
|
-
return frozenMock;
|
|
345
|
+
return asPromiseSafe(frozenMock);
|
|
313
346
|
}
|
|
314
347
|
logger.log(`Container.pipeline: mock-lookup:miss '${key}'.`);
|
|
315
348
|
} else {
|
|
@@ -371,7 +404,7 @@ export default class TeqFw_Di_Container {
|
|
|
371
404
|
|
|
372
405
|
const result = build(getKey(root));
|
|
373
406
|
logger.log('Container.pipeline: return:success.');
|
|
374
|
-
return result;
|
|
407
|
+
return asPromiseSafe(result);
|
|
375
408
|
} catch (error) {
|
|
376
409
|
logger.error(`Container.pipeline: failed at stage='${stage}'.`, error);
|
|
377
410
|
logger.log('Container.transition: operational -> failed.');
|