@teqfw/di 2.4.0 → 2.5.1
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 +12 -0
- package/README.md +159 -284
- package/ai/AGENTS.md +20 -1
- package/ai/concepts.md +2 -2
- package/ai/container.md +3 -0
- package/ai/dependency-id.md +4 -2
- package/ai/package-api.ts +2 -2
- package/ai/usage.md +58 -43
- package/dist/esm.js +1 -1
- package/dist/umd.js +1 -1
- package/package.json +1 -1
- package/src/Config/NamespaceRegistry.mjs +6 -1
- package/src/Container/Instantiate/ExportSelector.mjs +6 -1
- package/src/Container/Instantiate/Instantiator.mjs +7 -2
- package/src/Container/Lifecycle/Registry.mjs +7 -2
- package/src/Container/Resolve/GraphResolver.mjs +28 -11
- package/src/Container/Resolver.mjs +10 -5
- package/src/Container/Wrapper/Executor.mjs +6 -1
- package/src/Container.mjs +40 -20
- package/src/Def/Parser.mjs +8 -3
- package/src/Dto/DepId.mjs +6 -1
- package/src/Dto/Resolver/Config/Namespace.mjs +9 -4
- package/src/Dto/Resolver/Config.mjs +10 -5
- package/src/Enum/Composition.mjs +5 -0
- package/src/Enum/Life.mjs +5 -0
- package/src/Enum/Platform.mjs +5 -0
- package/src/Internal/Logger.mjs +5 -0
- package/types.d.ts +6 -6
package/ai/container.md
CHANGED
|
@@ -42,6 +42,7 @@ The container exposes a minimal public interface used by application code and co
|
|
|
42
42
|
|
|
43
43
|
The core operations are:
|
|
44
44
|
|
|
45
|
+
- **addNamespaceRoot(prefix, target, defaultExt)** — register namespace resolution rule for Teq modules.
|
|
45
46
|
- **register(identifier, value)** — register a predefined dependency or instance in the container.
|
|
46
47
|
- **get(identifier)** — resolve a dependency identifier and return the linked object.
|
|
47
48
|
- **addPreprocess(handler)** — register a handler that can transform dependency identifiers before resolution.
|
|
@@ -49,6 +50,8 @@ The core operations are:
|
|
|
49
50
|
|
|
50
51
|
The exact semantics of dependency identifiers are defined in **dependency-id.md**. Dependency descriptors are export-scoped: canonical descriptors are hierarchical and keyed by export name, while flat descriptors are shorthand for limited single-export cases.
|
|
51
52
|
|
|
53
|
+
`addNamespaceRoot(prefix, target, defaultExt)` accepts a module-specifier base. The `target` may point to a filesystem-backed path or to a URL-backed import base depending on runtime environment.
|
|
54
|
+
|
|
52
55
|
## Container State Model
|
|
53
56
|
|
|
54
57
|
The container operates in three states:
|
package/ai/dependency-id.md
CHANGED
|
@@ -48,7 +48,7 @@ If no platform prefix is present, the identifier refers to an application module
|
|
|
48
48
|
|
|
49
49
|
## Module Identification
|
|
50
50
|
|
|
51
|
-
The **ModuleName** identifies the module that provides the dependency. Module identifiers use namespace-based naming and are translated into
|
|
51
|
+
The **ModuleName** identifies the module that provides the dependency. Module identifiers use namespace-based naming and are translated into module specifiers according to namespace resolution rules described in **container.md**.
|
|
52
52
|
|
|
53
53
|
Identifier segments separated by underscores correspond to directory boundaries in the module path.
|
|
54
54
|
|
|
@@ -58,12 +58,14 @@ Example:
|
|
|
58
58
|
App_Service_User
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
maps to a module
|
|
61
|
+
maps to a module path such as:
|
|
62
62
|
|
|
63
63
|
```txt
|
|
64
64
|
AppRoot/Service/User.js
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
In URL-backed namespace configurations the same logical name may instead map to a URL-based module specifier with the same segment structure.
|
|
68
|
+
|
|
67
69
|
## Export Selection
|
|
68
70
|
|
|
69
71
|
A CDC may reference either the default export of a module or a named export.
|
package/ai/package-api.ts
CHANGED
|
@@ -267,14 +267,14 @@ export const PACKAGE_API: PackageApiContract = {
|
|
|
267
267
|
kind: 'module-contract',
|
|
268
268
|
summary: 'Shape expected from application modules resolved by the container.',
|
|
269
269
|
fields: {
|
|
270
|
-
__deps__: 'Optional
|
|
270
|
+
__deps__: 'Optional dependency descriptor. Accepted forms are: absent/empty, a flat Record<dependencyKey, CDC string> for a dependency-free default export or limited single-export shorthand, or a hierarchical Record<exportName, Record<dependencyKey, CDC string>> for named exports.',
|
|
271
271
|
moduleNamespace: 'Whole ES module namespace object returned for as-is CDC without selected export.',
|
|
272
272
|
defaultExport: 'Used when the parsed DepId selects exportName="default" for factory composition.',
|
|
273
273
|
namedExports: 'May be selected via __ExportName for factory composition and may also provide wrapper functions.',
|
|
274
274
|
wrapperExport: 'Named export whose identifier appears in depId.wrappers; it must be synchronous and unary.',
|
|
275
275
|
},
|
|
276
276
|
notes: [
|
|
277
|
-
'The
|
|
277
|
+
'The runtime accepts hierarchical descriptors for named exports and the flat shorthand form for dependency-free default exports or limited single-export cases.',
|
|
278
278
|
'Wrapper functions are exported by the same resolved module namespace, not registered globally in the container.',
|
|
279
279
|
],
|
|
280
280
|
},
|
package/ai/usage.md
CHANGED
|
@@ -14,46 +14,26 @@ Modules intended for container linking may expose a default export, a named expo
|
|
|
14
14
|
|
|
15
15
|
Dependencies are injected into the constructor as a single structured object. In TeqFW-style modules the public API is defined inside the constructor through assignments to `this`, while internal state may be held in constructor-local variables captured by closures.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Minimal single-export example:
|
|
18
18
|
|
|
19
19
|
```js
|
|
20
20
|
// @ts-check
|
|
21
21
|
|
|
22
22
|
export const __deps__ = {
|
|
23
|
-
|
|
24
|
-
cast: "Fl32_Web_Helper_Cast$",
|
|
25
|
-
},
|
|
26
|
-
Factory: {
|
|
27
|
-
cast: "Fl32_Web_Helper_Cast$",
|
|
28
|
-
},
|
|
23
|
+
cast: "App_Helper_Cast$",
|
|
29
24
|
};
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
* Runtime wrapper that can hold non-DI behavior.
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
export default class RuntimeWrapper {
|
|
36
|
-
constructor() {
|
|
37
|
-
return proxy;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* @typedef {Object} App_Factory$Deps
|
|
43
|
-
* @property {object} cast
|
|
44
|
-
*/
|
|
45
|
-
|
|
46
|
-
export class Factory {
|
|
26
|
+
export default class App_Root {
|
|
47
27
|
/**
|
|
48
|
-
* @param {
|
|
28
|
+
* @param {{cast: (value: unknown) => string}} deps
|
|
49
29
|
*/
|
|
50
30
|
constructor({ cast }) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
31
|
+
return {
|
|
32
|
+
configure(params = {}) {
|
|
33
|
+
return {
|
|
34
|
+
name: cast(params.name ?? "app"),
|
|
35
|
+
};
|
|
36
|
+
},
|
|
57
37
|
};
|
|
58
38
|
}
|
|
59
39
|
}
|
|
@@ -64,12 +44,10 @@ Dependency module:
|
|
|
64
44
|
```js
|
|
65
45
|
// @ts-check
|
|
66
46
|
|
|
67
|
-
export default
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
};
|
|
72
|
-
}
|
|
47
|
+
export default function App_Helper_Cast() {
|
|
48
|
+
return function cast(value) {
|
|
49
|
+
return String(value);
|
|
50
|
+
};
|
|
73
51
|
}
|
|
74
52
|
```
|
|
75
53
|
|
|
@@ -78,10 +56,41 @@ Rules:
|
|
|
78
56
|
- the canonical form of `__deps__` is hierarchical and keyed by export name
|
|
79
57
|
- each export entry maps constructor dependency names to CDC identifiers
|
|
80
58
|
- if `__deps__` is absent the module has no dependencies
|
|
81
|
-
- a flat `__deps__` object is shorthand for limited single-export cases
|
|
59
|
+
- a flat `__deps__` object is shorthand for a dependency-free default export or limited single-export cases
|
|
60
|
+
- a named export may declare dependencies without requiring a `default` entry
|
|
82
61
|
- dependencies are resolved recursively before instantiation
|
|
83
62
|
|
|
84
|
-
When the CDC selects `App_Module$`, the container uses the default export. When the CDC selects `App_Module__Factory$`, the container uses the named
|
|
63
|
+
When the CDC selects `App_Module$`, the container uses the default export. When the CDC selects `App_Module__Factory$`, the container uses the named export `Factory`.
|
|
64
|
+
|
|
65
|
+
Canonical export-scoped form:
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
export const __deps__ = {
|
|
69
|
+
default: {
|
|
70
|
+
cast: "App_Helper_Cast$",
|
|
71
|
+
},
|
|
72
|
+
Factory: {
|
|
73
|
+
cast: "App_Helper_Cast$",
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default class RuntimeWrapper {
|
|
78
|
+
constructor() {
|
|
79
|
+
return {mode: "wrapper"};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class Factory {
|
|
84
|
+
constructor({ cast }) {
|
|
85
|
+
this.configure = function (params = {}) {
|
|
86
|
+
return {
|
|
87
|
+
mode: "factory",
|
|
88
|
+
name: cast(params.name ?? "app"),
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
85
94
|
|
|
86
95
|
## Container Configuration
|
|
87
96
|
|
|
@@ -102,6 +111,13 @@ container.addNamespaceRoot("App_", path.resolve(__dirname, "./src/App"), ".mjs")
|
|
|
102
111
|
|
|
103
112
|
Configuration must be completed before the first dependency resolution.
|
|
104
113
|
|
|
114
|
+
Browser-oriented or isomorphic configuration may also use URL-based import roots:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
container.addNamespaceRoot("App_", "https://cdn.example.com/app", ".mjs");
|
|
118
|
+
container.addNamespaceRoot("Web_", "//cdn.example.com/web", ".mjs");
|
|
119
|
+
```
|
|
120
|
+
|
|
105
121
|
## Resolving Dependencies
|
|
106
122
|
|
|
107
123
|
Dependencies are obtained using CDC identifiers.
|
|
@@ -122,8 +138,7 @@ The container:
|
|
|
122
138
|
Example usage:
|
|
123
139
|
|
|
124
140
|
```js
|
|
125
|
-
console.log(root.
|
|
126
|
-
console.log(root.getChild().getName());
|
|
141
|
+
console.log(root.configure({name: 123}).name);
|
|
127
142
|
console.log(Object.isFrozen(root));
|
|
128
143
|
```
|
|
129
144
|
|
|
@@ -207,14 +222,14 @@ export const __deps__ = {
|
|
|
207
222
|
|
|
208
223
|
export default class RuntimeWrapper {
|
|
209
224
|
constructor() {
|
|
210
|
-
return
|
|
225
|
+
return {mode: "wrapper"};
|
|
211
226
|
}
|
|
212
227
|
}
|
|
213
228
|
|
|
214
229
|
export class Factory {
|
|
215
230
|
constructor({ cast }) {
|
|
216
231
|
this.configure = function (params = {}) {
|
|
217
|
-
return {
|
|
232
|
+
return {cast, params};
|
|
218
233
|
};
|
|
219
234
|
}
|
|
220
235
|
}
|
|
@@ -236,7 +251,7 @@ Some single-export modules may use a flat `__deps__` object as shorthand.
|
|
|
236
251
|
|
|
237
252
|
```js
|
|
238
253
|
export const __deps__ = {
|
|
239
|
-
cast: "
|
|
254
|
+
cast: "App_Helper_Cast$",
|
|
240
255
|
};
|
|
241
256
|
```
|
|
242
257
|
|
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(/(?:^|[^_])_[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 l=class{moduleName;platform;exportName;composition;life;wrappers;origin},c=class{create(e){const t=e&&"object"==typeof e?e:{},r=new l;r.moduleName="string"==typeof t.moduleName?t.moduleName:"";const c="string"==typeof t.platform?t.platform:void 0;r.platform=c&&i.has(c)?c: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 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=r.TEQ;if(a.startsWith("node:"))l=r.NODE,a=a.slice(5);else if(a.startsWith("npm:"))l=r.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],r=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),r.length>0&&(f=r.slice(1).split("_"))}else{if(a.includes("$"))throw new Error("Invalid lifecycle encoding.");if(l!==r.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 w=u,h=null;if(-1!==m){if(w=u.slice(0,m),h=u.slice(m+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!==r.NPM){if(l===r.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 $=o.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:{},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,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 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,(l=r.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}(r.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 r=e.platform,c=e.moduleName,p=`${r}::${c}`;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=l(r,c);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}}}}class w{constructor({parser:e,resolver:t,logger:r=null}){const o=r,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(r,s,a,l){const c=n(r);if(a.has(c)){const e=[...l,c].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const p=n(r);if(!s.has(p)){a.add(c),l.push(c);try{const n=await t.resolve(r);o&&o.log(`GraphResolver.walk: resolved '${p}'.`),s.set(p,{depId:r,namespace:n});const c=Reflect.get(n,"__deps__");if(void 0===c)return;let f={};if(null===c||"object"!=typeof c||Array.isArray(c))throw new Error("__deps__ must be a plain object.");const u=null===r.exportName?"default":r.exportName,d=Reflect.get(c,u);if(null==d||"object"!=typeof d||Array.isArray(d)){if("default"===u){const e=Object.values(c);if(e.every(e=>"string"==typeof e))f=c;else if(!e.every(e=>null!==e&&"object"==typeof e&&!Array.isArray(e)))throw new Error("__deps__ must be either flat or export-scoped.")}}else{if(!Object.values(d).every(e=>"string"==typeof e))throw new Error("__deps__ export entries must map dependency names to CDC strings.");f=d}for(const[,t]of Object.entries(f)){const r=t,n=e.parse(r);o&&o.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,r=new Set;return await i(e,t,r,[]),t}}}class h{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 y{constructor(r=null){const o=new Map,n=r;this.apply=function(r,i){if(r.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${r.composition}' cache=skip.`),i();if(r.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),i();if(r.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("::")}(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=i();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."),i()}}}class ${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 x{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 N=Object.freeze({log(){},error(){}});class E{constructor(){let e="notConfigured";const t=[],r=[],o=[],n=new Map,i=new WeakMap;let s=!1,a=!1,l=new p;const c=new m;let f,u,d,E=N;const _=new h,b=new $,C=function(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("::")},v=function(e){return C(e)},j=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;if("[object Module]"===Object.prototype.toString.call(e))return e;if(Object.isFrozen(e))return e;try{Object.freeze(e)}catch(e){E.log(`Container.freeze: skipped (${String(e)}).`)}return e},A=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;const r=e;if(i.has(r))return i.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 i.set(r,e),e}},I=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},O=function(e){a&&E.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){I(),O("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){I(),O("addPostprocess()."),r.push(e)},this.setParser=function(e){I(),l=e,"function"==typeof l.setLogger&&l.setLogger(a?E:null),O("setParser().")},this.addNamespaceRoot=function(e,t,r){I(),O(`addNamespaceRoot('${e}').`),o.push({prefix:e,target:t,defaultExt:r})},this.enableTestMode=function(){I(),O("enableTestMode()."),s=!0},this.enableLogging=function(){I(),a||(a=!0,E=new x,"function"==typeof l.setLogger&&l.setLogger(E),E.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(I(),O(`register('${e}').`),!0!==s)throw new Error("Container test mode is disabled.");const r=l.parse(e);n.set(v(r),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:o});"function"==typeof l.setLogger&&l.setLogger(E),f=new g({config:t,logger:E}),u=new w({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 r=e;for(const e of t)r=e(r);return r}(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=v(m);if(n.has(e)){E.log(`Container.pipeline: mock-lookup:hit '${e}'.`),a="freeze",E.log("Container.pipeline: freeze:entry.");const t=j(n.get(e));return E.log("Container.pipeline: freeze:exit."),E.log("Container.pipeline: return:success."),A(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(m);E.log(`Container.pipeline: resolve:exit nodes=${h.size}.`);const $=new Map,x=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 o=d.apply(t.depId,function(){a="instantiate",E.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},o=function(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{}}(t.namespace,t.depId);for(const[t,r]of Object.entries(o)){const o=r,n=l.parse(o);e[t]=x(C(n))}const n=_.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 r)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=j(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,o),o},N=x(C(m));return E.log("Container.pipeline: return:success."),A(N)}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};
|
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(/(?:^|[^_])_[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 l=class{moduleName;platform;exportName;composition;life;wrappers;origin},c=class{create(e){const t=e&&"object"==typeof e?e:{},r=new l;r.moduleName="string"==typeof t.moduleName?t.moduleName:"";const c="string"==typeof t.platform?t.platform:void 0;r.platform=c&&i.has(c)?c: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 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=r.TEQ;if(a.startsWith("node:"))l=r.NODE,a=a.slice(5);else if(a.startsWith("npm:"))l=r.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],r=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),r.length>0&&(f=r.slice(1).split("_"))}else{if(a.includes("$"))throw new Error("Invalid lifecycle encoding.");if(l!==r.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 w=u,h=null;if(-1!==m){if(w=u.slice(0,m),h=u.slice(m+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!==r.NPM){if(l===r.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 $=o.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:{},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,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 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,(l=r.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}(r.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 r=e.platform,c=e.moduleName,p=`${r}::${c}`;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=l(r,c);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}}}}class w{constructor({parser:e,resolver:t,logger:r=null}){const o=r,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(r,s,a,l){const c=n(r);if(a.has(c)){const e=[...l,c].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const p=n(r);if(!s.has(p)){a.add(c),l.push(c);try{const n=await t.resolve(r);o&&o.log(`GraphResolver.walk: resolved '${p}'.`),s.set(p,{depId:r,namespace:n});const c=Reflect.get(n,"__deps__");if(void 0===c)return;let f={};if(null===c||"object"!=typeof c||Array.isArray(c))throw new Error("__deps__ must be a plain object.");const u=null===r.exportName?"default":r.exportName,d=Reflect.get(c,u);if(null==d||"object"!=typeof d||Array.isArray(d)){if("default"===u){const e=Object.values(c);if(e.every(e=>"string"==typeof e))f=c;else if(!e.every(e=>null!==e&&"object"==typeof e&&!Array.isArray(e)))throw new Error("__deps__ must be either flat or export-scoped.")}}else{if(!Object.values(d).every(e=>"string"==typeof e))throw new Error("__deps__ export entries must map dependency names to CDC strings.");f=d}for(const[,t]of Object.entries(f)){const r=t,n=e.parse(r);o&&o.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,r=new Set;return await i(e,t,r,[]),t}}}class h{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 y{constructor(r=null){const o=new Map,n=r;this.apply=function(r,i){if(r.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${r.composition}' cache=skip.`),i();if(r.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),i();if(r.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("::")}(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=i();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."),i()}}}class ${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 x{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 N=Object.freeze({log(){},error(){}});return class{constructor(){let e="notConfigured";const t=[],r=[],o=[],n=new Map,i=new WeakMap;let s=!1,a=!1,l=new p;const c=new m;let f,u,d,E=N;const _=new h,b=new $,C=function(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("::")},v=function(e){return C(e)},j=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;if("[object Module]"===Object.prototype.toString.call(e))return e;if(Object.isFrozen(e))return e;try{Object.freeze(e)}catch(e){E.log(`Container.freeze: skipped (${String(e)}).`)}return e},A=function(e){if(null==e)return e;const t=typeof e;if("object"!==t&&"function"!==t)return e;const r=e;if(i.has(r))return i.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 i.set(r,e),e}},I=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},O=function(e){a&&E.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){I(),O("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){I(),O("addPostprocess()."),r.push(e)},this.setParser=function(e){I(),l=e,"function"==typeof l.setLogger&&l.setLogger(a?E:null),O("setParser().")},this.addNamespaceRoot=function(e,t,r){I(),O(`addNamespaceRoot('${e}').`),o.push({prefix:e,target:t,defaultExt:r})},this.enableTestMode=function(){I(),O("enableTestMode()."),s=!0},this.enableLogging=function(){I(),a||(a=!0,E=new x,"function"==typeof l.setLogger&&l.setLogger(E),E.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(I(),O(`register('${e}').`),!0!==s)throw new Error("Container test mode is disabled.");const r=l.parse(e);n.set(v(r),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:o});"function"==typeof l.setLogger&&l.setLogger(E),f=new g({config:t,logger:E}),u=new w({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 r=e;for(const e of t)r=e(r);return r}(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=v(m);if(n.has(e)){E.log(`Container.pipeline: mock-lookup:hit '${e}'.`),a="freeze",E.log("Container.pipeline: freeze:entry.");const t=j(n.get(e));return E.log("Container.pipeline: freeze:exit."),E.log("Container.pipeline: return:success."),A(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(m);E.log(`Container.pipeline: resolve:exit nodes=${h.size}.`);const $=new Map,x=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 o=d.apply(t.depId,function(){a="instantiate",E.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},o=function(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{}}(t.namespace,t.depId);for(const[t,r]of Object.entries(o)){const o=r,n=l.parse(o);e[t]=x(C(n))}const n=_.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 r)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=j(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,o),o},N=x(C(m));return E.log("Container.pipeline: return:success."),A(N)}catch(t){throw E.error(`Container.pipeline: failed at stage='${a}'.`,t),E.log("Container.transition: operational -> failed."),e="failed",t}}}}});
|
package/package.json
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @namespace TeqFw_Di_Config_NamespaceRegistry
|
|
5
|
+
* @description Deterministic loader for package namespace rules.
|
|
6
|
+
*/
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* @typedef {object} TeqFw_Di_Config_NamespaceRegistry_Dependencies
|
|
5
10
|
* @property {{readFile(path: string, encoding: string): Promise<string>, readdir(path: string): Promise<string[]>, realpath(path: string): Promise<string>, stat(path: string): Promise<{isDirectory(): boolean}>}} fs
|
|
@@ -19,7 +24,7 @@
|
|
|
19
24
|
*/
|
|
20
25
|
export default class TeqFw_Di_Config_NamespaceRegistry {
|
|
21
26
|
/**
|
|
22
|
-
* @param {TeqFw_Di_Config_NamespaceRegistry_Dependencies}
|
|
27
|
+
* @param {TeqFw_Di_Config_NamespaceRegistry_Dependencies} deps
|
|
23
28
|
*/
|
|
24
29
|
constructor({fs, path, appRoot}) {
|
|
25
30
|
const appRootAbs = path.resolve(appRoot);
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @namespace TeqFw_Di_Container_Instantiate_ExportSelector
|
|
5
|
+
* @description Selects target export from loaded module namespace.
|
|
6
|
+
*/
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* Instantiate-stage export selector.
|
|
5
10
|
*
|
|
@@ -15,7 +20,7 @@ export default class TeqFw_Di_Container_Instantiate_ExportSelector {
|
|
|
15
20
|
* Selects a raw export value from module namespace.
|
|
16
21
|
*
|
|
17
22
|
* @param {object} namespace Loaded ES module namespace object.
|
|
18
|
-
* @param {
|
|
23
|
+
* @param {TeqFw_Di_DepId__DTO} depId Dependency identity DTO.
|
|
19
24
|
* @returns {unknown} Raw selected export value.
|
|
20
25
|
*/
|
|
21
26
|
this.select = function (namespace, depId) {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @namespace TeqFw_Di_Container_Instantiate_Instantiator
|
|
5
|
+
* @description Instantiates selected exports using composition rules.
|
|
6
|
+
*/
|
|
7
|
+
|
|
3
8
|
import TeqFw_Di_Enum_Composition from '../../Enum/Composition.mjs';
|
|
4
9
|
|
|
5
10
|
/**
|
|
@@ -30,7 +35,7 @@ export default class TeqFw_Di_Container_Instantiate_Instantiator {
|
|
|
30
35
|
/**
|
|
31
36
|
* Selects the value used by composition.
|
|
32
37
|
*
|
|
33
|
-
* @param {
|
|
38
|
+
* @param {TeqFw_Di_DepId__DTO} depId
|
|
34
39
|
* @param {object} moduleNamespace
|
|
35
40
|
* @returns {Factory}
|
|
36
41
|
*/
|
|
@@ -66,7 +71,7 @@ export default class TeqFw_Di_Container_Instantiate_Instantiator {
|
|
|
66
71
|
/**
|
|
67
72
|
* Produces a value from a resolved module namespace and dependency map.
|
|
68
73
|
*
|
|
69
|
-
* @param {
|
|
74
|
+
* @param {TeqFw_Di_DepId__DTO} depId
|
|
70
75
|
* @param {object} moduleNamespace
|
|
71
76
|
* @param {Record<string, unknown>} resolvedDeps
|
|
72
77
|
* @returns {unknown}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @namespace TeqFw_Di_Container_Lifecycle_Registry
|
|
5
|
+
* @description Lifecycle policy cache for produced values.
|
|
6
|
+
*/
|
|
7
|
+
|
|
3
8
|
import TeqFw_Di_Enum_Composition from '../../Enum/Composition.mjs';
|
|
4
9
|
import TeqFw_Di_Enum_Life from '../../Enum/Life.mjs';
|
|
5
10
|
|
|
@@ -25,7 +30,7 @@ export default class TeqFw_Di_Container_Lifecycle_Registry {
|
|
|
25
30
|
/**
|
|
26
31
|
* Builds deterministic cache key from structural DepId fields.
|
|
27
32
|
*
|
|
28
|
-
* @param {
|
|
33
|
+
* @param {TeqFw_Di_DepId__DTO} depId
|
|
29
34
|
* @returns {string}
|
|
30
35
|
*/
|
|
31
36
|
const buildKey = function (depId) {
|
|
@@ -44,7 +49,7 @@ export default class TeqFw_Di_Container_Lifecycle_Registry {
|
|
|
44
49
|
/**
|
|
45
50
|
* Returns value according to lifecycle policy.
|
|
46
51
|
*
|
|
47
|
-
* @param {
|
|
52
|
+
* @param {TeqFw_Di_DepId__DTO} depId
|
|
48
53
|
* @param {() => unknown} producer
|
|
49
54
|
* @returns {unknown}
|
|
50
55
|
*/
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @namespace TeqFw_Di_Container_Resolve_GraphResolver
|
|
5
|
+
* @description Dependency graph resolver for container preloading.
|
|
6
|
+
*/
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* Resolve-stage graph builder.
|
|
5
10
|
*
|
|
@@ -15,7 +20,7 @@
|
|
|
15
20
|
*/
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
|
-
* @typedef {{depId:
|
|
23
|
+
* @typedef {{depId: TeqFw_Di_DepId__DTO, namespace: object}} TeqFw_Di_Container_Resolve_GraphResolver_Node
|
|
19
24
|
*/
|
|
20
25
|
|
|
21
26
|
export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
@@ -28,7 +33,7 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
|
28
33
|
const log = logger;
|
|
29
34
|
|
|
30
35
|
/**
|
|
31
|
-
* @param {
|
|
36
|
+
* @param {TeqFw_Di_DepId__DTO} depId
|
|
32
37
|
* @returns {string}
|
|
33
38
|
*/
|
|
34
39
|
const makeNodeKey = function (depId) {
|
|
@@ -45,7 +50,7 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
|
45
50
|
};
|
|
46
51
|
|
|
47
52
|
/**
|
|
48
|
-
* @param {
|
|
53
|
+
* @param {TeqFw_Di_DepId__DTO} depId
|
|
49
54
|
* @param {Map<string, TeqFw_Di_Container_Resolve_GraphResolver_Node>} out
|
|
50
55
|
* @param {Set<string>} stack
|
|
51
56
|
* @param {string[]} chain
|
|
@@ -76,18 +81,30 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
|
76
81
|
const depsDecl = Reflect.get(namespace, '__deps__');
|
|
77
82
|
if (depsDecl === undefined) return;
|
|
78
83
|
/** @type {Record<string, unknown>} */
|
|
79
|
-
let depsMap =
|
|
80
|
-
if ((depsDecl
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
let depsMap = {};
|
|
85
|
+
if ((depsDecl === null) || (typeof depsDecl !== 'object') || Array.isArray(depsDecl)) {
|
|
86
|
+
throw new Error('__deps__ must be a plain object.');
|
|
87
|
+
}
|
|
88
|
+
const exportName = depId.exportName === null ? 'default' : depId.exportName;
|
|
89
|
+
const exportScoped = Reflect.get(/** @type {object} */ (depsDecl), exportName);
|
|
90
|
+
if ((exportScoped !== undefined) && (exportScoped !== null) && (typeof exportScoped === 'object') && !Array.isArray(exportScoped)) {
|
|
91
|
+
const values = Object.values(/** @type {Record<string, unknown>} */ (exportScoped));
|
|
92
|
+
if (!values.every((value) => typeof value === 'string')) {
|
|
93
|
+
throw new Error('__deps__ export entries must map dependency names to CDC strings.');
|
|
94
|
+
}
|
|
95
|
+
depsMap = /** @type {Record<string, unknown>} */ (exportScoped);
|
|
96
|
+
} else if (exportName === 'default') {
|
|
97
|
+
const values = Object.values(/** @type {Record<string, unknown>} */ (depsDecl));
|
|
98
|
+
if (values.every((value) => typeof value === 'string')) {
|
|
99
|
+
depsMap = /** @type {Record<string, unknown>} */ (depsDecl);
|
|
100
|
+
} else if (!values.every((value) => (value !== null) && (typeof value === 'object') && !Array.isArray(value))) {
|
|
101
|
+
throw new Error('__deps__ must be either flat or export-scoped.');
|
|
85
102
|
}
|
|
86
103
|
}
|
|
87
104
|
for (const [, cdc] of Object.entries(depsMap)) {
|
|
88
105
|
/** @type {string} */
|
|
89
106
|
const nextCdc = /** @type {string} */ (cdc);
|
|
90
|
-
/** @type {
|
|
107
|
+
/** @type {TeqFw_Di_DepId__DTO} */
|
|
91
108
|
const nextDepId = parser.parse(nextCdc);
|
|
92
109
|
if (log) log.log(`GraphResolver.walk: edge '${key}' -> '${nextDepId.platform}::${nextDepId.moduleName}'.`);
|
|
93
110
|
await walk(nextDepId, out, stack, chain);
|
|
@@ -101,7 +118,7 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
|
101
118
|
/**
|
|
102
119
|
* Resolves full dependency graph for a root depId.
|
|
103
120
|
*
|
|
104
|
-
* @param {
|
|
121
|
+
* @param {TeqFw_Di_DepId__DTO} depId
|
|
105
122
|
* @returns {Promise<Map<string, TeqFw_Di_Container_Resolve_GraphResolver_Node>>}
|
|
106
123
|
*/
|
|
107
124
|
this.resolve = async function (depId) {
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @namespace TeqFw_Di_Resolver
|
|
5
|
+
* @description Module resolver for namespace-addressed dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* @typedef {object} TeqFw_Di_Resolver_Dependencies
|
|
5
|
-
* @property {
|
|
10
|
+
* @property {TeqFw_Di_Dto_Resolver_Config__DTO} config Resolver configuration DTO.
|
|
6
11
|
* @property {(specifier: string) => Promise<object>} [importFn] Import function override.
|
|
7
12
|
* @property {{log(message: string): void, error(message: string, error?: unknown): void}|null} [logger]
|
|
8
13
|
*/
|
|
@@ -19,12 +24,12 @@ export default class TeqFw_Di_Resolver {
|
|
|
19
24
|
/**
|
|
20
25
|
* Initializes resolver with runtime dependencies.
|
|
21
26
|
*
|
|
22
|
-
* @param {TeqFw_Di_Resolver_Dependencies}
|
|
27
|
+
* @param {TeqFw_Di_Resolver_Dependencies} deps Resolver dependencies descriptor.
|
|
23
28
|
*/
|
|
24
29
|
constructor({config, importFn = (specifier) => import(specifier), logger = null}) {
|
|
25
30
|
/** @type {Map<string, Promise<object>>} Cache keyed by `(platform,moduleName)`. */
|
|
26
31
|
const cache = new Map();
|
|
27
|
-
/** @type {
|
|
32
|
+
/** @type {TeqFw_Di_Dto_Resolver_Config__DTO} Original config reference captured from dependencies. */
|
|
28
33
|
const configInput = config;
|
|
29
34
|
/** @type {{nodeModulesRoot: (string|undefined), namespaces: TeqFw_Di_Resolver_NamespaceRule[]}|undefined} */
|
|
30
35
|
let configSnapshot;
|
|
@@ -36,7 +41,7 @@ export default class TeqFw_Di_Resolver {
|
|
|
36
41
|
/**
|
|
37
42
|
* Creates immutable-in-effect structural snapshot used for all post-start resolutions.
|
|
38
43
|
*
|
|
39
|
-
* @param {
|
|
44
|
+
* @param {TeqFw_Di_Dto_Resolver_Config__DTO} input Resolver config DTO.
|
|
40
45
|
* @returns {{nodeModulesRoot: (string|undefined), namespaces: TeqFw_Di_Resolver_NamespaceRule[]}}
|
|
41
46
|
*/
|
|
42
47
|
const makeConfigSnapshot = function (input) {
|
|
@@ -134,7 +139,7 @@ export default class TeqFw_Di_Resolver {
|
|
|
134
139
|
/**
|
|
135
140
|
* Resolves module namespace object by depId platform and moduleName.
|
|
136
141
|
*
|
|
137
|
-
* @param {
|
|
142
|
+
* @param {TeqFw_Di_DepId__DTO} depId Validated dependency identity DTO.
|
|
138
143
|
* @returns {Promise<object>} Promise resolved with ES module namespace object.
|
|
139
144
|
*/
|
|
140
145
|
this.resolve = async function (depId) {
|