@teqfw/di 2.5.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 +6 -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/Container/Resolve/GraphResolver.mjs +18 -6
- package/src/Container.mjs +21 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.5.1 - 2026-04-12 - Documentation refresh and dependency fix
|
|
4
|
+
|
|
5
|
+
* Fixed named-only `__deps__` resolution in the container and updated the related tests.
|
|
6
|
+
* Rewrote the README and refreshed the `ai/` agent documentation for the current release line.
|
|
7
|
+
* Updated package version metadata to `2.5.1`.
|
|
8
|
+
|
|
3
9
|
## 2.5.0 - 2026-04-04 - Parser and documentation refinement
|
|
4
10
|
|
|
5
11
|
* Clarified JSDoc typing and module headers to align with TeqFW specifications.
|
package/README.md
CHANGED
|
@@ -3,141 +3,65 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
**
|
|
6
|
+
**Deterministic runtime dependency linker for ES modules, built for pure JavaScript applications with explicit contracts.**
|
|
7
7
|
|
|
8
|
-
`@teqfw/di` is
|
|
8
|
+
`@teqfw/di` is a runtime container for JavaScript applications that want **late binding**, **explicit dependency declarations**, and **deterministic runtime linking** instead of application-level wiring through static imports.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
It is the reference implementation of the **Tequila Framework (TeqFW)** method: a way to structure modular monolith and isomorphic web applications around **Canonical Dependency Codes (CDC)** and module-level dependency descriptors (`__deps__`).
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
In practice, "reference implementation of a method" means this package is not only a container library. It is also the concrete runtime model for a broader way of structuring JavaScript applications around explicit contracts, namespace-based addressing, and late binding.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
- generate
|
|
16
|
-
- refactor
|
|
17
|
-
- extend
|
|
14
|
+
This package is designed primarily for:
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
- modular monolith web applications;
|
|
17
|
+
- isomorphic JavaScript systems that share code between browser and server;
|
|
18
|
+
- pure JavaScript + JSDoc codebases;
|
|
19
|
+
- projects developed or maintained with significant LLM-agent involvement.
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## Why Use It
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
`@teqfw/di` provides:
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
- deterministic runtime linking of ES modules;
|
|
26
|
+
- explicit dependency contracts through CDC and `__deps__`;
|
|
27
|
+
- namespace-based module resolution;
|
|
28
|
+
- lifecycle control for singleton and new-instance dependencies;
|
|
29
|
+
- immutable linked objects;
|
|
30
|
+
- wrapper-based extension points for cross-cutting behavior.
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
The result is an application structure that is easier to analyze, test, replace, and evolve when dependency relationships need to remain explicit.
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
- implicit dependency graphs
|
|
31
|
-
- tight coupling to file structure
|
|
32
|
-
- constructor-based dependency inference
|
|
34
|
+
## How It Fits in JavaScript
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
This approach is unusual in mainstream JavaScript.
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
Most JavaScript and TypeScript projects express dependency structure through some mix of:
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
- static imports;
|
|
41
|
+
- framework conventions;
|
|
42
|
+
- TypeScript-first source architecture;
|
|
43
|
+
- decorators or metadata-driven injection;
|
|
44
|
+
- framework-managed DI.
|
|
39
45
|
|
|
40
|
-
|
|
46
|
+
`@teqfw/di` makes a different tradeoff. It favors **explicit runtime contracts** over hidden or inferred wiring. Instead of relying on TypeScript metadata or decorator-driven injection, modules declare dependencies directly as data and the container resolves them deterministically at runtime.
|
|
41
47
|
|
|
42
|
-
|
|
48
|
+
That tradeoff is intentional.
|
|
43
49
|
|
|
44
|
-
|
|
50
|
+
TypeScript has had a major influence on the JavaScript ecosystem, and that influence has been broadly positive. At the same time, JavaScript itself continues to evolve every year, steadily narrowing part of the gap in developer ergonomics and language expressiveness.
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
For TypeScript-first ecosystems, other DI approaches are often a more natural fit because those ecosystems already rely on compile-time metadata, annotations, and framework or container conventions. TeqFW targets a different design space: **pure JavaScript + JSDoc**, isomorphic runtime behavior, and codebases where a single explicit structural representation is more valuable than TypeScript-oriented convenience.
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
- explicit dependency contracts (**CDC — Canonical Dependency Codes**)
|
|
50
|
-
- module dependency descriptors (`__deps__`)
|
|
51
|
-
- namespace-based module resolution
|
|
52
|
-
- runtime lifecycle management
|
|
53
|
-
- wrapper-based behavioral extensions
|
|
54
|
+
This is not presented as the only correct way to structure JavaScript. It is a deliberate alternative for projects that benefit from stronger runtime explicitness and machine-reconstructible structure.
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
## Comparison
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
| Dependency graph | implicit | deterministic |
|
|
65
|
-
| Refactoring | fragile | stable |
|
|
66
|
-
| Testing | manual wiring | container driven |
|
|
67
|
-
| AI compatibility | accidental | intentional |
|
|
68
|
-
|
|
69
|
-
TeqFW structures the application so that **LLM agents can reliably understand and modify the system.**
|
|
70
|
-
|
|
71
|
-
## Agent-Driven Implementation
|
|
72
|
-
|
|
73
|
-
Starting from version **2.0.0**, the source code of this library is **written primarily by Codex agents**.
|
|
74
|
-
|
|
75
|
-
The development workflow follows **specification-driven development**:
|
|
76
|
-
|
|
77
|
-
1. The human architect defines **product specifications**
|
|
78
|
-
2. LLM agents generate the implementation
|
|
79
|
-
3. The generated code is reviewed and integrated
|
|
80
|
-
|
|
81
|
-
This workflow follows the **ADSM methodology (Agent-Driven Software Management)** developed by **Alex Gusev**.
|
|
82
|
-
|
|
83
|
-
Earlier versions of the library (<2.0.0) were written manually.
|
|
84
|
-
The current version demonstrates how software can be developed using **human-defined architecture and AI-generated code**.
|
|
85
|
-
|
|
86
|
-
## Learn the Architecture (Interactive Onboarding)
|
|
87
|
-
|
|
88
|
-
Understanding this architecture can take time.
|
|
89
|
-
|
|
90
|
-
To make onboarding easier, an **interactive AI assistant** [is available](https://fly.wiredgeese.com/flancer/gpt/teqfw/guide/di/).
|
|
91
|
-
|
|
92
|
-
The assistant can explain:
|
|
93
|
-
|
|
94
|
-
- how the container works
|
|
95
|
-
- what Canonical Dependency Codes are
|
|
96
|
-
- how modules declare dependencies
|
|
97
|
-
- how runtime linking works
|
|
98
|
-
- how to integrate the library in real applications
|
|
99
|
-
|
|
100
|
-
The assistant acts as **interactive documentation** for the project.
|
|
101
|
-
|
|
102
|
-
Custom onboarding assistants like this can also be created **as a service** for other projects and libraries.
|
|
103
|
-
|
|
104
|
-
## Agent Interface (Documentation for LLM Agents)
|
|
105
|
-
|
|
106
|
-
This package includes **agent interface documentation** intended for LLM agents that use the library as an npm dependency.
|
|
107
|
-
|
|
108
|
-
These documents are distributed inside the package in:
|
|
109
|
-
|
|
110
|
-
```txt
|
|
111
|
-
./ai/
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
The files in this directory describe the **public interface of the package in an agent-friendly form**.
|
|
115
|
-
|
|
116
|
-
They explain:
|
|
117
|
-
|
|
118
|
-
- the container API
|
|
119
|
-
- dependency descriptors (`__deps__`)
|
|
120
|
-
- Canonical Dependency Codes (CDC)
|
|
121
|
-
- dependency resolution behavior
|
|
122
|
-
- integration patterns
|
|
123
|
-
|
|
124
|
-
Human developers typically read the README and source code, while **LLM agents can rely on the documentation in `./ai/`.**
|
|
125
|
-
|
|
126
|
-
## Tequila Framework Philosophy
|
|
127
|
-
|
|
128
|
-
`@teqfw/di` is the core building block of the **Tequila Framework (TeqFW)** ecosystem.
|
|
129
|
-
|
|
130
|
-
TeqFW is based on several architectural principles:
|
|
131
|
-
|
|
132
|
-
- runtime late binding between components
|
|
133
|
-
- namespace-based module organization
|
|
134
|
-
- modular monolith architecture
|
|
135
|
-
- pure JavaScript without compilation
|
|
136
|
-
- system structures optimized for collaboration with LLM agents
|
|
137
|
-
|
|
138
|
-
Full philosophy:
|
|
139
|
-
|
|
140
|
-
`PHILOSOPHY.md`
|
|
58
|
+
| Concern | Common TS/JS Approach | `@teqfw/di` |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| Dependency structure | static imports, decorators, framework wiring | explicit CDC + `__deps__` |
|
|
61
|
+
| Resolution model | partly implicit or framework-driven | deterministic runtime linking |
|
|
62
|
+
| Structural source of truth | spread across code, metadata, config | declared in module contracts |
|
|
63
|
+
| Best fit | TypeScript-first applications | pure JavaScript + JSDoc, isomorphic modular systems |
|
|
64
|
+
| LLM readability | mixed, often indirect | intentionally explicit |
|
|
141
65
|
|
|
142
66
|
## Installation
|
|
143
67
|
|
|
@@ -145,17 +69,9 @@ Full philosophy:
|
|
|
145
69
|
npm install @teqfw/di
|
|
146
70
|
```
|
|
147
71
|
|
|
148
|
-
## Quick
|
|
149
|
-
|
|
150
|
-
### Define modules
|
|
72
|
+
## Quick Start
|
|
151
73
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
```javascript
|
|
155
|
-
export default function App_Child() {
|
|
156
|
-
return { name: "child" };
|
|
157
|
-
}
|
|
158
|
-
```
|
|
74
|
+
Define one helper module and one module that declares its dependency explicitly.
|
|
159
75
|
|
|
160
76
|
`src/App/Helper/Cast.mjs`
|
|
161
77
|
|
|
@@ -174,28 +90,20 @@ export const __deps__ = {
|
|
|
174
90
|
cast: "App_Helper_Cast$",
|
|
175
91
|
};
|
|
176
92
|
|
|
177
|
-
export default class
|
|
178
|
-
constructor() {
|
|
179
|
-
return {
|
|
180
|
-
mode: "runtime-wrapper",
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export class Factory {
|
|
93
|
+
export default class App_Root {
|
|
186
94
|
constructor({ cast }) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
95
|
+
return {
|
|
96
|
+
configure(params = {}) {
|
|
97
|
+
return {
|
|
98
|
+
name: cast(params.name ?? "app"),
|
|
99
|
+
};
|
|
100
|
+
},
|
|
193
101
|
};
|
|
194
102
|
}
|
|
195
103
|
}
|
|
196
104
|
```
|
|
197
105
|
|
|
198
|
-
|
|
106
|
+
Configure the container and request the dependency:
|
|
199
107
|
|
|
200
108
|
```javascript
|
|
201
109
|
import path from "node:path";
|
|
@@ -206,116 +114,57 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
206
114
|
const __dirname = path.dirname(__filename);
|
|
207
115
|
|
|
208
116
|
const container = new Container();
|
|
209
|
-
|
|
210
117
|
container.addNamespaceRoot("App_", path.resolve(__dirname, "./src/App"), ".mjs");
|
|
211
118
|
|
|
212
|
-
const
|
|
119
|
+
const app = await container.get("App_Root$");
|
|
213
120
|
|
|
214
|
-
console.log(
|
|
121
|
+
console.log(app.configure({ name: 123 }).name);
|
|
122
|
+
// "123"
|
|
215
123
|
```
|
|
216
124
|
|
|
217
|
-
|
|
125
|
+
In this flow the container:
|
|
126
|
+
|
|
127
|
+
- parses the dependency request;
|
|
128
|
+
- resolves the module through the registered namespace root;
|
|
129
|
+
- reads `__deps__` for the selected export;
|
|
130
|
+
- recursively links dependencies;
|
|
131
|
+
- returns a frozen linked object.
|
|
218
132
|
|
|
219
|
-
|
|
220
|
-
- resolves dependency contracts
|
|
221
|
-
- constructs the object graph
|
|
222
|
-
- returns **frozen linked objects**
|
|
133
|
+
## Core Concepts
|
|
223
134
|
|
|
224
|
-
|
|
135
|
+
### `__deps__`
|
|
225
136
|
|
|
226
|
-
|
|
137
|
+
For a single-export module, dependencies can be declared in shorthand form:
|
|
227
138
|
|
|
228
139
|
```javascript
|
|
229
140
|
export const __deps__ = {
|
|
230
|
-
|
|
231
|
-
localName: "Dependency_CDC",
|
|
232
|
-
},
|
|
233
|
-
Factory: {
|
|
234
|
-
localName: "Dependency_CDC",
|
|
235
|
-
},
|
|
141
|
+
localName: "Dependency_CDC",
|
|
236
142
|
};
|
|
237
143
|
```
|
|
238
144
|
|
|
239
145
|
Rules:
|
|
240
146
|
|
|
241
|
-
- the canonical form is hierarchical and keyed by export name
|
|
242
|
-
- each export entry maps constructor argument names to CDC
|
|
243
|
-
- if `__deps__` is absent
|
|
244
|
-
- a flat `__deps__` object is
|
|
245
|
-
- dependencies are resolved recursively
|
|
147
|
+
- the canonical form is hierarchical and keyed by export name;
|
|
148
|
+
- each export entry maps constructor argument names to CDC strings;
|
|
149
|
+
- if `__deps__` is absent, the export has no declared dependencies;
|
|
150
|
+
- a flat `__deps__` object is shorthand for limited single-export cases.
|
|
246
151
|
|
|
247
|
-
Canonical
|
|
152
|
+
Canonical export-scoped form:
|
|
248
153
|
|
|
249
154
|
```javascript
|
|
250
155
|
export const __deps__ = {
|
|
251
156
|
default: {
|
|
252
|
-
|
|
157
|
+
localName: "Dependency_CDC",
|
|
253
158
|
},
|
|
254
159
|
Factory: {
|
|
255
|
-
|
|
160
|
+
localName: "Dependency_CDC",
|
|
256
161
|
},
|
|
257
162
|
};
|
|
258
|
-
|
|
259
|
-
export default class RuntimeWrapper {
|
|
260
|
-
constructor() {
|
|
261
|
-
return {
|
|
262
|
-
mode: "runtime-wrapper",
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
export class Factory {
|
|
268
|
-
constructor({ cast }) {
|
|
269
|
-
this.configure = function (params = {}) {
|
|
270
|
-
// DI-managed component
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
163
|
```
|
|
275
164
|
|
|
276
|
-
|
|
165
|
+
### CDC
|
|
277
166
|
|
|
278
|
-
|
|
279
|
-
- the named `Factory` export is the DI-managed component
|
|
280
|
-
- `__deps__` applies to the export selected by the CDC, such as `App_Module__Factory$`
|
|
281
|
-
|
|
282
|
-
Shorthand example for a single-export module:
|
|
283
|
-
|
|
284
|
-
```javascript
|
|
285
|
-
export const __deps__ = {
|
|
286
|
-
cast: "Fl32_Web_Helper_Cast$",
|
|
287
|
-
};
|
|
288
|
-
|
|
289
|
-
export default class RuntimeWrapper {
|
|
290
|
-
constructor() {
|
|
291
|
-
return {
|
|
292
|
-
mode: "runtime-wrapper",
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
Empty descriptor example:
|
|
299
|
-
|
|
300
|
-
```javascript
|
|
301
|
-
export default class App_Empty {
|
|
302
|
-
constructor() {
|
|
303
|
-
this.ready = function () {
|
|
304
|
-
return true;
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
In this pattern:
|
|
311
|
-
|
|
312
|
-
- the module has no declared dependencies
|
|
313
|
-
- `__deps__` is omitted entirely
|
|
314
|
-
- the empty form is valid and intentionally explicit through omission
|
|
315
|
-
|
|
316
|
-
## Canonical Dependency Codes (CDC)
|
|
317
|
-
|
|
318
|
-
CDC identifiers describe **how dependencies should be resolved**.
|
|
167
|
+
A **Canonical Dependency Code** is the string contract used to request a dependency.
|
|
319
168
|
|
|
320
169
|
General form:
|
|
321
170
|
|
|
@@ -326,89 +175,80 @@ General form:
|
|
|
326
175
|
Examples:
|
|
327
176
|
|
|
328
177
|
```txt
|
|
329
|
-
App_Service
|
|
330
178
|
App_Service$
|
|
331
|
-
|
|
332
|
-
App_Service$$_wrapLog_wrapTrace
|
|
179
|
+
App_Service__Factory$$
|
|
333
180
|
node:fs
|
|
334
|
-
npm:@humanfs/core
|
|
335
|
-
node:worker_threads
|
|
336
181
|
npm:lodash
|
|
337
182
|
```
|
|
338
183
|
|
|
339
184
|
Where:
|
|
340
185
|
|
|
341
|
-
- `
|
|
342
|
-
-
|
|
343
|
-
-
|
|
344
|
-
-
|
|
345
|
-
- wrappers modify runtime behavior
|
|
186
|
+
- `__Factory` selects a named export;
|
|
187
|
+
- `$` means singleton lifecycle;
|
|
188
|
+
- `$$` means new instance lifecycle;
|
|
189
|
+
- `node:` and `npm:` address platform-specific modules.
|
|
346
190
|
|
|
347
|
-
|
|
191
|
+
### Namespace Root
|
|
192
|
+
|
|
193
|
+
A namespace root maps a CDC prefix to a module-specifier base:
|
|
348
194
|
|
|
349
195
|
```javascript
|
|
350
|
-
|
|
196
|
+
container.addNamespaceRoot("App_", "/abs/path/to/src/App", ".mjs");
|
|
351
197
|
```
|
|
352
198
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
- `setParser(parser)`
|
|
356
|
-
- `addNamespaceRoot(prefix, target, defaultExt)`
|
|
357
|
-
- `addPreprocess(fn)`
|
|
358
|
-
- `addPostprocess(fn)`
|
|
359
|
-
- `enableLogging()`
|
|
360
|
-
- `enableTestMode()`
|
|
361
|
-
- `register(cdc, mock)`
|
|
199
|
+
This lets the container translate logical module names such as `App_Root__Factory$` into concrete ES module files or URL-based module specifiers.
|
|
362
200
|
|
|
363
|
-
|
|
201
|
+
In Node.js, that often means filesystem-backed module roots:
|
|
364
202
|
|
|
365
203
|
```javascript
|
|
366
|
-
|
|
204
|
+
container.addNamespaceRoot("App_", "/project/src/App", ".mjs");
|
|
367
205
|
```
|
|
368
206
|
|
|
369
|
-
|
|
207
|
+
In a web-oriented or isomorphic application, it can also mean URL-backed roots for browser imports:
|
|
370
208
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
209
|
+
```javascript
|
|
210
|
+
container.addNamespaceRoot("App_", "https://cdn.example.com/app", ".mjs");
|
|
211
|
+
container.addNamespaceRoot("Web_", "//cdn.example.com/web", ".mjs");
|
|
212
|
+
```
|
|
375
213
|
|
|
376
|
-
|
|
214
|
+
This keeps dependency addressing stable while allowing the same logical naming model to work across shared application code, browser-facing modules, and different runtime environments.
|
|
377
215
|
|
|
378
|
-
|
|
216
|
+
## Public API
|
|
379
217
|
|
|
380
|
-
|
|
218
|
+
Create a container:
|
|
381
219
|
|
|
382
|
-
```
|
|
383
|
-
|
|
220
|
+
```javascript
|
|
221
|
+
const container = new Container();
|
|
384
222
|
```
|
|
385
223
|
|
|
386
|
-
|
|
224
|
+
Configure it before the first `get(...)`:
|
|
387
225
|
|
|
388
|
-
-
|
|
389
|
-
-
|
|
390
|
-
-
|
|
391
|
-
-
|
|
392
|
-
-
|
|
393
|
-
|
|
394
|
-
|
|
226
|
+
- `setParser(parser)`
|
|
227
|
+
- `addNamespaceRoot(prefix, target, defaultExt)`
|
|
228
|
+
- `addPreprocess(fn)`
|
|
229
|
+
- `addPostprocess(fn)`
|
|
230
|
+
- `enableLogging()`
|
|
231
|
+
- `enableTestMode()`
|
|
232
|
+
- `register(cdc, mock)`
|
|
395
233
|
|
|
396
|
-
|
|
234
|
+
Resolve dependencies:
|
|
397
235
|
|
|
398
|
-
```
|
|
399
|
-
|
|
400
|
-
npm:@humanfs/core
|
|
236
|
+
```javascript
|
|
237
|
+
await container.get(cdc);
|
|
401
238
|
```
|
|
402
239
|
|
|
240
|
+
The container is builder-configurable until the first `get(...)`. After that point configuration is locked.
|
|
241
|
+
|
|
403
242
|
## Test Mode
|
|
404
243
|
|
|
244
|
+
Test mode allows registered mocks to be resolved before module instantiation:
|
|
245
|
+
|
|
405
246
|
```javascript
|
|
406
247
|
container.enableTestMode();
|
|
407
|
-
|
|
408
248
|
container.register("App_Service$", mockService);
|
|
409
249
|
```
|
|
410
250
|
|
|
411
|
-
|
|
251
|
+
This keeps replacement explicit and local to container configuration.
|
|
412
252
|
|
|
413
253
|
## Browser Usage
|
|
414
254
|
|
|
@@ -420,24 +260,59 @@ Mocks are resolved before module instantiation.
|
|
|
420
260
|
</script>
|
|
421
261
|
```
|
|
422
262
|
|
|
423
|
-
##
|
|
263
|
+
## LLM-Oriented Development
|
|
264
|
+
|
|
265
|
+
This package is designed for codebases where LLM agents participate in implementation and maintenance.
|
|
266
|
+
|
|
267
|
+
That affects the architecture directly. In many human-oriented JavaScript codebases, local explicitness is treated as extra ceremony. Here it is a deliberate tradeoff: dependency structure stays visible where it is needed, instead of being inferred from decorators, reflection, framework conventions, or scattered configuration.
|
|
268
|
+
|
|
269
|
+
This increases local structural surface area, but it reduces ambiguity. For LLM-driven maintenance, that makes dependency structure easier to reconstruct, edit, and verify from source code alone.
|
|
270
|
+
|
|
271
|
+
## When This Fits
|
|
272
|
+
|
|
273
|
+
This approach is a good fit when you want:
|
|
274
|
+
|
|
275
|
+
- a modular monolith with explicit component boundaries;
|
|
276
|
+
- shared JavaScript code across browser and Node.js;
|
|
277
|
+
- runtime late binding instead of static application wiring;
|
|
278
|
+
- explicit, machine-readable dependency structure;
|
|
279
|
+
- a pure JavaScript + JSDoc stack instead of TypeScript-first architecture.
|
|
280
|
+
|
|
281
|
+
## When It Probably Does Not
|
|
282
|
+
|
|
283
|
+
This approach is probably a poor fit when:
|
|
284
|
+
|
|
285
|
+
- your project is deeply committed to TypeScript-first conventions;
|
|
286
|
+
- you prefer decorator-based or framework-managed injection;
|
|
287
|
+
- your team values minimal local ceremony over explicit structural contracts;
|
|
288
|
+
- you do not need isomorphic runtime structure or late binding;
|
|
289
|
+
- the codebase is optimized only for human authorship and not for machine-assisted maintenance.
|
|
290
|
+
|
|
291
|
+
## Documentation for Agents
|
|
292
|
+
|
|
293
|
+
This package includes a machine-oriented package interface under `./ai/`.
|
|
294
|
+
|
|
295
|
+
Those files are intended for system prompts, examples, and agent consumption. They describe:
|
|
424
296
|
|
|
425
|
-
|
|
297
|
+
- container usage;
|
|
298
|
+
- dependency descriptors;
|
|
299
|
+
- CDC behavior;
|
|
300
|
+
- integration patterns.
|
|
426
301
|
|
|
427
|
-
-
|
|
428
|
-
- `ctx/docs/product/default-cdc-profile.md`
|
|
429
|
-
- `ctx/docs/architecture/cdc-profile/default/grammar.md`
|
|
430
|
-
- `ctx/docs/architecture/cdc-profile/default/transformation.md`
|
|
431
|
-
- `ctx/docs/architecture/cdc-profile/default/validation.md`
|
|
432
|
-
- `ctx/docs/code/components/container.md`
|
|
302
|
+
In other words, the package ships a human-facing README and a machine-oriented interface for agents that need to use it as a dependency.
|
|
433
303
|
|
|
434
|
-
##
|
|
304
|
+
## Further Reading
|
|
435
305
|
|
|
436
|
-
|
|
306
|
+
- Product overview: `ctx/docs/product/overview.md`
|
|
307
|
+
- Product scope and boundaries: `ctx/docs/product/scope.md`
|
|
308
|
+
- Default CDC profile and compatibility surface: `ctx/docs/product/default-cdc-profile.md`
|
|
309
|
+
- Architecture overview: `ctx/docs/architecture/overview.md`
|
|
310
|
+
- Runtime linking model: `ctx/docs/architecture/linking-model.md`
|
|
311
|
+
- Container implementation contract: `ctx/docs/code/components/container.md`
|
|
312
|
+
- Project philosophy and intended application domain: `PHILOSOPHY.md`
|
|
437
313
|
|
|
438
|
-
|
|
314
|
+
## TeqFW Context
|
|
439
315
|
|
|
440
|
-
|
|
441
|
-
- **ADSM (Agent-Driven Software Management)**
|
|
316
|
+
`@teqfw/di` is the core building block of the Tequila Framework (TeqFW).
|
|
442
317
|
|
|
443
|
-
The
|
|
318
|
+
TeqFW is aimed at building modular monolith web applications with a unified JavaScript codebase across browser and server runtimes. The method favors late binding, namespace-based structure, explicit contracts, and source artifacts that remain legible to both humans and LLM agents.
|
package/ai/AGENTS.md
CHANGED
|
@@ -6,7 +6,7 @@ Version: 20260331
|
|
|
6
6
|
|
|
7
7
|
This directory provides the **Agent Interface** of the package. It contains a compact usage-oriented description intended for LLM agents that use the package as a dependency. The documents describe how the package is applied by external code and exclude development methodology, repository organization, testing infrastructure, and other internal aspects of the project.
|
|
8
8
|
|
|
9
|
-
The package implements a **dependency
|
|
9
|
+
The package implements a **deterministic runtime dependency linker for ES modules** based on late binding. Dependencies between modules are resolved by the container using structured dependency identifiers and namespace resolution rules rather than application-level static imports. The container dynamically loads ES modules, instantiates exports according to CDC semantics, and returns linked frozen values.
|
|
10
10
|
|
|
11
11
|
## When to Use This Package
|
|
12
12
|
|
|
@@ -17,9 +17,18 @@ This package should be used when a system requires the following capabilities:
|
|
|
17
17
|
- deterministic dependency resolution
|
|
18
18
|
- controlled instantiation of components
|
|
19
19
|
- modules that remain environment-agnostic across Node.js and browser runtimes
|
|
20
|
+
- explicit machine-readable dependency structure for agent-assisted maintenance
|
|
20
21
|
|
|
21
22
|
The container serves as the **composition root** of the application. Application modules declare dependencies and receive instantiated objects from the container rather than resolving dependencies directly.
|
|
22
23
|
|
|
24
|
+
This package is primarily a fit for:
|
|
25
|
+
|
|
26
|
+
- modular monolith web applications
|
|
27
|
+
- isomorphic JavaScript systems with shared browser/server code
|
|
28
|
+
- pure JavaScript + JSDoc codebases
|
|
29
|
+
|
|
30
|
+
It is usually not a natural fit for TypeScript-first architectures built around decorators, metadata, or framework-managed DI conventions.
|
|
31
|
+
|
|
23
32
|
## Architectural Model
|
|
24
33
|
|
|
25
34
|
The system is based on four mechanisms:
|
|
@@ -42,6 +51,7 @@ In this model:
|
|
|
42
51
|
- ES modules provide implementations.
|
|
43
52
|
- CDC identifiers define dependency contracts.
|
|
44
53
|
- the container performs deterministic runtime linking.
|
|
54
|
+
- linked values are frozen before they are returned.
|
|
45
55
|
|
|
46
56
|
This mechanism separates module implementation from dependency binding and allows systems to assemble component graphs dynamically while preserving deterministic behavior.
|
|
47
57
|
|
|
@@ -59,6 +69,13 @@ Agents should read the documents in this directory in the following order:
|
|
|
59
69
|
|
|
60
70
|
This sequence reflects the intended agent workflow: contract surface first, then architectural model, operational mechanism, dependency addressing, extension points, and practical usage.
|
|
61
71
|
|
|
72
|
+
Agents that need a working integration should prefer:
|
|
73
|
+
|
|
74
|
+
1. `package-api.ts` for the programmatic surface,
|
|
75
|
+
2. `usage.md` for examples,
|
|
76
|
+
3. `dependency-id.md` for CDC details,
|
|
77
|
+
4. `container.md` for behavioral guarantees.
|
|
78
|
+
|
|
62
79
|
## Interface Scope
|
|
63
80
|
|
|
64
81
|
The documents in this directory define the supported usage semantics of the package. Behaviors not described here should be treated as undefined and should not be inferred. The interface intentionally contains only the information required to correctly apply the package in external code.
|
|
@@ -73,3 +90,5 @@ The package follows architectural principles used in the **Tequila Framework (Te
|
|
|
73
90
|
- development in modern JavaScript without compilation
|
|
74
91
|
|
|
75
92
|
Understanding the broader TeqFW ecosystem is not required to use the package, but the container follows the same architectural philosophy.
|
|
93
|
+
|
|
94
|
+
For agent usage, this directory is the package's machine-oriented interface. Prefer these documents over inferring behavior from repository-level prose.
|
package/ai/concepts.md
CHANGED
|
@@ -47,9 +47,9 @@ The identifier syntax and resolution rules are described in **dependency-id.md**
|
|
|
47
47
|
|
|
48
48
|
## Namespaces
|
|
49
49
|
|
|
50
|
-
Namespaces provide deterministic mapping between logical identifiers and module locations. Each package defines
|
|
50
|
+
Namespaces provide deterministic mapping between logical identifiers and module locations. Each package defines one or more namespace roots that map a CDC prefix to a module-specifier base.
|
|
51
51
|
|
|
52
|
-
The container resolves identifiers by applying namespace rules that translate identifier prefixes into
|
|
52
|
+
The container resolves identifiers by applying namespace rules that translate identifier prefixes into module specifiers. In Node.js this often means filesystem-backed paths. In browser-oriented or isomorphic systems this may also mean URL-based import roots.
|
|
53
53
|
|
|
54
54
|
Namespace resolution ensures:
|
|
55
55
|
|
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(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 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(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 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
|
@@ -81,12 +81,24 @@ export default class TeqFw_Di_Container_Resolve_GraphResolver {
|
|
|
81
81
|
const depsDecl = Reflect.get(namespace, '__deps__');
|
|
82
82
|
if (depsDecl === undefined) return;
|
|
83
83
|
/** @type {Record<string, unknown>} */
|
|
84
|
-
let depsMap =
|
|
85
|
-
if ((depsDecl
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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.');
|
|
90
102
|
}
|
|
91
103
|
}
|
|
92
104
|
for (const [, cdc] of Object.entries(depsMap)) {
|
package/src/Container.mjs
CHANGED
|
@@ -179,14 +179,29 @@ export default class TeqFw_Di_Container {
|
|
|
179
179
|
/** @type {unknown} */
|
|
180
180
|
const deps = Reflect.get(namespace, '__deps__');
|
|
181
181
|
if (deps === undefined) return {};
|
|
182
|
-
if ((deps
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
182
|
+
if ((deps === null) || (typeof deps !== 'object') || Array.isArray(deps)) {
|
|
183
|
+
throw new Error('__deps__ must be a plain object.');
|
|
184
|
+
}
|
|
185
|
+
const exportName = depId.exportName === null ? 'default' : depId.exportName;
|
|
186
|
+
const exportScoped = Reflect.get(/** @type {object} */ (deps), exportName);
|
|
187
|
+
if ((exportScoped !== undefined) && (exportScoped !== null) && (typeof exportScoped === 'object') && !Array.isArray(exportScoped)) {
|
|
188
|
+
const values = Object.values(/** @type {Record<string, unknown>} */ (exportScoped));
|
|
189
|
+
if (!values.every((value) => typeof value === 'string')) {
|
|
190
|
+
throw new Error('__deps__ export entries must map dependency names to CDC strings.');
|
|
191
|
+
}
|
|
192
|
+
return /** @type {Record<string, unknown>} */ (exportScoped);
|
|
193
|
+
}
|
|
194
|
+
if (exportName === 'default') {
|
|
195
|
+
const values = Object.values(/** @type {Record<string, unknown>} */ (deps));
|
|
196
|
+
if (values.every((value) => typeof value === 'string')) {
|
|
197
|
+
return /** @type {Record<string, unknown>} */ (deps);
|
|
198
|
+
}
|
|
199
|
+
if (values.every((value) => (value !== null) && (typeof value === 'object') && !Array.isArray(value))) {
|
|
200
|
+
return {};
|
|
187
201
|
}
|
|
202
|
+
throw new Error('__deps__ must be either flat or export-scoped.');
|
|
188
203
|
}
|
|
189
|
-
return
|
|
204
|
+
return {};
|
|
190
205
|
};
|
|
191
206
|
|
|
192
207
|
/**
|