@teqfw/di 0.35.0 → 1.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.
@@ -0,0 +1,48 @@
1
+ name: npm publication
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ release:
6
+ types: [ created ]
7
+
8
+ jobs:
9
+ build:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ # Checkout repository code
13
+ - uses: actions/checkout@v4
14
+
15
+ # Setup Node.js environment
16
+ - uses: actions/setup-node@v4
17
+ with:
18
+ node-version: 20
19
+
20
+ # Install dependencies using package-lock.json
21
+ - run: npm ci
22
+
23
+ # Run unit tests
24
+ - run: npm run test
25
+
26
+ publish-npm:
27
+ needs: build
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ # Checkout repository code again for a separate job
31
+ - uses: actions/checkout@v4
32
+
33
+ # Setup Node.js and configure npm registry
34
+ - uses: actions/setup-node@v4
35
+ with:
36
+ node-version: 20
37
+ registry-url: https://registry.npmjs.org/
38
+
39
+ # Install dependencies again
40
+ - run: npm ci
41
+
42
+ # Build a code for browsers
43
+ - run: npm run rollup
44
+
45
+ # Publish package to npm registry with authentication token
46
+ - run: npm publish --access public
47
+ env:
48
+ NODE_AUTH_TOKEN: ${{ secrets.npm_token }}
package/AGENTS.md ADDED
@@ -0,0 +1,6 @@
1
+ # AGENTS
2
+
3
+ - Write all code comments, documentation, commit messages, and changelog entries in English.
4
+ - Record every project change in `CHANGELOG.md` under a new version `X.Y.Z` (the exact number is assigned at release).
5
+ - For the platform philosophy behind this library, see [PHILOSOPHY.md](./PHILOSOPHY.md).
6
+
package/CHANGELOG.md ADDED
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+ - Started changelog for version 1.0.0.
5
+ - Added AGENTS.md with English-only guidelines and link to PHILOSOPHY.md.
6
+ - Switched tests from Mocha to Node's built-in runner.
7
+ - Updated package scripts and removed Mocha dependency.
8
+ - 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.
9
+
package/README.md CHANGED
@@ -3,6 +3,11 @@
3
3
  ![npms.io](https://img.shields.io/npm/dm/@teqfw/di)
4
4
  ![jsdelivr](https://img.shields.io/jsdelivr/npm/hm/@teqfw/di)
5
5
 
6
+ > [!IMPORTANT]
7
+ > **Breaking Changes in v1.0.0**
8
+ >
9
+ > 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`.
10
+
6
11
  `@teqfw/di` is a lightweight dependency injection container for standard JavaScript, enabling late binding of code
7
12
  objects with minimal manual configuration. It integrates smoothly in both browser and Node.js environments, supporting
8
13
  flexibility, modularity, and easier testing for your applications.
@@ -124,7 +129,7 @@ const app = await container.get('App_Main$');
124
129
 
125
130
  ## Test Mode Support
126
131
 
127
- `@teqfw/di` supports a dedicated **test mode** to help with unit testing and dependency mocking.
132
+ `@teqfw/di` supports a dedicated **test mode** to facilitate unit testing and dependency mocking.
128
133
 
129
134
  When test mode is enabled via `container.enableTestMode()`, you can manually register singleton dependencies using the
130
135
  `register(depId, obj)` method:
@@ -134,9 +139,31 @@ container.enableTestMode();
134
139
  container.register('App_Service_Customer$', mockCustomerService);
135
140
  ```
136
141
 
137
- This makes it easy to substitute real implementations with mocks or stubs during tests, without affecting production
138
- logic. Test mode safeguards this capability, ensuring that manual overrides are only permitted in designated test
139
- environments.
142
+ This makes it easy to substitute real implementations with mocks or stubs during tests, without altering production
143
+ logic. Overrides are allowed only in test mode, ensuring clean separation of concerns.
144
+
145
+ ### Mocking Node.js Built-in Modules
146
+
147
+ A powerful feature of `@teqfw/di` is the ability to mock **Node.js built-in libraries** such as `fs`, `path`, or
148
+ `process`. This is useful for isolating side effects and simulating system behavior:
149
+
150
+ ```js
151
+ container.register('node:fs', {
152
+ existsSync: (path) => path.endsWith('.html'),
153
+ });
154
+ ```
155
+
156
+ You can also register mocks for custom logic or environment-specific behavior:
157
+
158
+ ```js
159
+ container.register('node:path', {
160
+ join: (...args) => args.join('/'),
161
+ resolve: (p) => `/abs/${p}`,
162
+ });
163
+ ```
164
+
165
+ These mocks are injected transparently wherever such modules are used as dependencies, making it possible to write pure,
166
+ isolated, and deterministic unit tests — even for logic that relies on the filesystem or path resolution.
140
167
 
141
168
  ---
142
169
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teqfw/di",
3
- "version": "0.35.0",
3
+ "version": "1.0.0",
4
4
  "description": "Dependency Injection container for ES6 modules that works in both browser and Node.js apps.",
5
5
  "keywords": [
6
6
  "dependency injection",
@@ -29,18 +29,20 @@
29
29
  "scripts": {
30
30
  "rollup": "rollup -c",
31
31
  "eslint": "eslint './src/**/*.js'",
32
- "test": "mocha --recursive './test/**/*.test.mjs'"
32
+ "test": "node --test"
33
33
  },
34
34
  "devDependencies": {
35
- "@eslint/js": "^9.25.0",
35
+ "@eslint/js": "^9.33.0",
36
36
  "@rollup/plugin-node-resolve": "^16.0.1",
37
37
  "@rollup/plugin-terser": "^0.4.4",
38
- "eslint": "^9.25.0",
39
- "mocha": "^11.1.0",
40
- "rollup": "^4.36.0"
38
+ "eslint": "^9.33.0",
39
+ "rollup": "^4.47.1"
41
40
  },
42
- "mocha": {
43
- "spec": "./test/**/*.test.mjs",
44
- "timeout": 5000
41
+ "engines": {
42
+ "node": ">=20"
43
+ },
44
+ "funding": {
45
+ "type": "individual",
46
+ "url": "https://github.com/sponsors/teqfw"
45
47
  }
46
48
  }
package/src/Container.js CHANGED
@@ -76,11 +76,6 @@ export default class TeqFw_Di_Container {
76
76
 
77
77
  this.get = async function (depId, stack = []) {
78
78
  log(`Object '${depId}' is requested.`);
79
- // return the container itself if requested
80
- if ((depId === Defs.ID) || (depId === Defs.ID_FQN)) {
81
- log('Container itself is returned.');
82
- return _regSingles[Defs.ID];
83
- }
84
79
  // parse the `objectKey` and get the structured DTO
85
80
  const parsed = _parser.parse(depId);
86
81
  // modify the original key according to some rules (replacements, etc.)
@@ -188,8 +183,5 @@ export default class TeqFw_Di_Container {
188
183
  this.setPostProcessor = (data) => _postProcessor = data;
189
184
 
190
185
  this.setResolver = (data) => _resolver = data;
191
-
192
- // MAIN
193
- _regSingles[Defs.ID] = this;
194
186
  }
195
187
  };
package/src/Defs.js CHANGED
@@ -5,9 +5,6 @@
5
5
  export default {
6
6
  CA: 'A', // composition: as-is
7
7
  CF: 'F', // composition: factory
8
- // TODO: we don't need an access to the container itself.
9
- ID: 'container', // default ID for container itself
10
- ID_FQN: 'TeqFw_Di_Container$', // default Full Qualified Name for container itself
11
8
  LI: 'I', // lifestyle: instance
12
9
  LS: 'S', // lifestyle: singleton
13
10
 
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Pre-processor chunk that replaces module addresses during dependency resolution.
3
+ *
4
+ * This chunk allows dynamically overriding the module namespace used to resolve source code
5
+ * for a given dependency ID. Only the `moduleName` field of the `depId` is modified.
6
+ * All other metadata (such as lifestyle, export type, etc.) remains unchanged.
7
+ *
8
+ * This mechanism enables redirecting from one module implementation to another
9
+ * without changing aliases or component registration.
10
+ *
11
+ * Example:
12
+ * Replace module path 'Fl32_Cms_Back_Api_Adapter' with 'App_Cms_Adapter_Custom',
13
+ * while keeping the lifestyle and export configuration intact.
14
+ *
15
+ * @implements {TeqFw_Di_Api_Container_PreProcessor_Chunk}
16
+ */
17
+ export default class TeqFw_Di_Pre_Replace {
18
+
19
+ constructor() {
20
+ // VARS
21
+
22
+ /**
23
+ * Mapping of source module namespaces to their replacements.
24
+ * Keys and values are strings without export/lifestyle suffixes.
25
+ *
26
+ * Example:
27
+ * {
28
+ * 'Fl32_Cms_Back_Api_Adapter': 'App_Cms_Adapter_Custom'
29
+ * }
30
+ *
31
+ * This means: when resolving a module with `moduleName === 'Fl32_Cms_Back_Api_Adapter'`,
32
+ * use source code from `'App_Cms_Adapter_Custom'` instead.
33
+ *
34
+ * @type {Object<string, string>}
35
+ */
36
+ const map = {};
37
+
38
+ // INSTANCE METHODS
39
+
40
+ /**
41
+ * Register a single module address replacement.
42
+ *
43
+ * @param {string} orig - Original module namespace (e.g. 'X_Y_Z')
44
+ * @param {string} alter - Replacement module namespace (e.g. 'A_B_C')
45
+ */
46
+ this.add = function (orig, alter) {
47
+ map[orig] = alter;
48
+ };
49
+
50
+ /* eslint-disable no-unused-vars */
51
+ /**
52
+ * Replace the `moduleName` of the dependency ID if a mapping exists.
53
+ *
54
+ * This function does not alter any other part of the `depId` (e.g. lifestyle, export kind).
55
+ *
56
+ * @param {TeqFw_Di_DepId} depId - Dependency ID after previous transformations.
57
+ * @param {TeqFw_Di_DepId} originalId - Original dependency ID before any processing.
58
+ * @param {string[]} stack - Stack of parent dependency IDs (for trace/debug).
59
+ * @returns {TeqFw_Di_DepId} - Transformed `depId` with updated `moduleName` if replaced.
60
+ */
61
+ this.modify = function (depId, originalId, stack) {
62
+ let module = depId.moduleName;
63
+ const seen = new Set();
64
+
65
+ while (map[module] && !seen.has(module)) {
66
+ seen.add(module);
67
+ module = map[module];
68
+ }
69
+
70
+ if (module !== depId.moduleName) {
71
+ return {
72
+ ...depId,
73
+ moduleName: module,
74
+ };
75
+ }
76
+
77
+ return depId;
78
+ };
79
+ }
80
+ }
package/teqfw.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "@teqfw/di": {
3
3
  "autoload": {
4
- "ns": "TeqFw_Di",
5
- "path": "./src",
6
- "ext": "js"
4
+ "ns": "TeqFw_Di_",
5
+ "path": "./src"
7
6
  }
8
7
  }
9
8
  }
@@ -0,0 +1,98 @@
1
+ import parse from '../../../../../src/Container/A/Composer/A/SpecParser.js';
2
+ import {describe, it} from 'node:test';
3
+ import assert from 'node:assert';
4
+
5
+
6
+ describe('TeqFw_Di_Container_A_Composer_A_SpecParser', () => {
7
+
8
+ it('parses not a function', async () => {
9
+ const deps = parse('namedSingleton');
10
+ assert(deps, []);
11
+ });
12
+
13
+ describe('parses an arrow function', () => {
14
+ it('w/o arguments', async () => {
15
+ const fn = () => {};
16
+ const deps = parse(fn);
17
+ assert(deps, []);
18
+ });
19
+ it('with simple arguments', async () => {
20
+ const fn = ({container, logger, config}) => {};
21
+ const deps = parse(fn);
22
+ assert(deps, []);
23
+ });
24
+ it('with complex arguments', async () => {
25
+ const fn = (
26
+ {
27
+ Vnd_Pkg_Mod1,
28
+ Vnd_Pkg_Mod2$,
29
+ Vnd_Pkg_Mod3$$,
30
+ 'Vnd_Pkg_Mod4.exp': exp,
31
+ }
32
+ ) => {};
33
+ const deps = parse(fn);
34
+ assert(deps.length, 4);
35
+ });
36
+ });
37
+
38
+ describe('parses a regular function', () => {
39
+ it('w/o arguments', async () => {
40
+ const fn = function () {};
41
+ const deps = parse(fn);
42
+ assert(deps, []);
43
+ });
44
+ it('with simple arguments', async () => {
45
+ const fn = function ({container, logger, config}) {};
46
+ const deps = parse(fn);
47
+ assert(deps, []);
48
+ });
49
+ it('with complex arguments', async () => {
50
+ const fn = function (
51
+ {
52
+ Vnd_Pkg_Mod1,
53
+ Vnd_Pkg_Mod2$,
54
+ Vnd_Pkg_Mod3$$,
55
+ 'Vnd_Pkg_Mod4.exp': exp,
56
+ }
57
+ ) {};
58
+ const deps = parse(fn);
59
+ assert(deps.length, 4);
60
+ });
61
+ });
62
+
63
+ describe('parses a class', () => {
64
+ it('w/o arguments', async () => {
65
+ const Clazz = class {
66
+ constructor() { }
67
+
68
+ };
69
+ const deps = parse(Clazz);
70
+ assert(deps, []);
71
+ });
72
+ it('with simple arguments', async () => {
73
+ const Clazz = class {
74
+ constructor({container, logger, config}) { }
75
+
76
+ };
77
+ const deps = parse(Clazz);
78
+ assert(deps, []);
79
+ });
80
+ it('with complex arguments', async () => {
81
+ const Clazz = class {
82
+ constructor(
83
+ {
84
+ Vnd_Pkg_Mod1,
85
+ Vnd_Pkg_Mod2$,
86
+ Vnd_Pkg_Mod3$$,
87
+ 'Vnd_Pkg_Mod4.exp': exp,
88
+ }
89
+ ) { }
90
+
91
+ };
92
+ const deps = parse(Clazz);
93
+ assert(deps.length, 4);
94
+ });
95
+ });
96
+
97
+
98
+ });
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Default format for the objects:
3
+ * - Ns_Module: es6 module
4
+ * - Ns_Module$: default export, factory, singleton (the most frequently used case)
5
+ * - Ns_Module$$: default export, factory, instance
6
+ * - Ns_Module.default: default export, as-is, singleton
7
+ * - Ns_Module.name: named export, as-is, singleton
8
+ * - Ns_Module.name$: named export, factory, singleton (the most frequently used case)
9
+ * - Ns_Module.name$$: named export, factory, instance
10
+ * - Ns_Module$(proxy): default export, factory, singleton with one post wrapper
11
+ * - Ns_Module$(proxy,factory): default export, factory, singleton with two post wrappers
12
+ */
13
+ import DefChunk from '../../../../../src/Container/A/Parser/Chunk/Def.js';
14
+ import {describe, it} from 'node:test';
15
+ import assert from 'node:assert';
16
+ import Defs from '../../../../../src/Defs.js';
17
+
18
+ describe('TeqFw_Di_Container_A_Parser_Chunk_Def', () => {
19
+
20
+ const chunk = new DefChunk();
21
+
22
+ describe('should parse:', () => {
23
+
24
+ describe('default export:', () => {
25
+ it('es6 module (Ns_Module)', () => {
26
+ /** @type {TeqFw_Di_DepId} */
27
+ const dto = chunk.parse('Ns_Module');
28
+ assert.strictEqual(dto.composition, undefined);
29
+ assert.strictEqual(dto.exportName, undefined);
30
+ assert.strictEqual(dto.life, undefined);
31
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
32
+ assert.strictEqual(dto.origin, 'Ns_Module');
33
+ assert.strictEqual(dto.wrappers.length, 0);
34
+ });
35
+ it('factory, singleton (Ns_Module$)', () => {
36
+ /** @type {TeqFw_Di_DepId} */
37
+ const dto = chunk.parse('Ns_Module$');
38
+ assert.strictEqual(dto.composition, Defs.CF);
39
+ assert.strictEqual(dto.exportName, 'default');
40
+ assert.strictEqual(dto.life, Defs.LS);
41
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
42
+ assert.strictEqual(dto.origin, 'Ns_Module$');
43
+ assert.strictEqual(dto.wrappers.length, 0);
44
+ });
45
+ it('factory, instance (Ns_Module$$)', () => {
46
+ /** @type {TeqFw_Di_DepId} */
47
+ const dto = chunk.parse('Ns_Module$$');
48
+ assert.strictEqual(dto.composition, Defs.CF);
49
+ assert.strictEqual(dto.exportName, 'default');
50
+ assert.strictEqual(dto.life, Defs.LI);
51
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
52
+ assert.strictEqual(dto.origin, 'Ns_Module$$');
53
+ assert.strictEqual(dto.wrappers.length, 0);
54
+ });
55
+ it('default export, as-is, singleton (Ns_Module.)', () => {
56
+ /** @type {TeqFw_Di_DepId} */
57
+ const dto = chunk.parse('Ns_Module.');
58
+ assert.strictEqual(dto.composition, Defs.CA);
59
+ assert.strictEqual(dto.exportName, 'default');
60
+ assert.strictEqual(dto.life, Defs.LS);
61
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
62
+ assert.strictEqual(dto.origin, 'Ns_Module.');
63
+ assert.strictEqual(dto.wrappers.length, 0);
64
+ });
65
+ it('factory, singleton, wrapper (Ns_Module$(proxy))', () => {
66
+ /** @type {TeqFw_Di_DepId} */
67
+ const dto = chunk.parse('Ns_Module$(proxy)');
68
+ assert.strictEqual(dto.composition, Defs.CF);
69
+ assert.strictEqual(dto.exportName, 'default');
70
+ assert.strictEqual(dto.life, Defs.LS);
71
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
72
+ assert.strictEqual(dto.origin, 'Ns_Module$(proxy)');
73
+ assert.strictEqual(dto.wrappers.length, 1);
74
+ });
75
+ it('factory, singleton, wrappers (Ns_Module$(proxy,factory))', () => {
76
+ /** @type {TeqFw_Di_DepId} */
77
+ const dto = chunk.parse('Ns_Module$(proxy,factory)');
78
+ assert.strictEqual(dto.composition, Defs.CF);
79
+ assert.strictEqual(dto.exportName, 'default');
80
+ assert.strictEqual(dto.life, Defs.LS);
81
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
82
+ assert.strictEqual(dto.origin, 'Ns_Module$(proxy,factory)');
83
+ assert.strictEqual(dto.wrappers.length, 2);
84
+ });
85
+ });
86
+
87
+ describe('named export:', () => {
88
+ it('as-is, singleton (Ns_Module.name)', () => {
89
+ /** @type {TeqFw_Di_DepId} */
90
+ const dto = chunk.parse('Ns_Module.name');
91
+ assert.strictEqual(dto.composition, Defs.CA);
92
+ assert.strictEqual(dto.exportName, 'name');
93
+ assert.strictEqual(dto.life, Defs.LS);
94
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
95
+ assert.strictEqual(dto.origin, 'Ns_Module.name');
96
+ assert.strictEqual(dto.wrappers.length, 0);
97
+ });
98
+ it('factory, singleton (Ns_Module.name$)', () => {
99
+ /** @type {TeqFw_Di_DepId} */
100
+ const dto = chunk.parse('Ns_Module.name$');
101
+ assert.strictEqual(dto.composition, Defs.CF);
102
+ assert.strictEqual(dto.exportName, 'name');
103
+ assert.strictEqual(dto.life, Defs.LS);
104
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
105
+ assert.strictEqual(dto.origin, 'Ns_Module.name$');
106
+ assert.strictEqual(dto.wrappers.length, 0);
107
+ });
108
+ it('factory, instance (Ns_Module.name$$)', () => {
109
+ /** @type {TeqFw_Di_DepId} */
110
+ const dto = chunk.parse('Ns_Module.name$$');
111
+ assert.strictEqual(dto.composition, Defs.CF);
112
+ assert.strictEqual(dto.exportName, 'name');
113
+ assert.strictEqual(dto.life, Defs.LI);
114
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
115
+ assert.strictEqual(dto.origin, 'Ns_Module.name$$');
116
+ assert.strictEqual(dto.wrappers.length, 0);
117
+ });
118
+ it('factory, singleton.wrapper (Ns_Module.name$(proxy))', () => {
119
+ /** @type {TeqFw_Di_DepId} */
120
+ const dto = chunk.parse('Ns_Module.name$(proxy)');
121
+ assert.strictEqual(dto.composition, Defs.CF);
122
+ assert.strictEqual(dto.exportName, 'name');
123
+ assert.strictEqual(dto.life, Defs.LS);
124
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
125
+ assert.strictEqual(dto.origin, 'Ns_Module.name$(proxy)');
126
+ assert.strictEqual(dto.wrappers.length, 1);
127
+ });
128
+ it('factory, singleton.wrappers (Ns_Module.name$(proxy,factory))', () => {
129
+ /** @type {TeqFw_Di_DepId} */
130
+ const dto = chunk.parse('Ns_Module.name$(proxy,factory)');
131
+ assert.strictEqual(dto.composition, Defs.CF);
132
+ assert.strictEqual(dto.exportName, 'name');
133
+ assert.strictEqual(dto.life, Defs.LS);
134
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
135
+ assert.strictEqual(dto.origin, 'Ns_Module.name$(proxy,factory)');
136
+ assert.strictEqual(dto.wrappers.length, 2);
137
+ });
138
+ });
139
+
140
+ describe('named export with "#" separator:', () => {
141
+ it('as-is, singleton (Ns_Module#name)', () => {
142
+ const dto = chunk.parse('Ns_Module#name');
143
+ assert.strictEqual(dto.composition, Defs.CA);
144
+ assert.strictEqual(dto.exportName, 'name');
145
+ assert.strictEqual(dto.life, Defs.LS);
146
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
147
+ assert.strictEqual(dto.origin, 'Ns_Module#name');
148
+ assert.strictEqual(dto.wrappers.length, 0);
149
+ });
150
+ it('factory, singleton (Ns_Module#name$)', () => {
151
+ const dto = chunk.parse('Ns_Module#name$');
152
+ assert.strictEqual(dto.composition, Defs.CF);
153
+ assert.strictEqual(dto.exportName, 'name');
154
+ assert.strictEqual(dto.life, Defs.LS);
155
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
156
+ assert.strictEqual(dto.origin, 'Ns_Module#name$');
157
+ assert.strictEqual(dto.wrappers.length, 0);
158
+ });
159
+ it('factory, instance (Ns_Module#name$$)', () => {
160
+ const dto = chunk.parse('Ns_Module#name$$');
161
+ assert.strictEqual(dto.composition, Defs.CF);
162
+ assert.strictEqual(dto.exportName, 'name');
163
+ assert.strictEqual(dto.life, Defs.LI);
164
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
165
+ assert.strictEqual(dto.origin, 'Ns_Module#name$$');
166
+ assert.strictEqual(dto.wrappers.length, 0);
167
+ });
168
+ it('factory, singleton with wrappers (Ns_Module#name$(proxy,factory))', () => {
169
+ const dto = chunk.parse('Ns_Module#name$(proxy,factory)');
170
+ assert.strictEqual(dto.composition, Defs.CF);
171
+ assert.strictEqual(dto.exportName, 'name');
172
+ assert.strictEqual(dto.life, Defs.LS);
173
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
174
+ assert.strictEqual(dto.origin, 'Ns_Module#name$(proxy,factory)');
175
+ assert.strictEqual(dto.wrappers.length, 2);
176
+ });
177
+ });
178
+
179
+ describe('nodejs package:', () => {
180
+ it('es6 module (node:pkg)', () => {
181
+ const dto = chunk.parse('node:pkg');
182
+ assert.strictEqual(dto.isNodeModule, true);
183
+ assert.strictEqual(dto.moduleName, 'pkg');
184
+ assert.strictEqual(dto.composition, undefined);
185
+ assert.strictEqual(dto.exportName, undefined);
186
+ assert.strictEqual(dto.life, undefined);
187
+ assert.strictEqual(dto.origin, 'node:pkg');
188
+ });
189
+ it('default export, singleton (node:pkg$$)', () => {
190
+ const dto = chunk.parse('node:pkg$$');
191
+ assert.strictEqual(dto.isNodeModule, true);
192
+ assert.strictEqual(dto.moduleName, 'pkg');
193
+ assert.strictEqual(dto.composition, Defs.CF);
194
+ assert.strictEqual(dto.exportName, 'default');
195
+ assert.strictEqual(dto.life, Defs.LI);
196
+ assert.strictEqual(dto.origin, 'node:pkg$$');
197
+ });
198
+ it('named export (node:pkg.export)', () => {
199
+ const dto = chunk.parse('node:pkg.export');
200
+ assert.strictEqual(dto.isNodeModule, true);
201
+ assert.strictEqual(dto.moduleName, 'pkg');
202
+ assert.strictEqual(dto.composition, Defs.CA);
203
+ assert.strictEqual(dto.exportName, 'export');
204
+ assert.strictEqual(dto.life, Defs.LS);
205
+ assert.strictEqual(dto.origin, 'node:pkg.export');
206
+ });
207
+ it('named export, factory (node:pkg.export$$)', () => {
208
+ const dto = chunk.parse('node:pkg.export$$');
209
+ assert.strictEqual(dto.isNodeModule, true);
210
+ assert.strictEqual(dto.moduleName, 'pkg');
211
+ assert.strictEqual(dto.composition, Defs.CF);
212
+ assert.strictEqual(dto.exportName, 'export');
213
+ assert.strictEqual(dto.life, Defs.LI);
214
+ assert.strictEqual(dto.origin, 'node:pkg.export$$');
215
+ });
216
+ it('scoped package (node:@teqfw/db)', () => {
217
+ const dto = chunk.parse('node:@teqfw/db');
218
+ assert.strictEqual(dto.isNodeModule, true);
219
+ assert.strictEqual(dto.moduleName, '@teqfw/db');
220
+ assert.strictEqual(dto.composition, undefined);
221
+ assert.strictEqual(dto.exportName, undefined);
222
+ assert.strictEqual(dto.life, undefined);
223
+ assert.strictEqual(dto.origin, 'node:@teqfw/db');
224
+ });
225
+ it('scoped package export (node:@teqfw/db.export$$)', () => {
226
+ const dto = chunk.parse('node:@teqfw/db.export$$');
227
+ assert.strictEqual(dto.isNodeModule, true);
228
+ assert.strictEqual(dto.moduleName, '@teqfw/db');
229
+ assert.strictEqual(dto.composition, Defs.CF);
230
+ assert.strictEqual(dto.exportName, 'export');
231
+ assert.strictEqual(dto.life, Defs.LI);
232
+ assert.strictEqual(dto.origin, 'node:@teqfw/db.export$$');
233
+ });
234
+ });
235
+ });
236
+
237
+ });
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Default format for the objects:
3
+ * - Ns_Module: es6 module
4
+ * - Ns_Module$: default export, factory, singleton (the most frequently used case)
5
+ * - Ns_Module$I: default export, factory, instance
6
+ * - Ns_Module$AS: default export, as-is, singleton
7
+ * - Ns_Module.name: named export, as-is, singleton
8
+ * - Ns_Module.name$: named export, factory, singleton (the most frequently used case)
9
+ * - Ns_Module.name$I: named export, factory, instance
10
+ */
11
+ import DefChunk from '../../../../../src/Container/A/Parser/Chunk/V02X.js';
12
+ import {describe, it} from 'node:test';
13
+ import assert from 'node:assert';
14
+ import Defs from '../../../../../src/Defs.js';
15
+
16
+ describe('TeqFw_Di_Container_A_Parser_Chunk_V02X', () => {
17
+
18
+ const chunk = new DefChunk();
19
+
20
+ describe('should parse:', () => {
21
+
22
+ describe('default export:', () => {
23
+ it('es6 module (Ns_Module)', () => {
24
+ /** @type {TeqFw_Di_DepId} */
25
+ const dto = chunk.parse('Ns_Module');
26
+ assert.strictEqual(dto.composition, undefined);
27
+ assert.strictEqual(dto.exportName, undefined);
28
+ assert.strictEqual(dto.life, undefined);
29
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
30
+ assert.strictEqual(dto.origin, 'Ns_Module');
31
+ assert.strictEqual(dto.wrappers.length, 0);
32
+ });
33
+ it('factory, singleton (Ns_Module$)', () => {
34
+ /** @type {TeqFw_Di_DepId} */
35
+ const dto = chunk.parse('Ns_Module$');
36
+ assert.strictEqual(dto.composition, Defs.CF);
37
+ assert.strictEqual(dto.exportName, 'default');
38
+ assert.strictEqual(dto.life, Defs.LS);
39
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
40
+ assert.strictEqual(dto.origin, 'Ns_Module$');
41
+ assert.strictEqual(dto.wrappers.length, 0);
42
+ });
43
+ it('factory, instance (Ns_Module$I)', () => {
44
+ /** @type {TeqFw_Di_DepId} */
45
+ const dto = chunk.parse('Ns_Module$I');
46
+ assert.strictEqual(dto.composition, Defs.CF);
47
+ assert.strictEqual(dto.exportName, 'default');
48
+ assert.strictEqual(dto.life, Defs.LI);
49
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
50
+ assert.strictEqual(dto.origin, 'Ns_Module$I');
51
+ assert.strictEqual(dto.wrappers.length, 0);
52
+ });
53
+ it('default export, as-is, singleton (Ns_Module$AS)', () => {
54
+ /** @type {TeqFw_Di_DepId} */
55
+ const dto = chunk.parse('Ns_Module$AS');
56
+ assert.strictEqual(dto.composition, Defs.CA);
57
+ assert.strictEqual(dto.exportName, 'default');
58
+ assert.strictEqual(dto.life, Defs.LS);
59
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
60
+ assert.strictEqual(dto.origin, 'Ns_Module$AS');
61
+ assert.strictEqual(dto.wrappers.length, 0);
62
+ });
63
+ });
64
+
65
+ describe('named export:', () => {
66
+ it('as-is, singleton (Ns_Module.name)', () => {
67
+ /** @type {TeqFw_Di_DepId} */
68
+ const dto = chunk.parse('Ns_Module.name');
69
+ assert.strictEqual(dto.composition, Defs.CA);
70
+ assert.strictEqual(dto.exportName, 'name');
71
+ assert.strictEqual(dto.life, Defs.LS);
72
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
73
+ assert.strictEqual(dto.origin, 'Ns_Module.name');
74
+ assert.strictEqual(dto.wrappers.length, 0);
75
+ });
76
+ it('factory, singleton (Ns_Module.name$)', () => {
77
+ /** @type {TeqFw_Di_DepId} */
78
+ const dto = chunk.parse('Ns_Module.name$');
79
+ assert.strictEqual(dto.composition, Defs.CF);
80
+ assert.strictEqual(dto.exportName, 'name');
81
+ assert.strictEqual(dto.life, Defs.LS);
82
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
83
+ assert.strictEqual(dto.origin, 'Ns_Module.name$');
84
+ assert.strictEqual(dto.wrappers.length, 0);
85
+ });
86
+ it('factory, instance (Ns_Module.name$I)', () => {
87
+ /** @type {TeqFw_Di_DepId} */
88
+ const dto = chunk.parse('Ns_Module.name$I');
89
+ assert.strictEqual(dto.composition, Defs.CF);
90
+ assert.strictEqual(dto.exportName, 'name');
91
+ assert.strictEqual(dto.life, Defs.LI);
92
+ assert.strictEqual(dto.moduleName, 'Ns_Module');
93
+ assert.strictEqual(dto.origin, 'Ns_Module.name$I');
94
+ assert.strictEqual(dto.wrappers.length, 0);
95
+ });
96
+ });
97
+
98
+ describe('nodejs package:', () => {
99
+ it('es6 module (node:pkg)', () => {
100
+ const dto = chunk.parse('node:pkg');
101
+ assert.strictEqual(dto.isNodeModule, true);
102
+ assert.strictEqual(dto.moduleName, 'pkg');
103
+ assert.strictEqual(dto.composition, undefined);
104
+ assert.strictEqual(dto.exportName, undefined);
105
+ assert.strictEqual(dto.life, undefined);
106
+ assert.strictEqual(dto.origin, 'node:pkg');
107
+ });
108
+ it('default export, singleton (node:pkg$)', () => {
109
+ const dto = chunk.parse('node:pkg$');
110
+ assert.strictEqual(dto.isNodeModule, true);
111
+ assert.strictEqual(dto.moduleName, 'pkg');
112
+ assert.strictEqual(dto.composition, Defs.CF);
113
+ assert.strictEqual(dto.exportName, 'default');
114
+ assert.strictEqual(dto.life, Defs.LS);
115
+ assert.strictEqual(dto.origin, 'node:pkg$');
116
+ });
117
+ it('named export (node:pkg.export)', () => {
118
+ const dto = chunk.parse('node:pkg.export');
119
+ assert.strictEqual(dto.isNodeModule, true);
120
+ assert.strictEqual(dto.moduleName, 'pkg');
121
+ assert.strictEqual(dto.composition, Defs.CA);
122
+ assert.strictEqual(dto.exportName, 'export');
123
+ assert.strictEqual(dto.life, Defs.LS);
124
+ assert.strictEqual(dto.origin, 'node:pkg.export');
125
+ });
126
+ it('named export, factory (node:pkg.export$)', () => {
127
+ const dto = chunk.parse('node:pkg.export$');
128
+ assert.strictEqual(dto.isNodeModule, true);
129
+ assert.strictEqual(dto.moduleName, 'pkg');
130
+ assert.strictEqual(dto.composition, Defs.CF);
131
+ assert.strictEqual(dto.exportName, 'export');
132
+ assert.strictEqual(dto.life, Defs.LS);
133
+ assert.strictEqual(dto.origin, 'node:pkg.export$');
134
+ });
135
+ });
136
+ });
137
+
138
+ });
@@ -0,0 +1,141 @@
1
+ import {dirname, join} from 'node:path';
2
+ import assert from 'node:assert';
3
+ import {describe, it} from 'node:test';
4
+ import Container from '../src/Container.js';
5
+ import Defs from '../src/Defs.js';
6
+
7
+ const __dirname = dirname(import.meta.url);
8
+ const ROOT = join(__dirname, '_data', 'Container');
9
+
10
+ describe('TeqFw_Di_Container', () => {
11
+
12
+ describe('basic tests', () => {
13
+ it('has right classname', async () => {
14
+ const container = new Container();
15
+ assert.strictEqual(container.constructor.name, 'TeqFw_Di_Container');
16
+ });
17
+
18
+ it('has all expected public methods', async () => {
19
+ const container = new Container();
20
+ const methods = Object.getOwnPropertyNames(container)
21
+ .filter(p => (typeof container[p] === 'function'));
22
+ assert.deepStrictEqual(methods.sort(), [
23
+ 'enableTestMode',
24
+ 'get',
25
+ 'getParser',
26
+ 'getPostProcessor',
27
+ 'getPreProcessor',
28
+ 'getResolver',
29
+ 'register',
30
+ 'setDebug',
31
+ 'setParser',
32
+ 'setPostProcessor',
33
+ 'setPreProcessor',
34
+ 'setResolver',
35
+ ]);
36
+ });
37
+
38
+ it('does not contain itself inside', async () => {
39
+ const container = new Container();
40
+ await assert.rejects(
41
+ async () => {
42
+ await container.get('container');
43
+ },
44
+ {
45
+ code: 'ERR_MODULE_NOT_FOUND',
46
+ message: /^Cannot find package 'container'/
47
+ }
48
+ );
49
+ });
50
+ });
51
+
52
+ describe('creates objects', () => {
53
+
54
+ it('default export singleton (App_Service$)', async () => {
55
+ const container = new Container();
56
+ container.setDebug(true);
57
+ const resolver = container.getResolver();
58
+ const src = join(ROOT, 'classes');
59
+ resolver.addNamespaceRoot('App_', src, 'js');
60
+ const dep = await container.get('App_Service$');
61
+ assert(dep);
62
+ assert(Object.isFrozen(dep));
63
+ dep({boobs: 'big'});
64
+ });
65
+
66
+ });
67
+
68
+ describe('prevents loops', () => {
69
+
70
+ it('simple loop', async () => {
71
+ const container = new Container();
72
+ container.setDebug(true);
73
+ const resolver = container.getResolver();
74
+ const src = join(ROOT, 'loop');
75
+ resolver.addNamespaceRoot('App_', src, 'js');
76
+ try {
77
+ await container.get('App_Service$');
78
+ } catch (e) {
79
+ assert(e);
80
+ }
81
+ });
82
+
83
+ });
84
+
85
+ describe('register()', () => {
86
+
87
+ it('registers a new singleton successfully when test mode is enabled', async () => {
88
+ const container = new Container();
89
+ container.enableTestMode();
90
+ const obj = {hello: 'world'};
91
+ container.register('My_Test_Module$', obj);
92
+ const instance = await container.get('My_Test_Module$');
93
+ assert.strictEqual(instance, obj);
94
+ });
95
+
96
+ it('throws if already registered, even in test mode', async () => {
97
+ const container = new Container();
98
+ container.enableTestMode();
99
+ const obj = {hello: 'again'};
100
+ container.register('My_Test_Module$', obj);
101
+ assert.throws(() => {
102
+ container.register('My_Test_Module$', {oops: true});
103
+ }, /already registered/);
104
+ });
105
+
106
+ it('throws if test mode is not enabled', () => {
107
+ const container = new Container();
108
+ const obj = {unauthorized: true};
109
+ assert.throws(() => {
110
+ container.register('My_Test_Module$', obj);
111
+ }, /Use enableTestMode\(\) to allow it/);
112
+ });
113
+
114
+ it('throws if depId is missing (in test mode)', () => {
115
+ const container = new Container();
116
+ container.enableTestMode();
117
+ assert.throws(() => {
118
+ container.register(undefined, {});
119
+ }, /required/);
120
+ });
121
+
122
+ it('throws if object is missing (in test mode)', () => {
123
+ const container = new Container();
124
+ container.enableTestMode();
125
+ assert.throws(() => {
126
+ container.register('My_Missing_Obj$', null);
127
+ }, /required/);
128
+ });
129
+
130
+ it('throws if trying to register a non-singleton (in test mode)', () => {
131
+ const container = new Container();
132
+ container.enableTestMode();
133
+ assert.throws(() => {
134
+ container.register('My_Test_Module$$', {notAllowed: true});
135
+ }, /Only node modules & singletons can be registered/);
136
+ });
137
+
138
+ });
139
+
140
+
141
+ });
@@ -0,0 +1,3 @@
1
+ export default {
2
+ appName: 'Test App',
3
+ };
@@ -0,0 +1,7 @@
1
+ export default async function ({App_Config: config}) {
2
+ console.info(`Logger is created with config: '${JSON.stringify(config)}'`);
3
+ return {
4
+ error: (msg) => console.error(msg),
5
+ info: (msg) => console.info(msg),
6
+ };
7
+ };
@@ -0,0 +1,10 @@
1
+ export default async function (
2
+ {
3
+ App_Logger$: logger,
4
+ App_Config: config,
5
+ }
6
+ ) {
7
+ return function (opts) {
8
+ logger.info(`Service '${config.appName}' is running with: ${JSON.stringify(opts)}`);
9
+ };
10
+ }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ appName: 'Test App',
3
+ };
@@ -0,0 +1,7 @@
1
+ export default async function ({App_Config: config}) {
2
+ console.info(`Logger is created with config: '${JSON.stringify(config)}'`);
3
+ return {
4
+ error: (msg) => console.error(msg),
5
+ info: (msg) => console.info(msg),
6
+ };
7
+ };
@@ -0,0 +1,15 @@
1
+ export default class App_Service {
2
+ constructor(
3
+ {
4
+ App_Logger$: logger,
5
+ App_Config: config,
6
+ 'node:http2': http2,
7
+ }
8
+ ) {
9
+ const {constants: {HTTP2_HEADER_CONTENT_TYPE}} = http2;
10
+ return function (opts) {
11
+ logger.info(`Service '${config.appName}' is running with: ${JSON.stringify(opts)}`);
12
+ logger.info(`Header: ${HTTP2_HEADER_CONTENT_TYPE}`);
13
+ };
14
+ }
15
+ }
@@ -0,0 +1,7 @@
1
+ export default class {
2
+ constructor({App_Service$}) {
3
+ return {
4
+ appName: 'Test App',
5
+ };
6
+ }
7
+ };
@@ -0,0 +1,7 @@
1
+ export default async function ({App_Config$I: config}) {
2
+ console.info(`Logger is created with config: '${JSON.stringify(config)}'`);
3
+ return {
4
+ error: (msg) => console.error(msg),
5
+ info: (msg) => console.info(msg),
6
+ };
7
+ };
@@ -0,0 +1,12 @@
1
+ export default class App_Service {
2
+ constructor(
3
+ {
4
+ App_Logger$: logger,
5
+ App_Config: config,
6
+ }
7
+ ) {
8
+ return function (opts) {
9
+ logger.info(`Service '${config.appName}' is running with: ${JSON.stringify(opts)}`);
10
+ };
11
+ }
12
+ }
@@ -0,0 +1,3 @@
1
+ export default {
2
+ appName: 'Test App',
3
+ };
@@ -0,0 +1,7 @@
1
+ export default async function ({App_Config: config}) {
2
+ console.info(`Logger is created with config: '${JSON.stringify(config)}'`);
3
+ return {
4
+ error: (msg) => console.error(msg),
5
+ info: (msg) => console.info(msg),
6
+ };
7
+ };
@@ -0,0 +1,12 @@
1
+ export default class App_Service {
2
+ constructor(
3
+ {
4
+ App_Logger$: logger,
5
+ App_Config: config,
6
+ }
7
+ ) {
8
+ return function (opts) {
9
+ logger.info(`Service '${config.appName}' is running with: ${JSON.stringify(opts)}`);
10
+ };
11
+ }
12
+ }
package/RELEASE.md DELETED
@@ -1,109 +0,0 @@
1
- # @teqfw/di releases
2
-
3
- ## 0.35.0 – Support for Node.js Module Mocking in Test Mode
4
-
5
- - DI container (test mode): extended support to register not only singleton instances but also mock implementations of
6
- native Node.js modules, enabling more flexible and robust unit testing.
7
- - Added missing property `TeqFw_Di_DepId.isNodeModule` to properly identify Node.js modules in the dependency resolution
8
- process.
9
- - Improved documentation (`README.md` and `PHILOSOPHY.md`) to clarify the design philosophy of TeqFW and explain the
10
- immutability and test mode features in greater detail.
11
-
12
- ## 0.34.0 – Strict Mode and Test Support
13
-
14
- - Removed outdated interface `TeqFw_Di_Api_Container` and associated documentation.
15
- - Unified the `get()` and `compose()` methods for dependency retrieval.
16
- - Introduced strict mode by default: manual registration of dependencies is now **disabled** unless explicitly allowed.
17
- - Added `enableTestMode()` method to the container, enabling safe, test-specific manual registration of singleton
18
- dependencies.
19
- - Improved error messages and safeguards against accidental overwrites in `register()`.
20
- - Updated internal naming and documentation for better clarity and alignment with the platform's principles.
21
- - Cleaned up ESLint and JSDoc inconsistencies in internal files.
22
-
23
- ## 0.33.0 – Cleanup and Enhancements
24
-
25
- - Removed deprecated documentation.
26
- - Updated ESLint rules for consistency with current platform practices.
27
- - Introduced `#` as a separator (in addition to `.`) between namespace and export names.
28
-
29
- ## 0.32.0 - added support for the `node:` prefix
30
-
31
- - **Added ability** to manually register objects in the container (`register`).
32
- - **Optimized dependency parsing**, added support for the `node:` prefix.
33
- - **Updated singleton handling**, fixed issues with `Defs.LS`.
34
- - **Added protection against object modification**, freezing objects when possible (`Object.freeze`).
35
- - **Updated dependencies** (Mocha, Rollup, Rollup plugins).
36
-
37
- ## 0.31.0
38
-
39
- * Added optional `stack` parameter to the `get` method for improved dependency tracking and debugging.
40
- * Fixed browser example in the README by updating the usage of `window.TeqFw_Di_Container` for compatibility.
41
-
42
- ## 0.30.2
43
-
44
- * Enhanced the README with updates from ChatGPT.
45
- * Unified JSDoc annotations.
46
-
47
- ## 0.30.1
48
-
49
- * Improve the README.
50
-
51
- ## 0.30.0
52
-
53
- * New format of the depId for default parser (`Ns_Module.export$$(post)`).
54
- * The rollup is added.
55
-
56
- ## 0.22.0
57
-
58
- * Add Windows paths to the Resolver.
59
-
60
- ## 0.21.1
61
-
62
- * Fix the dependency key signature in `TeqFw_Di_Container_A_Parser_Chunk_Def`.
63
-
64
- ## 0.21.0
65
-
66
- * Restructured modules in the package.
67
- * Documentation update.
68
-
69
- ## 0.20.1
70
-
71
- * Changed regex for parameter extraction in the Spec Analyzer.
72
- * Removed leading namespace separator in the Resolver.
73
- * Added `teqfw.json` descriptor to add npm-package to DI container as a sources root in `teqfw/web`.
74
-
75
- ## 0.20.0
76
-
77
- * Fully redesigned package with simplified composition of objects in the container. Spec Analyzer is used instead of a
78
- proxy object.
79
-
80
- ## 0.12.1
81
-
82
- * Hotfix for Windows delimiters.
83
-
84
- ## 0.12.0
85
-
86
- * Standardized comments style for improved code readability and maintenance.
87
-
88
- ## 0.11.0
89
-
90
- * Restructure `/@teqfw/di/replace` node in `teqfw.json`.
91
- * Remove `TeqFw_Di_Shared_Api_Enum_Area` enumeration.
92
- * Fix example code (`npm run example`).
93
-
94
- ## 0.10.0
95
-
96
- * Improve error messaging.
97
- * Use '.' instead of '#' in depIDs (Vnd_Plugin#export => Vnd_Plugin.export). Both variants are available for now.
98
- * Experimental proxy for deps are added (Vnd_Plugin.export@@).
99
-
100
- ## 0.9.0
101
-
102
- * `TeqFw_Di_Shared_Api_Enum_Area` enumeration is added;
103
-
104
- ## 0.8.0
105
-
106
- * docs for plugin's teq-descriptor (see in `main` branch);
107
- * use object notation instead of array notation in namespace replacement statements of
108
- teq-descriptor (`@teqfw/di.replace` node format is changed in `./teqfw.json`);
109
- * array is used as a container for upline dependencies in the 'SpecProxy' (object was);
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env bash
2
- ##
3
- # Clean development files from release branch
4
- ##
5
- DIR_ROOT=${DIR_ROOT:-$(cd "$(dirname "$0")/../../" && pwd)}
6
-
7
- rm -fr "${DIR_ROOT}/demo/"
8
- rm -fr "${DIR_ROOT}/doc/"
9
- rm -fr "${DIR_ROOT}/docs/"
10
- rm -fr "${DIR_ROOT}/example/"
11
- rm -fr "${DIR_ROOT}/node_modules/"
12
- rm -fr "${DIR_ROOT}/package-lock.json"
13
- rm -fr "${DIR_ROOT}/test/"
14
- rm -fr "${DIR_ROOT}/tmp/"
package/dist/esm.js DELETED
@@ -1 +0,0 @@
1
- var e={CA:"A",CF:"F",ID:"container",ID_FQN:"TeqFw_Di_Container$",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 n(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 s(s){return"function"==typeof s?e.isClass(s)?function(e){const t=[],s=e.toString(),r=o.exec(s);return r&&t.push(...n(r[1])),t}(s):function(e){const o=[],s=e.toString(),r=t.exec(s);return r&&o.push(...n(r[2])),o}(s):[]}class r{constructor(){let t=!1;this.create=async function(o,n,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}=n;if(o.composition===e.CF){if("function"==typeof l){const n=s(l);n.length&&(c=`Deps for object '${o.origin}' are: ${JSON.stringify(n)}`,t&&console.log(c));const r={};for(const e of n)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 n;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 n=c.exec(t);if(n&&(o.isNodeModule=Boolean(n[1]),o.moduleName=n[2].replace(/^node:/,""),"."===n[4]||"#"===n[4]?"$"===n[6]||"$$"===n[6]?(o.composition=e.CF,o.exportName=n[5],o.life="$"===n[6]?e.LS:e.LI):(o.composition=e.CA,o.life=e.LS,o.exportName=""!==n[5]?n[5]:"default"):"$"===n[6]||"$$"===n[6]?(o.composition=e.CF,o.exportName="default",o.life="$"===n[6]?e.LS:e.LI):(o.composition=void 0,o.exportName=void 0,o.life=void 0),n[10]&&(o.wrappers=n[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 n;for(const e of t)if(e.canParse(o)){n=e.parse(o);break}return n||(n=e?.parse(o)),n},this.setDefaultChunk=function(t){e=t}}}class u{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=function(t,o){let n=t;for(const s of e)n=s.modify(n,t,o);return n}}}class f{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=async function(t,o,n){let s=t;for(const t of e)s=t.modify(s,o,n),s instanceof Promise&&(s=await s);return s}}}const d="ext",p="ns",h="root";class m{constructor(){const e={};let t=!1,o=[],n="/";this.addNamespaceRoot=function(n,s,r){const i=(t?s.replace(/^\\/,""):s).replace(/\\/g,"/"),c=t?`file://${i}`:i;e[n]={[d]:r??"js",[p]:n,[h]:c},o=Object.keys(e).sort(((e,t)=>t.localeCompare(e)))},this.resolve=function(t){let s,r,i;for(i of o)if(t.startsWith(i)){s=e[i][h],r=e[i].ext;break}if(s&&r){let e=t.replace(i,"");0===e.indexOf("_")&&(e=e.replace("_",""));const o=e.replaceAll("_",n);return`${s}${n}${o}.${r}`}return t},this.setWindowsEnv=function(e=!0){t=e,n=e?"\\":"/"}}}function g(e){return`${e.moduleName}#${e.exportName}`}class ${constructor(){let t=new r,o=!1,n=new l,s=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=[]){if($(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return $("Container itself is returned."),d[e.ID];const l=n.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 s.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=()=>n,this.getPreProcessor=()=>i,this.getPostProcessor=()=>s,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 s=n.parse(t);if(s.life!==e.LS&&!s.isNodeModule)throw new Error(`Only node modules & singletons can be registered: '${t}'`);if(s.life===e.LS){const e=g(s);if(d[e])throw new Error(`'${t}' is already registered`);d[e]=o}else if(s.isNodeModule){const e=s.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=>n=e,this.setPreProcessor=e=>i=e,this.setPostProcessor=e=>s=e,this.setResolver=e=>h=e,d[e.ID]=this}}export{$ as default};
package/dist/umd.js DELETED
@@ -1 +0,0 @@
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={CA:"A",CF:"F",ID:"container",ID_FQN:"TeqFw_Di_Container$",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 n(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 s(s){return"function"==typeof s?e.isClass(s)?function(e){const t=[],s=e.toString(),r=o.exec(s);return r&&t.push(...n(r[1])),t}(s):function(e){const o=[],s=e.toString(),r=t.exec(s);return r&&o.push(...n(r[2])),o}(s):[]}class r{constructor(){let t=!1;this.create=async function(o,n,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}=n;if(o.composition===e.CF){if("function"==typeof l){const n=s(l);n.length&&(c=`Deps for object '${o.origin}' are: ${JSON.stringify(n)}`,t&&console.log(c));const r={};for(const e of n)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 n;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 n=c.exec(t);if(n&&(o.isNodeModule=Boolean(n[1]),o.moduleName=n[2].replace(/^node:/,""),"."===n[4]||"#"===n[4]?"$"===n[6]||"$$"===n[6]?(o.composition=e.CF,o.exportName=n[5],o.life="$"===n[6]?e.LS:e.LI):(o.composition=e.CA,o.life=e.LS,o.exportName=""!==n[5]?n[5]:"default"):"$"===n[6]||"$$"===n[6]?(o.composition=e.CF,o.exportName="default",o.life="$"===n[6]?e.LS:e.LI):(o.composition=void 0,o.exportName=void 0,o.life=void 0),n[10]&&(o.wrappers=n[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 n;for(const e of t)if(e.canParse(o)){n=e.parse(o);break}return n||(n=e?.parse(o)),n},this.setDefaultChunk=function(t){e=t}}}class u{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=function(t,o){let n=t;for(const s of e)n=s.modify(n,t,o);return n}}}class f{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=async function(t,o,n){let s=t;for(const t of e)s=t.modify(s,o,n),s instanceof Promise&&(s=await s);return s}}}const d="ext",p="ns",h="root";class m{constructor(){const e={};let t=!1,o=[],n="/";this.addNamespaceRoot=function(n,s,r){const i=(t?s.replace(/^\\/,""):s).replace(/\\/g,"/"),c=t?`file://${i}`:i;e[n]={[d]:r??"js",[p]:n,[h]:c},o=Object.keys(e).sort(((e,t)=>t.localeCompare(e)))},this.resolve=function(t){let s,r,i;for(i of o)if(t.startsWith(i)){s=e[i][h],r=e[i].ext;break}if(s&&r){let e=t.replace(i,"");0===e.indexOf("_")&&(e=e.replace("_",""));const o=e.replaceAll("_",n);return`${s}${n}${o}.${r}`}return t},this.setWindowsEnv=function(e=!0){t=e,n=e?"\\":"/"}}}function g(e){return`${e.moduleName}#${e.exportName}`}return class{constructor(){let t=new r,o=!1,n=new l,s=new f,i=new u,c=!1;const a={},d={},p={};let h=new m;function w(){o&&console.log(...arguments)}this.get=async function(o,r=[]){if(w(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return w("Container itself is returned."),d[e.ID];const l=n.parse(o),u=i.modify(l,r);if(u.life===e.LS){const e=g(u);if(d[e])return w(`Existing singleton '${e}' is returned.`),d[e]}if(u.isNodeModule&&c){const e=u.origin;if(p[e])return w(`Existing nodejs lib '${e}' is returned.`),p[e]}let f;a[u.moduleName]||(w(`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),w(`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 $=await t.create(u,f,r,this);var y;if(null===(y=$)||"object"!=typeof y&&"function"!=typeof y||"[object Module]"===Object.prototype.toString.call(y)||Object.isFrozen(y)||Object.freeze($),$=await s.modify($,u,r),w(`Object '${o}' is created.`),u.life===e.LS){const e=g(u);d[e]=$,w(`Object '${o}' is saved as singleton.`)}return $},this.enableTestMode=function(){c=!0,w("Test mode enabled")},this.getParser=()=>n,this.getPreProcessor=()=>i,this.getPostProcessor=()=>s,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 s=n.parse(t);if(s.life!==e.LS&&!s.isNodeModule)throw new Error(`Only node modules & singletons can be registered: '${t}'`);if(s.life===e.LS){const e=g(s);if(d[e])throw new Error(`'${t}' is already registered`);d[e]=o}else if(s.isNodeModule){const e=s.origin;if(p[e])throw new Error(`'${t}' is already registered`);p[e]=o}w(`'${t}' is registered`)},this.setDebug=function(e){o=e,t.setDebug(e)},this.setParser=e=>n=e,this.setPreProcessor=e=>i=e,this.setPostProcessor=e=>s=e,this.setResolver=e=>h=e,d[e.ID]=this}}}));