@teqfw/di 2.5.0 → 2.6.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 +14 -0
- package/README.md +449 -263
- package/ai/AGENTS.md +42 -55
- package/ai/concepts.md +11 -54
- package/ai/container.md +57 -45
- package/ai/dependency-id.md +78 -58
- package/ai/extensions.md +68 -38
- package/ai/package-api.ts +13 -11
- package/ai/usage.md +80 -205
- package/dist/esm.js +1 -1
- package/dist/umd.js +1 -1
- package/package.json +1 -1
- package/src/Config/NamespaceRegistry.mjs +19 -15
- package/src/Container/Lifecycle/Registry.mjs +2 -12
- package/src/Container/Resolve/GraphResolver.mjs +8 -28
- package/src/Container/Runtime.mjs +244 -0
- package/src/Container.mjs +14 -387
- package/src/Internal/DependencyKey.mjs +29 -0
- package/src/Internal/DepsDecl.mjs +42 -0
- package/src/Internal/PromiseSafe.mjs +39 -0
- package/src/{Def/Parser.mjs → Parser.mjs} +82 -35
- package/src/Container/Instantiate/ExportSelector.mjs +0 -44
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(!/^[\x00-\x7F]+$/.test(i))throw new Error("CDC must be ASCII.");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.");let c=null,p=!1,f=[],u=a;const m=u.match(/(\${1,3})(?:_[a-z][0-9A-Za-z]*)*$/);if(m){const e=m[1],o=m[0].slice(e.length);if(p=!0,"$"===e)c=t.SINGLETON;else if("$$"===e)c=t.TRANSIENT;else{if("$$$"!==e)throw new Error("Lifecycle marker is invalid.");c=null}u=u.slice(0,m.index),o.length>0&&(f=o.slice(1).split("_"))}else{if(a.includes("$"))throw new Error("Invalid lifecycle encoding.");if(l!==o.NODE&&/(?:^|[^_])_[a-z][0-9A-Za-z]*$/.test(a))throw new Error("Wrapper without lifecycle is forbidden.")}const d=u.indexOf("__"),g=u.lastIndexOf("__");if(-1!==d&&d!==g)throw new Error("Export delimiter must appear at most once.");if(u.startsWith("__")||u.endsWith("__"))throw new Error("Malformed export segment.");let w=u,h=null;if(-1!==d){if(w=u.slice(0,d),h=u.slice(d+2),!h)throw new Error("Export must be non-empty.");if(h.includes("_"))throw new Error("Export must not contain _.");if(h.includes("$"))throw new Error("Export must not contain $.")}if(!w)throw new Error("moduleName must be non-empty.");if(w.startsWith("_")||w.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(w.includes("__"))throw new Error("moduleName must not contain __.");if(w.includes("$"))throw new Error("moduleName must not contain $.");if(l!==o.NPM){if(l===o.NODE){if(!/^[A-Za-z_][$0-9A-Za-z_/-]*$/.test(w))throw new Error("node moduleName must satisfy the built-in specifier form.")}else if(!/^[A-Za-z_][$0-9A-Za-z_]*$/.test(w))throw new Error("moduleName must satisfy the canonical identifier form.")}else if(!/^[@A-Za-z_][$0-9A-Za-z_./-]*$/.test(w))throw new Error("npm moduleName must satisfy the package specifier form.");let y=e.AS_IS;null!==h?y=e.FACTORY:p&&(y=e.FACTORY,h="default");const $=r.create({moduleName:w,platform:l,exportName:h,composition:y,life:c,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 m{namespaces;nodeModulesRoot}class d{constructor(){const e=new u;this.create=function(t){const o=t&&"object"==typeof t?t:{},r=new m,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;let f=c;if(null!==c&&"object"==typeof c&&!Array.isArray(c)){const e=null===o.exportName?"default":o.exportName,t=Reflect.get(c,e);null==t||"object"!=typeof t||Array.isArray(t)||(f=t)}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 y{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 ${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 x=Object.freeze({log(){},error(){}});class E{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 d;let f,u,m,E=x;const C=new h,b=new $,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("::")},A=function(e){return v(e)},_=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){E.log(`Container.freeze: skipped (${String(e)}).`)}return e},j=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}},I=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},R=function(e){a&&E.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){I(),R("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){I(),R("addPostprocess()."),o.push(e)},this.setParser=function(e){I(),l=e,"function"==typeof l.setLogger&&l.setLogger(a?E:null),R("setParser().")},this.addNamespaceRoot=function(e,t,o){I(),R(`addNamespaceRoot('${e}').`),r.push({prefix:e,target:t,defaultExt:o})},this.enableTestMode=function(){I(),R("enableTestMode()."),s=!0},this.enableLogging=function(){I(),a||(a=!0,E=new N,"function"==typeof l.setLogger&&l.setLogger(E),E.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(I(),R(`register('${e}').`),!0!==s)throw new Error("Container test mode is disabled.");const o=l.parse(e);n.set(A(o),t)},this.get=async function(i){if("failed"===e)throw E.error(`Container.get: rejected in failed state cdc='${i}'.`),new Error("Container is in failed state.");let a="start";try{E.log(`Container.get: cdc='${i}'.`),function(){if("notConfigured"!==e)return;E.log("Container.transition: notConfigured -> operational."),e="operational";const t=c.create({namespaces:r});"function"==typeof l.setLogger&&l.setLogger(E),f=new g({config:t,logger:E}),u=new w({parser:l,resolver:f,logger:E}),m=new y(E)}(),E.log(`Container.state: '${e}'.`),a="parse",E.log("Container.pipeline: parse:entry.");const p=l.parse(i);E.log(`Container.pipeline: parse:exit '${p.platform}::${p.moduleName}'.`),a="preprocess",E.log("Container.pipeline: preprocess:entry.");const d=function(e){let o=e;for(const e of t)o=e(o);return o}(p);if(E.log(`Container.pipeline: preprocess:exit '${d.platform}::${d.moduleName}'.`),!0===s){a="mock",E.log("Container.pipeline: mock-lookup:entry.");const e=A(d);if(n.has(e)){E.log(`Container.pipeline: mock-lookup:hit '${e}'.`),a="freeze",E.log("Container.pipeline: freeze:entry.");const t=_(n.get(e));return E.log("Container.pipeline: freeze:exit."),E.log("Container.pipeline: return:success."),j(t)}E.log(`Container.pipeline: mock-lookup:miss '${e}'.`)}else E.log("Container.pipeline: mock-lookup:disabled.");a="resolve",E.log("Container.pipeline: resolve:entry.");const h=await u.resolve(d);E.log(`Container.pipeline: resolve:exit nodes=${h.size}.`);const $=new Map,N=function(e){if($.has(e))return $.get(e);if(!h.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=h.get(e);a="lifecycle",E.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const r=m.apply(t.depId,function(){a="instantiate",E.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},r=function(e,t){const o=Reflect.get(e,"__deps__");if(void 0===o)return{};if(null!==o&&"object"==typeof o&&!Array.isArray(o)){const e=null===t.exportName?"default":t.exportName,r=Reflect.get(o,e);if(null!=r&&"object"==typeof r&&!Array.isArray(r))return r}return o}(t.namespace,t.depId);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);E.log(`Container.pipeline: instantiate:exit '${t.depId.platform}::${t.depId.moduleName}'.`),a="postprocess",E.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);E.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const s=b.execute(t.depId,i,t.namespace);a="freeze",E.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const c=_(s);return E.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),c});return E.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),$.set(e,r),r},x=N(v(d));return E.log("Container.pipeline: return:success."),j(x)}catch(t){throw E.error(`Container.pipeline: failed at stage='${a}'.`,t),E.log("Container.transition: operational -> failed."),e="failed",t}}}}export{E 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 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};
|
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(!/^[\x00-\x7F]+$/.test(i))throw new Error("CDC must be ASCII.");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.");let c=null,p=!1,f=[],u=a;const d=u.match(/(\${1,3})(?:_[a-z][0-9A-Za-z]*)*$/);if(d){const e=d[1],o=d[0].slice(e.length);if(p=!0,"$"===e)c=t.SINGLETON;else if("$$"===e)c=t.TRANSIENT;else{if("$$$"!==e)throw new Error("Lifecycle marker is invalid.");c=null}u=u.slice(0,d.index),o.length>0&&(f=o.slice(1).split("_"))}else{if(a.includes("$"))throw new Error("Invalid lifecycle encoding.");if(l!==o.NODE&&/(?:^|[^_])_[a-z][0-9A-Za-z]*$/.test(a))throw new Error("Wrapper without lifecycle is forbidden.")}const m=u.indexOf("__"),g=u.lastIndexOf("__");if(-1!==m&&m!==g)throw new Error("Export delimiter must appear at most once.");if(u.startsWith("__")||u.endsWith("__"))throw new Error("Malformed export segment.");let h=u,w=null;if(-1!==m){if(h=u.slice(0,m),w=u.slice(m+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(!h)throw new Error("moduleName must be non-empty.");if(h.startsWith("_")||h.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(h.includes("__"))throw new Error("moduleName must not contain __.");if(h.includes("$"))throw new Error("moduleName must not contain $.");if(l!==o.NPM){if(l===o.NODE){if(!/^[A-Za-z_][$0-9A-Za-z_/-]*$/.test(h))throw new Error("node moduleName must satisfy the built-in specifier form.")}else if(!/^[A-Za-z_][$0-9A-Za-z_]*$/.test(h))throw new Error("moduleName must satisfy the canonical identifier form.")}else if(!/^[@A-Za-z_][$0-9A-Za-z_./-]*$/.test(h))throw new Error("npm moduleName must satisfy the package specifier form.");let y=e.AS_IS;null!==w?y=e.FACTORY:p&&(y=e.FACTORY,w="default");const $=r.create({moduleName:h,platform:l,exportName:w,composition:y,life:c,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 h{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;let f=c;if(null!==c&&"object"==typeof c&&!Array.isArray(c)){const e=null===o.exportName?"default":o.exportName,t=Reflect.get(c,e);null==t||"object"!=typeof t||Array.isArray(t)||(f=t)}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 w{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 y{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 ${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 x=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,E=x;const C=new w,b=new $,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)},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){E.log(`Container.freeze: skipped (${String(e)}).`)}return e},j=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}},I=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},R=function(e){a&&E.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){I(),R("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){I(),R("addPostprocess()."),o.push(e)},this.setParser=function(e){I(),l=e,"function"==typeof l.setLogger&&l.setLogger(a?E:null),R("setParser().")},this.addNamespaceRoot=function(e,t,o){I(),R(`addNamespaceRoot('${e}').`),r.push({prefix:e,target:t,defaultExt:o})},this.enableTestMode=function(){I(),R("enableTestMode()."),s=!0},this.enableLogging=function(){I(),a||(a=!0,E=new N,"function"==typeof l.setLogger&&l.setLogger(E),E.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(I(),R(`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 E.error(`Container.get: rejected in failed state cdc='${i}'.`),new Error("Container is in failed state.");let a="start";try{E.log(`Container.get: cdc='${i}'.`),function(){if("notConfigured"!==e)return;E.log("Container.transition: notConfigured -> operational."),e="operational";const t=c.create({namespaces:r});"function"==typeof l.setLogger&&l.setLogger(E),f=new g({config:t,logger:E}),u=new h({parser:l,resolver:f,logger:E}),d=new y(E)}(),E.log(`Container.state: '${e}'.`),a="parse",E.log("Container.pipeline: parse:entry.");const p=l.parse(i);E.log(`Container.pipeline: parse:exit '${p.platform}::${p.moduleName}'.`),a="preprocess",E.log("Container.pipeline: preprocess:entry.");const m=function(e){let o=e;for(const e of t)o=e(o);return o}(p);if(E.log(`Container.pipeline: preprocess:exit '${m.platform}::${m.moduleName}'.`),!0===s){a="mock",E.log("Container.pipeline: mock-lookup:entry.");const e=_(m);if(n.has(e)){E.log(`Container.pipeline: mock-lookup:hit '${e}'.`),a="freeze",E.log("Container.pipeline: freeze:entry.");const t=A(n.get(e));return E.log("Container.pipeline: freeze:exit."),E.log("Container.pipeline: return:success."),j(t)}E.log(`Container.pipeline: mock-lookup:miss '${e}'.`)}else E.log("Container.pipeline: mock-lookup:disabled.");a="resolve",E.log("Container.pipeline: resolve:entry.");const w=await u.resolve(m);E.log(`Container.pipeline: resolve:exit nodes=${w.size}.`);const $=new Map,N=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);a="lifecycle",E.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const r=d.apply(t.depId,function(){a="instantiate",E.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},r=function(e,t){const o=Reflect.get(e,"__deps__");if(void 0===o)return{};if(null!==o&&"object"==typeof o&&!Array.isArray(o)){const e=null===t.exportName?"default":t.exportName,r=Reflect.get(o,e);if(null!=r&&"object"==typeof r&&!Array.isArray(r))return r}return o}(t.namespace,t.depId);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);E.log(`Container.pipeline: instantiate:exit '${t.depId.platform}::${t.depId.moduleName}'.`),a="postprocess",E.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);E.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const s=b.execute(t.depId,i,t.namespace);a="freeze",E.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const c=A(s);return E.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),c});return E.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),$.set(e,r),r},x=N(v(m));return E.log("Container.pipeline: return:success."),j(x)}catch(t){throw E.error(`Container.pipeline: failed at stage='${a}'.`,t),E.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 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)}}}});
|
package/package.json
CHANGED
|
@@ -138,6 +138,21 @@ export default class TeqFw_Di_Config_NamespaceRegistry {
|
|
|
138
138
|
return {namespaces, dependencies};
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Checks whether package.json exists inside a dependency directory.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} candidateAbs
|
|
145
|
+
* @returns {Promise<boolean>}
|
|
146
|
+
*/
|
|
147
|
+
const hasPackageJson = async function (candidateAbs) {
|
|
148
|
+
try {
|
|
149
|
+
await fs.stat(path.join(candidateAbs, 'package.json'));
|
|
150
|
+
return true;
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
141
156
|
/**
|
|
142
157
|
* @param {string} packageName
|
|
143
158
|
* @param {string} fromPackageRootAbs
|
|
@@ -147,19 +162,8 @@ export default class TeqFw_Di_Config_NamespaceRegistry {
|
|
|
147
162
|
let cursor = fromPackageRootAbs;
|
|
148
163
|
while (isInside(appRootAbs, cursor)) {
|
|
149
164
|
const candidate = path.join(cursor, 'node_modules', packageName);
|
|
150
|
-
if (await isDirectory(candidate)) {
|
|
151
|
-
|
|
152
|
-
const hasPackageJson = await (async () => {
|
|
153
|
-
try {
|
|
154
|
-
await fs.stat(packageJsonAbs);
|
|
155
|
-
return true;
|
|
156
|
-
} catch {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
})();
|
|
160
|
-
if (hasPackageJson) {
|
|
161
|
-
return path.resolve(candidate);
|
|
162
|
-
}
|
|
165
|
+
if (await isDirectory(candidate) && (await hasPackageJson(candidate))) {
|
|
166
|
+
return path.resolve(candidate);
|
|
163
167
|
}
|
|
164
168
|
if (cursor === appRootAbs) break;
|
|
165
169
|
const parent = path.dirname(cursor);
|
|
@@ -174,7 +178,7 @@ export default class TeqFw_Di_Config_NamespaceRegistry {
|
|
|
174
178
|
*/
|
|
175
179
|
this.build = async function () {
|
|
176
180
|
/** @type {{name: string, rootAbs: string}[]} */
|
|
177
|
-
const queue = [{
|
|
181
|
+
const queue = [{rootAbs: appRootAbs}];
|
|
178
182
|
/** @type {Set<string>} */
|
|
179
183
|
const visitedRoots = new Set();
|
|
180
184
|
/** @type {Set<string>} */
|
|
@@ -201,7 +205,7 @@ export default class TeqFw_Di_Config_NamespaceRegistry {
|
|
|
201
205
|
|
|
202
206
|
for (const depName of meta.dependencies) {
|
|
203
207
|
const depRootAbs = await resolveDependencyPackageRoot(depName, packageRootAbs);
|
|
204
|
-
queue.push({
|
|
208
|
+
queue.push({rootAbs: depRootAbs});
|
|
205
209
|
}
|
|
206
210
|
}
|
|
207
211
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import TeqFw_Di_Enum_Composition from '../../Enum/Composition.mjs';
|
|
9
9
|
import TeqFw_Di_Enum_Life from '../../Enum/Life.mjs';
|
|
10
|
+
import {buildDependencyKey} from '../../Internal/DependencyKey.mjs';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Lifecycle-stage registry for produced dependency values.
|
|
@@ -33,18 +34,7 @@ export default class TeqFw_Di_Container_Lifecycle_Registry {
|
|
|
33
34
|
* @param {TeqFw_Di_DepId__DTO} depId
|
|
34
35
|
* @returns {string}
|
|
35
36
|
*/
|
|
36
|
-
const buildKey =
|
|
37
|
-
/** @type {string} */
|
|
38
|
-
const wrappers = Array.isArray(depId.wrappers) ? depId.wrappers.join('|') : '';
|
|
39
|
-
return [
|
|
40
|
-
depId.platform,
|
|
41
|
-
depId.moduleName,
|
|
42
|
-
depId.exportName === null ? '' : depId.exportName,
|
|
43
|
-
depId.composition,
|
|
44
|
-
depId.life === null ? '' : depId.life,
|
|
45
|
-
wrappers,
|
|
46
|
-
].join('::');
|
|
47
|
-
};
|
|
37
|
+
const buildKey = buildDependencyKey;
|
|
48
38
|
|
|
49
39
|
/**
|
|
50
40
|
* Returns value according to lifecycle policy.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* @typedef {object} TeqFw_Di_Container_Resolve_GraphResolver_Dependencies
|
|
17
|
-
* @property {
|
|
17
|
+
* @property {TeqFw_Di_Parser} parser
|
|
18
18
|
* @property {TeqFw_Di_Resolver} resolver
|
|
19
19
|
* @property {{log(message: string): void}|null} [logger]
|
|
20
20
|
*/
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
* @typedef {{depId: TeqFw_Di_DepId__DTO, namespace: object}} TeqFw_Di_Container_Resolve_GraphResolver_Node
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
+
import {buildDependencyKey} from '../../Internal/DependencyKey.mjs';
|
|
27
|
+
import {readDepsDecl} from '../../Internal/DepsDecl.mjs';
|
|
28
|
+
|
|
26
29
|
export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
27
30
|
|
|
28
31
|
/**
|
|
@@ -36,18 +39,7 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
|
36
39
|
* @param {TeqFw_Di_DepId__DTO} depId
|
|
37
40
|
* @returns {string}
|
|
38
41
|
*/
|
|
39
|
-
const makeNodeKey =
|
|
40
|
-
/** @type {string} */
|
|
41
|
-
const wrappers = Array.isArray(depId.wrappers) ? depId.wrappers.join('|') : '';
|
|
42
|
-
return [
|
|
43
|
-
depId.platform,
|
|
44
|
-
depId.moduleName,
|
|
45
|
-
depId.exportName === null ? '' : depId.exportName,
|
|
46
|
-
depId.composition,
|
|
47
|
-
depId.life === null ? '' : depId.life,
|
|
48
|
-
wrappers,
|
|
49
|
-
].join('::');
|
|
50
|
-
};
|
|
42
|
+
const makeNodeKey = buildDependencyKey;
|
|
51
43
|
|
|
52
44
|
/**
|
|
53
45
|
* @param {TeqFw_Di_DepId__DTO} depId
|
|
@@ -77,23 +69,11 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
|
77
69
|
if (log) log.log(`GraphResolver.walk: resolved '${key}'.`);
|
|
78
70
|
out.set(key, {depId, namespace});
|
|
79
71
|
|
|
80
|
-
/** @type {unknown} */
|
|
81
|
-
const depsDecl = Reflect.get(namespace, '__deps__');
|
|
82
|
-
if (depsDecl === undefined) return;
|
|
83
72
|
/** @type {Record<string, unknown>} */
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const exportName = depId.exportName === null ? 'default' : depId.exportName;
|
|
87
|
-
const exportScoped = Reflect.get(/** @type {object} */ (depsDecl), exportName);
|
|
88
|
-
if ((exportScoped !== undefined) && (exportScoped !== null) && (typeof exportScoped === 'object') && !Array.isArray(exportScoped)) {
|
|
89
|
-
depsMap = /** @type {Record<string, unknown>} */ (exportScoped);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
for (const [, cdc] of Object.entries(depsMap)) {
|
|
93
|
-
/** @type {string} */
|
|
94
|
-
const nextCdc = /** @type {string} */ (cdc);
|
|
73
|
+
const depsMap = readDepsDecl(namespace, depId);
|
|
74
|
+
for (const nextCdc of Object.values(depsMap)) {
|
|
95
75
|
/** @type {TeqFw_Di_DepId__DTO} */
|
|
96
|
-
const nextDepId = parser.parse(nextCdc);
|
|
76
|
+
const nextDepId = parser.parse(/** @type {string} */ (nextCdc));
|
|
97
77
|
if (log) log.log(`GraphResolver.walk: edge '${key}' -> '${nextDepId.platform}::${nextDepId.moduleName}'.`);
|
|
98
78
|
await walk(nextDepId, out, stack, chain);
|
|
99
79
|
}
|
|
@@ -0,0 +1,244 @@
|
|
|
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
|
+
}
|