@teqfw/di 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -42
- package/README.md +173 -271
- package/dist/esm.js +1 -1
- package/dist/umd.js +1 -1
- package/package.json +12 -8
- package/src/AGENTS.md +177 -0
- package/src/Config/NamespaceRegistry.mjs +210 -0
- package/src/Container/Instantiate/ExportSelector.mjs +39 -0
- package/src/Container/Instantiate/Instantiator.mjs +143 -0
- package/src/Container/Lifecycle/Registry.mjs +81 -0
- package/src/Container/Resolve/GraphResolver.mjs +119 -0
- package/src/Container/Resolver.mjs +175 -0
- package/src/Container/Wrapper/Executor.mjs +71 -0
- package/src/Container.mjs +380 -0
- package/src/Def/Parser.mjs +146 -0
- package/src/Dto/DepId.mjs +131 -0
- package/src/Dto/Resolver/Config/Namespace.mjs +48 -0
- package/src/Dto/Resolver/Config.mjs +58 -0
- package/src/Enum/Composition.mjs +11 -0
- package/src/Enum/Life.mjs +11 -0
- package/src/Enum/Platform.mjs +12 -0
- package/src/Internal/Logger.mjs +54 -0
- package/types.d.ts +58 -21
- package/src/Api/Container/Parser/Chunk.js +0 -23
- package/src/Api/Container/Parser.js +0 -28
- package/src/Api/Container/PostProcessor/Chunk.js +0 -17
- package/src/Api/Container/PostProcessor.js +0 -25
- package/src/Api/Container/PreProcessor/Chunk.js +0 -17
- package/src/Api/Container/PreProcessor.js +0 -23
- package/src/Api/Container/Resolver.js +0 -16
- package/src/Container/A/Composer/A/SpecParser.js +0 -86
- package/src/Container/A/Composer.js +0 -69
- package/src/Container/A/Parser/Chunk/Def.js +0 -69
- package/src/Container/A/Parser/Chunk/V02X.js +0 -66
- package/src/Container/Parser.js +0 -48
- package/src/Container/PostProcessor.js +0 -32
- package/src/Container/PreProcessor.js +0 -34
- package/src/Container/Resolver.js +0 -80
- package/src/Container.js +0 -187
- package/src/Defs.js +0 -22
- package/src/DepId.js +0 -52
- package/src/Pre/Replace.js +0 -80
- package/teqfw.json +0 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,45 +1,8 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## 2.0.0 - 2026-02-27
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
## 1.1.3
|
|
10
|
-
|
|
11
|
-
- Added global type declarations to `types.d.ts` to match the type map architecture rules.
|
|
12
|
-
|
|
13
|
-
## 1.1.2
|
|
14
|
-
|
|
15
|
-
- Moved architecture diagrams into `ctx/img/` and updated the context map accordingly.
|
|
16
|
-
- Clarified type map rules to require global namespace declarations for IDE type resolution.
|
|
17
|
-
|
|
18
|
-
## 1.1.1
|
|
19
|
-
|
|
20
|
-
- Added missing published files (`CHANGELOG.md`, `teqfw.json`, `types.d.ts`) and declared `types.d.ts` in `package.json`.
|
|
21
|
-
|
|
22
|
-
## 1.1.0
|
|
23
|
-
|
|
24
|
-
- Added ADSM cognitive context documentation and reporting structure under `ctx/`.
|
|
25
|
-
- Added type map documentation and a `types.d.ts` namespace-to-source mapping for IDE support.
|
|
26
|
-
- Updated `.npmignore` to ignore `output.md` artifacts.
|
|
27
|
-
|
|
28
|
-
## 1.0.2
|
|
29
|
-
|
|
30
|
-
- Added ability to import the Replace preprocessor chunk via package subpath (`./pre/replace`).
|
|
31
|
-
- Updated `.npmignore` to exclude development artifacts (`ctx/`, logs, test files) and ensure clean npm package contents.
|
|
32
|
-
- Improved ignore patterns to prevent accidental publication of internal files.
|
|
33
|
-
|
|
34
|
-
## 1.0.1
|
|
35
|
-
|
|
36
|
-
- Prepare npm package for publication.
|
|
37
|
-
- Add distribution build outputs to package files and specify entry points.
|
|
38
|
-
|
|
39
|
-
## 1.0.0
|
|
40
|
-
|
|
41
|
-
- Started changelog for version 1.0.0.
|
|
42
|
-
- Added AGENTS.md with English-only guidelines and link to PHILOSOPHY.md.
|
|
43
|
-
- Switched tests from Mocha to Node's built-in runner.
|
|
44
|
-
- Updated package scripts and removed Mocha dependency.
|
|
45
|
-
- Documented breaking changes for v1.0.0: the container can no longer access itself, configuration must occur in the Composition Root, and legacy versions live in the `forerunner` branch.
|
|
5
|
+
- Started a new changelog lineage for generation 2 of `@teqfw/di`.
|
|
6
|
+
- Replaced legacy v1 implementation with the new architecture.
|
|
7
|
+
- Promoted `src2/` to `src/` and `test2/` to `test/` as the primary code and test layout.
|
|
8
|
+
- Updated project configuration and type paths to the new directory structure.
|
package/README.md
CHANGED
|
@@ -3,360 +3,262 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
>
|
|
8
|
-
> The library has been stable for a long time and is now promoted to its first major version. To improve security, the Object Container can no longer access itself, so all configuration must occur in the Composition Root. This restriction ensures that third-party plugins cannot override or modify the container's internal functionality. Legacy versions are maintained in the `forerunner` branch, and packages like `@teqfw/core` should depend on `@teqfw/di` versions below `1.0.0`.
|
|
6
|
+
Deterministic runtime DI container for native ES modules.
|
|
9
7
|
|
|
10
|
-
`@teqfw/di`
|
|
11
|
-
|
|
12
|
-
flexibility, modularity, and easier testing for your applications.
|
|
8
|
+
`@teqfw/di` uses explicit dependency contracts (CDC strings) and module-level dependency descriptors (`__deps__`).
|
|
9
|
+
It does not infer dependencies from constructor signatures.
|
|
13
10
|
|
|
14
|
-
|
|
15
|
-
directly to their source paths for greater simplicity. However, for advanced use cases—such as unit testing—it is
|
|
16
|
-
possible to explicitly register singleton objects using the `register(depId, obj)` method (available only in test mode).
|
|
17
|
-
This allows controlled substitution of dependencies without altering the main codebase.
|
|
11
|
+
## Version Line
|
|
18
12
|
|
|
19
|
-
|
|
20
|
-
support CommonJS, AMD, UMD, or other module formats.**
|
|
13
|
+
This branch is the v2 line.
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
behavior at runtime.
|
|
15
|
+
- package version: `2.0.0`
|
|
16
|
+
- changelog starts from `2.0.0`
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
TypeScript to compose dependency identifiers in the same way as in JavaScript. It is important to ensure that TypeScript
|
|
28
|
-
transpiles the source code to ES6 modules for proper functionality. With this setup, TypeScript users can effectively
|
|
29
|
-
leverage the benefits of this library without any additional configuration.
|
|
30
|
-
|
|
31
|
-
---
|
|
32
|
-
|
|
33
|
-
## Design Philosophy
|
|
34
|
-
|
|
35
|
-
This library is a component of the **TeqFW platform**, an experimental framework grounded in the principles of modular
|
|
36
|
-
monolith design, long-term maintainability, late binding, and immutability-first logic composition.
|
|
37
|
-
|
|
38
|
-
To explore the conceptual background, see: **[TeqFW Philosophy](./PHILOSOPHY.md)**.
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
## Dependency Declaration Model
|
|
43
|
-
|
|
44
|
-
In `@teqfw/di`, dependencies are declared **exclusively in the constructor (or factory) signature**.
|
|
45
|
-
A component defines its dependencies using a **single object parameter**, where each property name
|
|
46
|
-
is a dependency identifier interpreted by the container.
|
|
47
|
-
|
|
48
|
-
The container analyzes the constructor signature, resolves all declared identifiers **before object
|
|
49
|
-
creation**, and invokes the constructor with a fully populated argument object. Created objects are isolated from the container.
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
## Samples
|
|
54
|
-
|
|
55
|
-
Explore `@teqfw/di` in action through the following demo applications:
|
|
56
|
-
|
|
57
|
-
- [demo-di-app](https://flancer64.github.io/demo-di-app/): A simple demonstration of dependency injection with
|
|
58
|
-
`@teqfw/di`.
|
|
59
|
-
- [demo-wa-esm-openai](https://github.com/flancer64/demo-wa-esm-openai): Integrates OpenAI with ES6 modules.
|
|
60
|
-
- [pwa-wallet](https://github.com/flancer64/pwa-wallet): A progressive web application wallet showcasing the library's
|
|
61
|
-
modularity.
|
|
62
|
-
- [spa-remote-console](https://github.com/flancer64/spa-remote-console): Demonstrates remote console functionality in a
|
|
63
|
-
single-page application.
|
|
64
|
-
- [demo-webauthn-pubkey](https://github.com/flancer64/demo-webauthn-pubkey): Uses Web Authentication (WebAuthn) with
|
|
65
|
-
public key credentials.
|
|
66
|
-
- [tg-bot-habr-demo-grammy](https://github.com/flancer64/tg-bot-habr-demo-grammy): A Telegram bot demo built with the
|
|
67
|
-
grammY library.
|
|
68
|
-
|
|
69
|
-
These projects offer practical examples and insights into using `@teqfw/di` effectively!
|
|
70
|
-
|
|
71
|
-
---
|
|
18
|
+
## Installation
|
|
72
19
|
|
|
73
|
-
|
|
20
|
+
```bash
|
|
21
|
+
npm install @teqfw/di
|
|
22
|
+
```
|
|
74
23
|
|
|
75
|
-
|
|
76
|
-
configuring the container, and finally retrieving the main object with injected dependencies.
|
|
24
|
+
## Quick Start
|
|
77
25
|
|
|
78
|
-
###
|
|
26
|
+
### 1. Define modules with `__deps__`
|
|
79
27
|
|
|
80
|
-
|
|
81
|
-
as the container can be configured to work with any layout (e.g., within `/home/user/project/`):
|
|
28
|
+
`src/App/Child.mjs`
|
|
82
29
|
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
./Sale.js
|
|
88
|
-
./Config.js
|
|
89
|
-
./Logger.js
|
|
90
|
-
./Main.js
|
|
30
|
+
```js
|
|
31
|
+
export default function App_Child() {
|
|
32
|
+
return { name: "child" };
|
|
33
|
+
}
|
|
91
34
|
```
|
|
92
35
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
In your code, declare dependencies by specifying them as keys in the constructor. This is the only supported
|
|
96
|
-
way to declare dependencies in `@teqfw/di`. Dependency identifiers here follow a namespace style similar to PHP
|
|
97
|
-
Zend 1, which is used by default in this library. You can also implement a custom parser if you prefer a
|
|
98
|
-
different naming convention or mapping strategy.
|
|
36
|
+
`src/App/Root.mjs`
|
|
99
37
|
|
|
100
38
|
```js
|
|
101
|
-
export
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
39
|
+
export const __deps__ = {
|
|
40
|
+
child: "App_Child$",
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default function App_Root({ child }) {
|
|
44
|
+
return {
|
|
45
|
+
name: "root",
|
|
46
|
+
child,
|
|
47
|
+
};
|
|
110
48
|
}
|
|
111
49
|
```
|
|
112
50
|
|
|
113
|
-
###
|
|
114
|
-
|
|
115
|
-
Next, set up the container and configure it to use the correct namespace and path for your dependencies:
|
|
51
|
+
### 2. Configure container in composition root
|
|
116
52
|
|
|
117
53
|
```js
|
|
54
|
+
import path from "node:path";
|
|
55
|
+
import { fileURLToPath } from "node:url";
|
|
118
56
|
import Container from "@teqfw/di";
|
|
119
57
|
|
|
120
|
-
|
|
121
|
-
const
|
|
58
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
59
|
+
const __dirname = path.dirname(__filename);
|
|
122
60
|
|
|
123
|
-
|
|
124
|
-
|
|
61
|
+
const container = new Container();
|
|
62
|
+
container.addNamespaceRoot("App_", path.resolve(__dirname, "./src/App"), ".mjs");
|
|
125
63
|
|
|
126
|
-
|
|
127
|
-
|
|
64
|
+
const root = await container.get("App_Root$");
|
|
65
|
+
console.log(root.name); // root
|
|
66
|
+
console.log(root.child.name); // child
|
|
67
|
+
console.log(Object.isFrozen(root)); // true
|
|
128
68
|
```
|
|
129
69
|
|
|
130
|
-
|
|
70
|
+
## Dependency Descriptor (`__deps__`)
|
|
131
71
|
|
|
132
|
-
|
|
72
|
+
`__deps__` is a static module export:
|
|
133
73
|
|
|
134
74
|
```js
|
|
135
|
-
|
|
136
|
-
|
|
75
|
+
export const __deps__ = {
|
|
76
|
+
localName: "Some_CDC",
|
|
77
|
+
};
|
|
137
78
|
```
|
|
138
79
|
|
|
139
|
-
|
|
80
|
+
Rules used by container runtime:
|
|
140
81
|
|
|
141
|
-
|
|
82
|
+
- if `__deps__` is missing: module has zero dependencies
|
|
83
|
+
- keys are local argument names passed into factory/class constructor
|
|
84
|
+
- values are CDC strings
|
|
85
|
+
- dependencies are resolved recursively before instantiation
|
|
142
86
|
|
|
143
|
-
|
|
87
|
+
## CDC Grammar (Default Profile)
|
|
144
88
|
|
|
145
|
-
|
|
146
|
-
`register(depId, obj)` method:
|
|
89
|
+
Surface form:
|
|
147
90
|
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
container.register("App_Service_Customer$", mockCustomerService);
|
|
91
|
+
```text
|
|
92
|
+
[PlatformPrefix]ModuleName[__ExportName][LifecycleAndWrappers]
|
|
151
93
|
```
|
|
152
94
|
|
|
153
|
-
|
|
154
|
-
logic. Overrides are allowed only in test mode, ensuring clean separation of concerns.
|
|
95
|
+
Where:
|
|
155
96
|
|
|
156
|
-
|
|
97
|
+
- `PlatformPrefix`: `node_` | `npm_` | omitted (`teq` by default)
|
|
98
|
+
- `Export segment`: `__ExportName`
|
|
99
|
+
- `Lifecycle marker`: `$` | `$$` | `$$$`
|
|
100
|
+
- `Wrappers`: `_<wrapperId>` suffixes after lifecycle marker
|
|
157
101
|
|
|
158
|
-
|
|
159
|
-
`process`. This is useful for isolating side effects and simulating system behavior:
|
|
102
|
+
Examples:
|
|
160
103
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
104
|
+
- `App_Service` - whole module (`as-is`)
|
|
105
|
+
- `App_Service$` - default export as factory with lifecycle marker
|
|
106
|
+
- `App_Service__build$$` - named export `build` with lifecycle marker
|
|
107
|
+
- `App_Service$$_wrapLog_wrapTrace` - wrapper chain in declared order
|
|
108
|
+
- `node_fs` - Node builtin
|
|
109
|
+
- `npm_lodash` - npm package
|
|
110
|
+
|
|
111
|
+
Notes:
|
|
166
112
|
|
|
167
|
-
|
|
113
|
+
- explicit `teq_` prefix is forbidden
|
|
114
|
+
- wrappers without lifecycle marker are invalid
|
|
115
|
+
- parser is deterministic and fail-fast
|
|
116
|
+
|
|
117
|
+
## Public API
|
|
168
118
|
|
|
169
119
|
```js
|
|
170
|
-
container
|
|
171
|
-
join: (...args) => args.join("/"),
|
|
172
|
-
resolve: (p) => `/abs/${p}`,
|
|
173
|
-
});
|
|
120
|
+
const container = new Container();
|
|
174
121
|
```
|
|
175
122
|
|
|
176
|
-
|
|
177
|
-
isolated, and deterministic unit tests — even for logic that relies on the filesystem or path resolution.
|
|
123
|
+
Builder stage methods (only before first `get`):
|
|
178
124
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
125
|
+
- `setParser(parser)`
|
|
126
|
+
- `addNamespaceRoot(prefix, target, defaultExt)`
|
|
127
|
+
- `addPreprocess(fn)`
|
|
128
|
+
- `addPostprocess(fn)`
|
|
129
|
+
- `enableLogging()`
|
|
130
|
+
- `enableTestMode()`
|
|
131
|
+
- `register(cdc, mock)` (only in test mode)
|
|
182
132
|
|
|
183
|
-
|
|
184
|
-
extensive flexibility and configurability. This allows the library to adapt seamlessly to a wide range of project needs.
|
|
185
|
-
Here’s what makes it stand out:
|
|
133
|
+
Resolution:
|
|
186
134
|
|
|
187
|
-
-
|
|
188
|
-
signatures. This basic functionality works out of the box but can be fully customized if needed.
|
|
135
|
+
- `await container.get(cdc)`
|
|
189
136
|
|
|
190
|
-
|
|
191
|
-
dependencies, making it easy to adapt the library to specific naming conventions or custom mapping rules.
|
|
137
|
+
Behavioral guarantees:
|
|
192
138
|
|
|
193
|
-
-
|
|
194
|
-
|
|
139
|
+
- configuration is locked after first `get`
|
|
140
|
+
- fail-fast pipeline
|
|
141
|
+
- deterministic linking under identical contracts and config
|
|
142
|
+
- produced values are frozen
|
|
143
|
+
- container enters failed state after fatal linking error
|
|
195
144
|
|
|
196
|
-
|
|
197
|
-
dependencies cannot be modified once instantiated, eliminating unintended mutations and reinforcing modular,
|
|
198
|
-
predictable application behavior.
|
|
145
|
+
## Wrappers
|
|
199
146
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
different teams may tailor dependencies to their specific requirements. The default preprocessing can also be replaced
|
|
203
|
-
to suit more precise needs.
|
|
147
|
+
Wrappers are postprocess plugins registered during container configuration.
|
|
148
|
+
They are activated by wrapper markers in the CDC string and applied to the produced value after instantiation.
|
|
204
149
|
|
|
205
|
-
|
|
206
|
-
JavaScript files with JSDoc annotations. The container supports configuring dependencies to replace interfaces with
|
|
207
|
-
project-specific implementations, offering flexibility without requiring TypeScript.
|
|
150
|
+
Wrappers:
|
|
208
151
|
|
|
209
|
-
- **
|
|
210
|
-
|
|
152
|
+
- are **container-level plugins**
|
|
153
|
+
- are **not module exports**
|
|
154
|
+
- are applied in declared order
|
|
155
|
+
- must return synchronously
|
|
156
|
+
- run before lifecycle enforcement and freeze
|
|
211
157
|
|
|
212
|
-
|
|
213
|
-
be easily customized to meet unique project demands.
|
|
158
|
+
Surface form:
|
|
214
159
|
|
|
215
|
-
|
|
160
|
+
```text
|
|
161
|
+
ModuleName$$_wrapperA_wrapperB
|
|
162
|
+
```
|
|
216
163
|
|
|
217
|
-
|
|
164
|
+
Wrappers are part of the dependency contract (CDC).
|
|
165
|
+
They declaratively modify how a resolved value behaves.
|
|
218
166
|
|
|
219
|
-
|
|
167
|
+
---
|
|
220
168
|
|
|
221
|
-
|
|
169
|
+
### Example: Logging Wrapper
|
|
222
170
|
|
|
223
|
-
|
|
224
|
-
npm install @teqfw/di
|
|
225
|
-
```
|
|
171
|
+
Suppose we want to log all method calls of a service without modifying the service itself.
|
|
226
172
|
|
|
227
|
-
|
|
173
|
+
#### Service module
|
|
228
174
|
|
|
229
175
|
```js
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
176
|
+
export default function App_Service() {
|
|
177
|
+
return {
|
|
178
|
+
sum(a, b) {
|
|
179
|
+
return a + b;
|
|
180
|
+
},
|
|
181
|
+
multiply(a, b) {
|
|
182
|
+
return a * b;
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
234
186
|
```
|
|
235
187
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
To use `@teqfw/di` in a browser environment with ES modules, include it as follows (~5KB):
|
|
239
|
-
|
|
240
|
-
```html
|
|
241
|
-
<script type="module">
|
|
242
|
-
import Container from "https://cdn.jsdelivr.net/npm/@teqfw/di@latest/+esm";
|
|
188
|
+
#### Container configuration
|
|
243
189
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
190
|
+
```js
|
|
191
|
+
container.addPostprocessWrapper("logIO", (value) => {
|
|
192
|
+
return new Proxy(value, {
|
|
193
|
+
get(target, prop, receiver) {
|
|
194
|
+
const original = Reflect.get(target, prop, receiver);
|
|
195
|
+
|
|
196
|
+
if (typeof original !== "function") {
|
|
197
|
+
return original;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return function (...args) {
|
|
201
|
+
console.log(`[CALL] ${String(prop)} ->`, args);
|
|
202
|
+
const result = original.apply(this, args);
|
|
203
|
+
console.log(`[RETURN] ${String(prop)} ->`, result);
|
|
204
|
+
return result;
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
});
|
|
247
209
|
```
|
|
248
210
|
|
|
249
|
-
|
|
211
|
+
#### Request
|
|
250
212
|
|
|
251
|
-
|
|
213
|
+
```js
|
|
214
|
+
const service = await container.get("App_Service$$_logIO");
|
|
252
215
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
/** @type {TeqFw_Di_Container} */
|
|
257
|
-
const container = new window.TeqFw_Di_Container();
|
|
258
|
-
</script>
|
|
216
|
+
service.sum(2, 3);
|
|
217
|
+
// [CALL] sum -> [2, 3]
|
|
218
|
+
// [RETURN] sum -> 5
|
|
259
219
|
```
|
|
260
220
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
## Using the Container
|
|
264
|
-
|
|
265
|
-
### In Node.js
|
|
221
|
+
The module remains unaware of logging.
|
|
222
|
+
The wrapper applies cross-cutting behavior declaratively through CDC.
|
|
266
223
|
|
|
267
|
-
|
|
268
|
-
roots to map dependency IDs to their source paths.
|
|
224
|
+
This allows:
|
|
269
225
|
|
|
270
|
-
|
|
271
|
-
|
|
226
|
+
- tracing
|
|
227
|
+
- metrics collection
|
|
228
|
+
- access control
|
|
229
|
+
- behavioral instrumentation
|
|
272
230
|
|
|
273
|
-
|
|
274
|
-
resolver.setWindowsEnv(platform === "win32"); // Adjusts for Windows environment if needed
|
|
275
|
-
resolver.addNamespaceRoot("App_", "/path/to/src");
|
|
276
|
-
```
|
|
231
|
+
without modifying business logic or module structure.
|
|
277
232
|
|
|
278
|
-
|
|
233
|
+
Wrappers therefore act as a declarative DI-level AOP mechanism.
|
|
279
234
|
|
|
280
|
-
|
|
281
|
-
const app = await container.get("App_Main$");
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
### In the Browser
|
|
285
|
-
|
|
286
|
-
1. **Configure Dependency Mapping**: Set up namespace roots to map dependency IDs to their source paths, using URLs as
|
|
287
|
-
needed.
|
|
288
|
-
|
|
289
|
-
```js
|
|
290
|
-
const resolver = container.getResolver();
|
|
291
|
-
resolver.addNamespaceRoot("App_", "https://cdn.jsdelivr.net/npm/@flancer64/demo-di-app@0.2/src");
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
2. **Retrieve Singleton Instances**: Retrieve the main application instance as a singleton asynchronously:
|
|
295
|
-
|
|
296
|
-
```js
|
|
297
|
-
const app = await container.get("App_Main$");
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
With these steps, the container is configured to automatically resolve and inject dependencies based on your setup,
|
|
301
|
-
whether in Node.js or in a browser environment.
|
|
302
|
-
|
|
303
|
-
---
|
|
304
|
-
|
|
305
|
-
## Dependency ID Types
|
|
306
|
-
|
|
307
|
-
`@teqfw/di` supports various dependency ID formats to match different import styles and object requirements. Here’s a
|
|
308
|
-
quick reference:
|
|
309
|
-
|
|
310
|
-
| Dependency ID | Import Style | Description |
|
|
311
|
-
| --------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------- |
|
|
312
|
-
| `App_Service` | `import * as Service from './App/Service.js';` | Imports the entire module as an ES module. |
|
|
313
|
-
| `App_Service.default` | `import {default} from './App/Service.js';` | Imports the default export as-is. |
|
|
314
|
-
| `App_Service.name` | `import {name} from './App/Service.js';` | Imports a named export as-is. |
|
|
315
|
-
| `App_Service$` | `import {default as Factory} from './App/Service.js';` | Uses default export as a singleton for the container. |
|
|
316
|
-
| `App_Service$$` | `import {default as Factory} from './App/Service.js';` | Creates a new instance from the default export for each dependency. |
|
|
317
|
-
| `App_Service.name$` | `import {name} from './App/Service.js';` | Uses a named export as a singleton. |
|
|
318
|
-
| `App_Service.name$$` | `import {name} from './App/Service.js';` | Creates a new instance from a named export for each dependency. |
|
|
319
|
-
| `App_Service.name$$(proxy)` | `import {name} from './App/Service.js';` | Applies a custom wrapper to the created object in postprocessing, using a handler function `proxy()`. |
|
|
320
|
-
|
|
321
|
-
### Example Usage
|
|
322
|
-
|
|
323
|
-
Here’s an example showing a class with multiple dependencies, each using different dependency IDs:
|
|
235
|
+
## Test Mode and Mocks
|
|
324
236
|
|
|
325
237
|
```js
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
App_Service: EsModule,
|
|
329
|
-
"App_Service.default": defaultExportAsIs,
|
|
330
|
-
"App_Service.name": namedExportAsIs,
|
|
331
|
-
App_Service$: defaultExportAsSingleton,
|
|
332
|
-
App_Service$$: defaultExportAsInstance,
|
|
333
|
-
"App_Service.name$": namedExportAsSingleton,
|
|
334
|
-
"App_Service.name$$": namedExportAsInstance,
|
|
335
|
-
"App_Service.name(factory)": factoryToCreateInstancesFromNamedExport,
|
|
336
|
-
}) {
|
|
337
|
-
const { default: SrvDef, name: SrvName } = EsModule; // Deconstruct the module and access the exports
|
|
338
|
-
}
|
|
339
|
-
}
|
|
238
|
+
container.enableTestMode();
|
|
239
|
+
container.register("App_Service$", { name: "mock-service" });
|
|
340
240
|
```
|
|
341
241
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
## Summary
|
|
242
|
+
Mock lookup uses canonical parsed dependency identity and is applied before resolver/instantiation.
|
|
345
243
|
|
|
346
|
-
|
|
347
|
-
With its flexible dependency mapping, customizable ID configurations, and support for dynamic object creation,
|
|
348
|
-
`@teqfw/di` empowers developers to build modular, testable, and scalable codebases.
|
|
244
|
+
## Browser Usage
|
|
349
245
|
|
|
350
|
-
|
|
351
|
-
functionality that you can further adapt to fit your project’s unique requirements. Feel free to explore and extend the
|
|
352
|
-
library as needed to create your ideal development environment.
|
|
246
|
+
ESM via jsDelivr:
|
|
353
247
|
|
|
354
|
-
|
|
355
|
-
|
|
248
|
+
```html
|
|
249
|
+
<script type="module">
|
|
250
|
+
import Container from "https://cdn.jsdelivr.net/npm/@teqfw/di@2/+esm";
|
|
251
|
+
const container = new Container();
|
|
252
|
+
</script>
|
|
253
|
+
```
|
|
356
254
|
|
|
357
|
-
|
|
358
|
-
- **LinkedIn**: [LinkedIn Profile](https://www.linkedin.com/in/aleksandrs-gusevs-011ba928/)
|
|
255
|
+
## Documentation Source
|
|
359
256
|
|
|
360
|
-
|
|
257
|
+
Normative docs live in `ctx/`:
|
|
361
258
|
|
|
362
|
-
|
|
259
|
+
- product overview: `ctx/docs/product/overview.md`
|
|
260
|
+
- default CDC profile: `ctx/docs/product/default-cdc-profile.md`
|
|
261
|
+
- grammar: `ctx/docs/architecture/cdc-profile/default/grammar.md`
|
|
262
|
+
- transformation: `ctx/docs/architecture/cdc-profile/default/transformation.md`
|
|
263
|
+
- validation: `ctx/docs/architecture/cdc-profile/default/validation.md`
|
|
264
|
+
- container contract: `ctx/docs/code/components/container.md`
|
package/dist/esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={CA:"A",CF:"F",LI:"I",LS:"S",isClass(e){const t=Object.getOwnPropertyDescriptor(e,"prototype");return t&&!t.writable}};const t=/(function)*\s*\w*\s*\(\s*\{([^}]*)}/s,o=/constructor\s*\(\s*\{([^}]*)}/s;function s(e){const t=[];try{const o=new Function(`{${e}}`,"return");o(new Proxy({},{get:(e,o)=>t.push(o)}))}catch(t){throw new Error(`Cannot analyze the deps specification:${e}\n\nPlease, be sure that spec does not contain extra ')' in a comments.\n\nError: ${t}`)}return t}function n(n){return"function"==typeof n?e.isClass(n)?function(e){const t=[],n=e.toString(),r=o.exec(n);return r&&t.push(...s(r[1])),t}(n):function(e){const o=[],n=e.toString(),r=t.exec(n);return r&&o.push(...s(r[2])),o}(n):[]}class r{constructor(){let t=!1;this.create=async function(o,s,r,i){if(r.includes(o.origin))throw new Error(`Circular dependency for '${o.origin}'. Parents are: ${JSON.stringify(r)}`);if(o.exportName){const a=[...r,o.origin],{[o.exportName]:l}=s;if(o.composition===e.CF){if("function"==typeof l){const s=n(l);s.length&&(c=`Deps for object '${o.origin}' are: ${JSON.stringify(s)}`,t&&console.log(c));const r={};for(const e of s)r[e]=await i.get(e,a);const u=e.isClass(l)?new l(r):l(r);return u instanceof Promise?await u:u}return Object.assign({},l)}return l}return s;var c},this.setDebug=function(e){t=e}}}class i{exportName;composition;isNodeModule;life;moduleName;origin;wrappers=[]}const c=/^(node:)?(@?[A-Za-z0-9_-]+\/?[A-Za-z0-9_-]*)(([.#])?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?$/;class a{canParse(){return!0}parse(t){const o=new i;o.origin=t;const s=c.exec(t);if(s&&(o.isNodeModule=Boolean(s[1]),o.moduleName=s[2].replace(/^node:/,""),"."===s[4]||"#"===s[4]?"$"===s[6]||"$$"===s[6]?(o.composition=e.CF,o.exportName=s[5],o.life="$"===s[6]?e.LS:e.LI):(o.composition=e.CA,o.life=e.LS,o.exportName=""!==s[5]?s[5]:"default"):"$"===s[6]||"$$"===s[6]?(o.composition=e.CF,o.exportName="default",o.life="$"===s[6]?e.LS:e.LI):(o.composition=void 0,o.exportName=void 0,o.life=void 0),s[10]&&(o.wrappers=s[10].split(","))),o.composition===e.CA&&o.life===e.LI)throw new Error(`Export is not a function and should be used as a singleton only: '${o.origin}'.`);return o}}class l{constructor(){let e=new a;const t=[];this.addChunk=function(e){t.push(e)},this.parse=function(o){let s;for(const e of t)if(e.canParse(o)){s=e.parse(o);break}return s||(s=e?.parse(o)),s},this.setDefaultChunk=function(t){e=t}}}class u{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=function(t,o){let s=t;for(const n of e)s=n.modify(s,t,o);return s}}}class f{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=async function(t,o,s){let n=t;for(const t of e)n=t.modify(n,o,s),n instanceof Promise&&(n=await n);return n}}}const d="ext",p="ns",h="root";class m{constructor(){const e={};let t=!1,o=[],s="/";this.addNamespaceRoot=function(s,n,r){const i=(t?n.replace(/^\\/,""):n).replace(/\\/g,"/"),c=t?`file://${i}`:i;e[s]={[d]:r??"js",[p]:s,[h]:c},o=Object.keys(e).sort((e,t)=>t.localeCompare(e))},this.resolve=function(t){let n,r,i;for(i of o)if(t.startsWith(i)){n=e[i][h],r=e[i].ext;break}if(n&&r){let e=t.replace(i,"");0===e.indexOf("_")&&(e=e.replace("_",""));const o=e.replaceAll("_",s);return`${n}${s}${o}.${r}`}return t},this.setWindowsEnv=function(e=!0){t=e,s=e?"\\":"/"}}}function g(e){return`${e.moduleName}#${e.exportName}`}class ${constructor(){let t=new r,o=!1,s=new l,n=new f,i=new u,c=!1;const a={},d={},p={};let h=new m;function $(){o&&console.log(...arguments)}this.get=async function(o,r=[]){$(`Object '${o}' is requested.`);const l=s.parse(o),u=i.modify(l,r);if(u.life===e.LS){const e=g(u);if(d[e])return $(`Existing singleton '${e}' is returned.`),d[e]}if(u.isNodeModule&&c){const e=u.origin;if(p[e])return $(`Existing nodejs lib '${e}' is returned.`),p[e]}let f;a[u.moduleName]||($(`ES6 module '${u.moduleName}' is not resolved yet`),a[u.moduleName]=h.resolve(u.moduleName));const m=a[u.moduleName];try{f=await import(m),$(`ES6 module '${u.moduleName}' is loaded from '${m}'.`)}catch(e){throw console.error(e?.message,`Object key: "${o}".`,`Path: "${m}".`,`Stack: ${JSON.stringify(r)}`),e}let w=await t.create(u,f,r,this);var y;if(null===(y=w)||"object"!=typeof y&&"function"!=typeof y||"[object Module]"===Object.prototype.toString.call(y)||Object.isFrozen(y)||Object.freeze(w),w=await n.modify(w,u,r),$(`Object '${o}' is created.`),u.life===e.LS){const e=g(u);d[e]=w,$(`Object '${o}' is saved as singleton.`)}return w},this.enableTestMode=function(){c=!0,$("Test mode enabled")},this.getParser=()=>s,this.getPreProcessor=()=>i,this.getPostProcessor=()=>n,this.getResolver=()=>h,this.register=function(t,o){if(!c)throw new Error("Use enableTestMode() to allow it");if(!t||!o)throw new Error("Both params are required");const n=s.parse(t);if(n.life!==e.LS&&!n.isNodeModule)throw new Error(`Only node modules & singletons can be registered: '${t}'`);if(n.life===e.LS){const e=g(n);if(d[e])throw new Error(`'${t}' is already registered`);d[e]=o}else if(n.isNodeModule){const e=n.origin;if(p[e])throw new Error(`'${t}' is already registered`);p[e]=o}$(`'${t}' is registered`)},this.setDebug=function(e){o=e,t.setDebug(e)},this.setParser=e=>s=e,this.setPreProcessor=e=>i=e,this.setPostProcessor=e=>n=e,this.setResolver=e=>h=e}}export{$ as default};
|
|
1
|
+
const e={AS_IS:"A",FACTORY:"F"},t={SINGLETON:"S",TRANSIENT:"T"},o={TEQ:"teq",NODE:"node",NPM:"npm"},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,t){const o=e&&"object"==typeof e?e:{},c=new l;c.moduleName="string"==typeof o.moduleName?o.moduleName:"";const p="string"==typeof o.platform?o.platform:void 0;c.platform=p&&i.has(p)?p:r;let f=null;null===o.exportName?f=null:"string"==typeof o.exportName&&(f=o.exportName),c.exportName=f;const u="string"==typeof o.composition?o.composition:void 0;c.composition=u&&s.has(u)?u:n;const m="string"==typeof o.life?o.life:void 0;return c.life=m&&a.has(m)?m:null,c.wrappers=Array.isArray(o.wrappers)?o.wrappers.filter(e=>"string"==typeof e):[],c.origin="string"==typeof o.origin?o.origin:"",!0===t?.immutable&&(Object.freeze(c.wrappers),Object.freeze(c)),c}};class p{constructor(){const r=new c;let n=null;this.parse=function(i){if(n&&n.log(`Parser.parse: input='${i}'.`),"string"!=typeof i)throw new Error("CDC must be a string.");if(0===i.length)throw new Error("CDC must be non-empty.");if(!/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(i))throw new Error("CDC must satisfy AsciiCdcIdentifier.");const s=i;let a=i,l=o.TEQ;if(a.startsWith("node_"))l=o.NODE,a=a.slice(5);else if(a.startsWith("npm_"))l=o.NPM,a=a.slice(4);else if(a.startsWith("teq_"))throw new Error("Explicit teq_ prefix is forbidden.");if(0===a.length)throw new Error("moduleName must be non-empty.");const c=a.match(/(\${1,3})(?:_([A-Za-z0-9]+(?:_[A-Za-z0-9]+)*))?$/);let p=null,f=[],u=a;if(c){const e=c[1],o=c[2];if("$"===e)p=t.SINGLETON;else{if("$$"!==e&&"$$$"!==e)throw new Error("Lifecycle marker overflow.");p=t.TRANSIENT}if(u=a.slice(0,c.index),o){f=o.split("_");for(const e of f){if(!e)throw new Error("Wrapper must be non-empty.");if(e.includes("$"))throw new Error("Wrapper must not contain $.");if(e.includes("_"))throw new Error("Wrapper must not contain _.")}}}else{if(a.includes("$"))throw new Error("Invalid lifecycle marker.");if(a.match(/(?:^|[^_])_([a-z][A-Za-z0-9]*)$/))throw new Error("Wrapper without lifecycle is forbidden.")}if(u.includes("$$$$"))throw new Error("Lifecycle marker overflow.");const m=u.indexOf("__"),d=u.lastIndexOf("__");if(-1!==m&&m!==d)throw new Error("Export delimiter must appear at most once.");if(u.startsWith("__")||u.endsWith("__"))throw new Error("Malformed export segment.");let g=u,w=null;if(-1!==m){if(g=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(!g)throw new Error("moduleName must be non-empty.");if(g.startsWith("_")||g.startsWith("$"))throw new Error("moduleName must not start with _ or $.");if(g.includes("__"))throw new Error("moduleName must not contain __.");if(g.includes("$"))throw new Error("moduleName must not contain $.");let h=e.AS_IS;null!==w?h=e.FACTORY:p===t.SINGLETON?(w="default",h=e.FACTORY):p===t.TRANSIENT&&(h=e.FACTORY,null===w&&(w="default"));const $=r.create({moduleName:g,platform:l,exportName:w,composition:h,life:p,wrappers:f,origin:s},{immutable:!0});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,t){const o=e&&"object"==typeof e?e:{},r=t&&"object"==typeof t?t:{},n=new f;return n.prefix="string"==typeof o.prefix?o.prefix:void 0,n.target="string"==typeof o.target?o.target:void 0,n.defaultExt="string"==typeof o.defaultExt?o.defaultExt:void 0,!0===r.immutable&&Object.freeze(n),n}};class m{namespaces;nodeModulesRoot}class d{constructor(){const e=new u;this.create=function(t,o){const r=t&&"object"==typeof t?t:{},n=o&&"object"==typeof o?o:{},i=new m,s=Array.isArray(r.namespaces)?r.namespaces:[];return i.namespaces=s.map(t=>e.create(t,n)),i.nodeModulesRoot="string"==typeof r.nodeModulesRoot?r.nodeModulesRoot:void 0,!0===n.immutable&&(Object.freeze(i.namespaces),Object.freeze(i)),i}}}class g{constructor({config:e,importFn:t=e=>import(e),logger:o=null}){const r=new Map,n=e;let i;const s=t,a=o,l=function(e,t){if("node"===e){const e=`node:${t}`;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("npm"===e){const e=t;return a&&a.log(`Resolver.specifier: module='${t}' -> '${e}'.`),e}if("teq"!==e)throw new Error(`Unsupported platform: ${e}`);const o=function(e){let t=null,o=-1;const r=i.namespaces;for(const n of r){const r=e.startsWith(n.prefix);a&&a.log(`Resolver.namespace: prefix='${n.prefix}' match=${String(r)} module='${e}'.`),r&&n.prefix.length>o&&(t=n,o=n.prefix.length)}if(!t)throw new Error(`Namespace rule is not found for '${e}'.`);return t}(t),r=t.slice(o.prefix.length).split("_").join("/"),n=(s=r,(l=o.defaultExt)?s.endsWith(l)?s:`${s}${l}`:s);var s,l;const c=function(e,t){return e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}(o.target,n);return a&&a.log(`Resolver.specifier: module='${t}' -> '${c}'.`),c};this.resolve=async function(e){var t;await Promise.resolve(),i||(i={nodeModulesRoot:(t=n).nodeModulesRoot,namespaces:t.namespaces.map(e=>({prefix:e.prefix,target:e.target,defaultExt:e.defaultExt}))});const o=e.platform,c=e.moduleName,p=`${o}::${c}`;if(r.has(p))return a&&a.log(`Resolver.cache: hit key='${p}'.`),r.get(p);a&&a.log(`Resolver.cache: miss key='${p}'.`);const f=(async()=>{const e=l(o,c);return a&&a.log(`Resolver.import: '${e}'.`),s(e)})();r.set(p,f);try{return await f}catch(e){throw r.delete(p),a&&a.error(`Resolver.cache: evict key='${p}' after failure.`,e),e}}}}class w{constructor({parser:e,resolver:t,logger:o=null}){const r=o,n=async function(o,i,s,a){const l=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")}(o);if(s.has(l)){const e=[...a,l].join(" -> ");throw new Error(`Cyclic dependency detected: ${e}`)}const c=function(e){return`${e.platform}::${e.moduleName}`}(o);if(!i.has(c)){s.add(l),a.push(l);try{const l=await t.resolve(o);r&&r.log(`GraphResolver.walk: resolved '${c}'.`),i.set(c,{depId:o,namespace:l});const p=Reflect.get(l,"__deps__");if(void 0===p)return;const f=p;for(const[,t]of Object.entries(f)){const o=t,l=e.parse(o);r&&r.log(`GraphResolver.walk: edge '${c}' -> '${l.platform}::${l.moduleName}'.`),await n(l,i,s,a)}}finally{a.pop(),s.delete(l)}}};this.resolve=async function(e){const t=new Map,o=new Set;return await n(e,t,o,[]),t}}}class h{constructor(){this.instantiate=function(t,o,r){const n=function(e,t){if(null===e.exportName)return t;if(!(e.exportName in t))throw new Error(`Export '${e.exportName}' is not found in module namespace.`);return t[e.exportName]}(t,o);if(t.composition===e.AS_IS)return n;if(t.composition===e.FACTORY){if("function"!=typeof n)throw new Error("Factory composition requires a callable export.");const e=n;let t;if(function(e){try{return Reflect.construct(String,[],e),!0}catch{return!1}}(e)){t=new e(r)}else{t=e(r)}if(function(e){if(null==e)return!1;const t=typeof e;return("object"===t||"function"===t)&&"function"==typeof e.then}(t))throw new Error("Factory composition must return synchronously (non-thenable).");return t}throw new Error(`Unsupported composition mode: ${String(t.composition)}.`)}}}class ${constructor(o=null){const r=new Map,n=o;this.apply=function(o,i){if(o.composition!==e.FACTORY)return n&&n.log(`Lifecycle.apply: composition='${o.composition}' cache=skip.`),i();if(o.life===t.TRANSIENT)return n&&n.log("Lifecycle.apply: transient cache=skip."),i();if(o.life===t.SINGLETON){const e=function(e){const t=Array.isArray(e.wrappers)?e.wrappers.join("|"):"";return[e.platform,e.moduleName,null===e.exportName?"":e.exportName,e.composition,null===e.life?"":e.life,t].join("::")}(o);if(r.has(e))return n&&n.log(`Lifecycle.cache: hit key='${e}'.`),r.get(e);n&&n.log(`Lifecycle.cache: miss key='${e}', create.`);const t=i();return r.set(e,t),n&&n.log(`Lifecycle.cache: stored key='${e}'.`),t}return n&&n.log("Lifecycle.apply: no lifecycle marker cache=skip."),i()}}}class y{constructor(){const e=function(e){if(null==e)return!1;const t=typeof e;if("object"!==t&&"function"!==t)return!1;return"function"==typeof e.then},t=function(e){return"function"==typeof e};this.execute=function(o,r,n){let i=r;const s=o.wrappers;for(const o of s){if(!(o in n))throw new Error(`Wrapper '${o}' is not found in module namespace.`);const r=n[o];if(!t(r))throw new Error(`Wrapper '${o}' must be callable.`);if(i=r(i),e(i))throw new Error(`Wrapper '${o}' must return synchronously (non-thenable).`)}return i}}}class N{constructor(e="teqfw/di"){const t=`[${e}]`;this.log=function(e){console.debug(`${t} ${e}`)},this.error=function(e,o){console.error(`${t} ${e}`),o instanceof Error?console.error(o.stack??o.message):void 0!==o&&console.error(o)}}}const E=Object.freeze({log(){},error(){}});class x{constructor(){let e="notConfigured";const t=[],o=[],r=[],n=new Map;let i=!1,s=!1,a=new p;const l=new d;let c,f,u,m=E;const x=new h,C=new y,b=function(e){return`${e.platform}::${e.moduleName}`},_=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("::")},v=function(e){if(null==e)return e;const t=typeof e;return"object"!==t&&"function"!==t||"[object Module]"===Object.prototype.toString.call(e)||Object.isFrozen(e)||Object.freeze(e),e},I=function(){if("notConfigured"!==e)throw new Error("Container configuration is locked.")},j=function(e){s&&m.log(`Container.builder: ${e}`)};this.addPreprocess=function(e){I(),j("addPreprocess()."),t.push(e)},this.addPostprocess=function(e){I(),j("addPostprocess()."),o.push(e)},this.setParser=function(e){I(),a=e,"function"==typeof a.setLogger&&a.setLogger(s?m:null),j("setParser().")},this.addNamespaceRoot=function(e,t,o){I(),j(`addNamespaceRoot('${e}').`),r.push({prefix:e,target:t,defaultExt:o})},this.enableTestMode=function(){I(),j("enableTestMode()."),i=!0},this.enableLogging=function(){I(),s||(s=!0,m=new N,"function"==typeof a.setLogger&&a.setLogger(m),m.log("Container.builder: enableLogging()."))},this.register=function(e,t){if(I(),j(`register('${e}').`),!0!==i)throw new Error("Container test mode is disabled.");const o=a.parse(e);n.set(_(o),t)},this.get=async function(s){if("failed"===e)throw m.error(`Container.get: rejected in failed state cdc='${s}'.`),new Error("Container is in failed state.");let p="start";try{m.log(`Container.get: cdc='${s}'.`),function(){if("notConfigured"!==e)return;m.log("Container.transition: notConfigured -> operational."),e="operational";const t=l.create({namespaces:r},{immutable:!0});"function"==typeof a.setLogger&&a.setLogger(m),c=new g({config:t,logger:m}),f=new w({parser:a,resolver:c,logger:m}),u=new $(m)}(),m.log(`Container.state: '${e}'.`),p="parse",m.log("Container.pipeline: parse:entry.");const d=a.parse(s);m.log(`Container.pipeline: parse:exit '${d.platform}::${d.moduleName}'.`),p="preprocess",m.log("Container.pipeline: preprocess:entry.");const h=function(e){let o=e;for(const e of t)o=e(o);return o}(d);if(m.log(`Container.pipeline: preprocess:exit '${h.platform}::${h.moduleName}'.`),!0===i){p="mock",m.log("Container.pipeline: mock-lookup:entry.");const e=_(h);if(n.has(e)){m.log(`Container.pipeline: mock-lookup:hit '${e}'.`),p="freeze",m.log("Container.pipeline: freeze:entry.");const t=v(n.get(e));return m.log("Container.pipeline: freeze:exit."),m.log("Container.pipeline: return:success."),t}m.log(`Container.pipeline: mock-lookup:miss '${e}'.`)}else m.log("Container.pipeline: mock-lookup:disabled.");p="resolve",m.log("Container.pipeline: resolve:entry.");const y=await f.resolve(h);m.log(`Container.pipeline: resolve:exit nodes=${y.size}.`);const N=new Map,E=function(e){if(N.has(e))return N.get(e);if(!y.has(e))throw new Error(`Resolved graph node is missing for '${e}'.`);const t=y.get(e);p="lifecycle",m.log(`Container.pipeline: lifecycle:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const r=u.apply(t.depId,function(){p="instantiate",m.log(`Container.pipeline: instantiate:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const e={},r=function(e){const t=Reflect.get(e,"__deps__");return void 0===t?{}:t}(t.namespace);for(const[t,o]of Object.entries(r)){const r=o,n=a.parse(r);e[t]=E(b(n))}const n=x.instantiate(t.depId,t.namespace,e);m.log(`Container.pipeline: instantiate:exit '${t.depId.platform}::${t.depId.moduleName}'.`),p="postprocess",m.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);m.log(`Container.pipeline: postprocess:exit '${t.depId.platform}::${t.depId.moduleName}'.`);const s=C.execute(t.depId,i,t.namespace);p="freeze",m.log(`Container.pipeline: freeze:entry '${t.depId.platform}::${t.depId.moduleName}'.`);const l=v(s);return m.log(`Container.pipeline: freeze:exit '${t.depId.platform}::${t.depId.moduleName}'.`),l});return m.log(`Container.pipeline: lifecycle:exit '${t.depId.platform}::${t.depId.moduleName}'.`),N.set(e,r),r},I=E(b(h));return m.log("Container.pipeline: return:success."),I}catch(t){throw m.error(`Container.pipeline: failed at stage='${p}'.`,t),m.log("Container.transition: operational -> failed."),e="failed",t}}}}export{x as default};
|