@teqfw/di 1.3.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.
Files changed (46) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +173 -259
  3. package/dist/esm.js +1 -1
  4. package/dist/umd.js +1 -1
  5. package/package.json +16 -10
  6. package/src/AGENTS.md +177 -0
  7. package/src/Config/NamespaceRegistry.mjs +210 -0
  8. package/src/Container/Instantiate/ExportSelector.mjs +39 -0
  9. package/src/Container/Instantiate/Instantiator.mjs +143 -0
  10. package/src/Container/Lifecycle/Registry.mjs +81 -0
  11. package/src/Container/Resolve/GraphResolver.mjs +119 -0
  12. package/src/Container/Resolver.mjs +175 -0
  13. package/src/Container/Wrapper/Executor.mjs +71 -0
  14. package/src/Container.mjs +380 -0
  15. package/src/Def/Parser.mjs +146 -0
  16. package/src/Dto/DepId.mjs +131 -0
  17. package/src/Dto/Resolver/Config/Namespace.mjs +48 -0
  18. package/src/Dto/Resolver/Config.mjs +58 -0
  19. package/src/Enum/Composition.mjs +11 -0
  20. package/src/Enum/Life.mjs +11 -0
  21. package/src/Enum/Platform.mjs +12 -0
  22. package/src/Internal/Logger.mjs +54 -0
  23. package/types.d.ts +53 -26
  24. package/src/Api/Container/Config.js +0 -73
  25. package/src/Api/Container/Parser/Chunk.js +0 -27
  26. package/src/Api/Container/Parser.js +0 -34
  27. package/src/Api/Container/PostProcessor/Chunk.js +0 -17
  28. package/src/Api/Container/PostProcessor.js +0 -29
  29. package/src/Api/Container/PreProcessor/Chunk.js +0 -19
  30. package/src/Api/Container/PreProcessor.js +0 -27
  31. package/src/Api/Container/Resolver.js +0 -18
  32. package/src/Api/Container.js +0 -19
  33. package/src/Container/A/Composer/A/SpecParser.js +0 -86
  34. package/src/Container/A/Composer.js +0 -69
  35. package/src/Container/A/Parser/Chunk/Def.js +0 -69
  36. package/src/Container/A/Parser/Chunk/V02X.js +0 -66
  37. package/src/Container/Config.js +0 -93
  38. package/src/Container/Parser.js +0 -48
  39. package/src/Container/PostProcessor.js +0 -32
  40. package/src/Container/PreProcessor.js +0 -34
  41. package/src/Container/Resolver.js +0 -80
  42. package/src/Container.js +0 -187
  43. package/src/Defs.js +0 -22
  44. package/src/DepId.js +0 -52
  45. package/src/Pre/Replace.js +0 -80
  46. package/teqfw.json +0 -8
@@ -1,69 +0,0 @@
1
- /**
2
- * The composer creates requested objects. It uses the container to create dependencies.
3
- *
4
- * @namespace TeqFw_Di_Container_A_Composer
5
- */
6
- import Defs from '../../Defs.js';
7
- import specParser from './Composer/A/SpecParser.js';
8
-
9
- export default class TeqFw_Di_Container_A_Composer {
10
-
11
- constructor() {
12
- // VARS
13
- let _debug = false;
14
-
15
- // FUNCS
16
- function log(msg) {
17
- if (_debug) console.log(msg);
18
- }
19
-
20
- // INSTANCE METHODS
21
-
22
- /**
23
- * Returns or creates and returns the requested object.
24
- *
25
- * @param {TeqFw_Di_DepId} depId
26
- * @param {object} module - imported es6 module
27
- * @param {string[]} stack - array of the parent objects IDs to prevent dependency loop
28
- * @param {TeqFw_Di_Container} container - to create dependencies for requested object
29
- * @returns {Promise<*>}
30
- */
31
- this.create = async function (depId, module, stack, container) {
32
- if (stack.includes(depId.origin))
33
- throw new Error(`Circular dependency for '${depId.origin}'. Parents are: ${JSON.stringify(stack)}`);
34
- if (depId.exportName) {
35
- // use export from the es6-module
36
- const stackNew = [...stack, depId.origin];
37
- const {[depId.exportName]: exp} = module;
38
- if (depId.composition === Defs.CF) {
39
- if (typeof exp === 'function') {
40
- // create deps for factory function
41
- const deps = specParser(exp);
42
- if (deps.length) log(`Deps for object '${depId.origin}' are: ${JSON.stringify(deps)}`);
43
- const spec = {};
44
- for (const dep of deps)
45
- spec[dep] = await container.get(dep, stackNew);
46
- // create a new object with the factory function
47
- const res = (Defs.isClass(exp)) ? new exp(spec) : exp(spec);
48
- if (res instanceof Promise)
49
- return await res;
50
- else
51
- return res;
52
- } else
53
- // just clone the export
54
- return Object.assign({}, exp);
55
- } else
56
- // just return the export (w/o factory function)
57
- return exp;
58
- } else {
59
- return module;
60
- }
61
- };
62
-
63
- this.setDebug = function (data) {
64
- _debug = data;
65
- };
66
-
67
- // MAIN
68
- }
69
- };
@@ -1,69 +0,0 @@
1
- /**
2
- * Default parser for object keys in format:
3
- * - Ns_Module.export$$(post)
4
- * - node:package.export$$(post)
5
- * - node:@scope/package.export$$(post)
6
- *
7
- * @namespace TeqFw_Di_Container_A_Parser_Chunk_Def
8
- */
9
- import Dto from '../../../../DepId.js';
10
- import Defs from '../../../../Defs.js';
11
-
12
- // VARS
13
- /** @type {RegExp} expression for a default object key */
14
- const REGEXP = /^(node:)?(@?[A-Za-z0-9_-]+\/?[A-Za-z0-9_-]*)(([.#])?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?$/;
15
-
16
- /**
17
- * @implements TeqFw_Di_Api_Container_Parser_Chunk
18
- */
19
- export default class TeqFw_Di_Container_A_Parser_Chunk_Def {
20
-
21
- canParse() {
22
- // the default parser always tries to parse the depId
23
- return true;
24
- }
25
-
26
- parse(objectKey) {
27
- const res = new Dto();
28
- res.origin = objectKey;
29
- const parts = REGEXP.exec(objectKey);
30
- if (parts) {
31
- res.isNodeModule = Boolean(parts[1]); // Detect 'node:' prefix
32
- res.moduleName = parts[2].replace(/^node:/, ''); // Remove 'node:' prefix
33
-
34
- if ((parts[4] === '.') || (parts[4] === '#')) {
35
- // Ns_Module.export or node:package.export
36
- if ((parts[6] === '$') || (parts[6] === '$$')) {
37
- res.composition = Defs.CF;
38
- res.exportName = parts[5];
39
- res.life = (parts[6] === '$') ? Defs.LS : Defs.LI;
40
- } else {
41
- res.composition = Defs.CA;
42
- res.life = Defs.LS;
43
- res.exportName = (parts[5] !== '') ? parts[5] : 'default';
44
- }
45
- } else if ((parts[6] === '$') || parts[6] === '$$') {
46
- // Ns_Module$$ or node:package$$
47
- res.composition = Defs.CF;
48
- res.exportName = 'default';
49
- res.life = (parts[6] === '$') ? Defs.LS : Defs.LI;
50
- } else {
51
- // Ns_Module or node:package (ES6 module)
52
- res.composition = undefined;
53
- res.exportName = undefined;
54
- res.life = undefined;
55
- }
56
-
57
- // Wrappers handling
58
- if (parts[10]) {
59
- res.wrappers = parts[10].split(',');
60
- }
61
- }
62
-
63
- // Ensure singletons for non-factory exports
64
- if ((res.composition === Defs.CA) && (res.life === Defs.LI))
65
- throw new Error(`Export is not a function and should be used as a singleton only: '${res.origin}'.`);
66
-
67
- return res;
68
- }
69
- }
@@ -1,66 +0,0 @@
1
- /**
2
- * Default parser for object keys in format:
3
- * - Ns_Module[.|#]export$[F|A][S|I]
4
- * - node:package[.|#]export$[F|A][S|I]
5
- *
6
- * @namespace TeqFw_Di_Container_A_Parser_Chunk_V02X
7
- */
8
- import Dto from '../../../../DepId.js';
9
- import Defs from '../../../../Defs.js';
10
-
11
- // VARS
12
- /** @type {RegExp} expression for a default object key */
13
- const REGEXP = /^(node:)?(([A-Z])[A-Za-z0-9_]*|[a-z][a-z0-9-]*)(([#.])?([A-Za-z0-9_]*)((\$)([F|A])?([S|I])?)?)?$/;
14
-
15
- /**
16
- * @implements TeqFw_Di_Api_Container_Parser_Chunk
17
- */
18
- export default class TeqFw_Di_Container_A_Parser_Chunk_V02X {
19
-
20
- canParse() {
21
- // the default parser always tries to parse the depId
22
- return true;
23
- }
24
-
25
- parse(objectKey) {
26
- const res = new Dto();
27
- res.origin = objectKey;
28
- const parts = REGEXP.exec(objectKey);
29
- if (parts) {
30
- res.isNodeModule = Boolean(parts[1]); // Check if it starts with 'node:'
31
- res.moduleName = parts[2].replace(/^node:/, ''); // Remove 'node:' if present
32
-
33
- if (parts[5] === '.') {
34
- // App_Service.export or node:package.export
35
- if (parts[8] === '$') {
36
- // App_Service.export$ or node:package.export$
37
- res.composition = Defs.CF;
38
- res.exportName = parts[6];
39
- res.life = (parts[10] === Defs.LI) ? Defs.LI : Defs.LS;
40
- } else {
41
- res.composition = (!parts[8] || parts[8] === Defs.CA) ? Defs.CA : Defs.CF;
42
- res.exportName = parts[6];
43
- res.life = (!parts[8] || parts[10] === Defs.LS) ? Defs.LS : Defs.LI;
44
- }
45
- } else if (parts[8] === '$') {
46
- // App_Logger$FS or node:package$
47
- res.composition = (!parts[9] || parts[9] === Defs.CF) ? Defs.CF : Defs.CA;
48
- res.exportName = 'default';
49
- res.life = parts[10] ? (parts[10] === Defs.LS ? Defs.LS : Defs.LI) : (res.composition === Defs.CF ? Defs.LS : Defs.LI);
50
- } else {
51
- // App_Service or node:package (ES6 module)
52
- res.composition = undefined;
53
- res.exportName = undefined;
54
- res.life = undefined;
55
- }
56
-
57
- }
58
-
59
- // Enforce singleton for non-factory exports
60
- if (res.composition === Defs.CA && res.life === Defs.LI) {
61
- throw new Error(`Export is not a function and should be used as a singleton only: '${res.origin}'.`);
62
- }
63
-
64
- return res;
65
- }
66
- }
@@ -1,93 +0,0 @@
1
- import Container from '../Container.js';
2
-
3
- /**
4
- * Runtime configuration facade for the DI container.
5
- *
6
- * @implements {TeqFw_Di_Api_Container_Config}
7
- */
8
- export default class TeqFw_Di_Container_Config {
9
- constructor() {
10
- // VARS
11
- const _container = new Container();
12
- let _finalized = false;
13
-
14
- // FUNCS
15
- function assertNotFinalized() {
16
- if (_finalized) throw new Error('Container configuration is finalized.');
17
- }
18
-
19
- // INSTANCE METHODS
20
-
21
- /**
22
- * Returns the parser configurator.
23
- *
24
- * @returns {TeqFw_Di_Api_Container_Parser}
25
- */
26
- this.parser = function () {
27
- assertNotFinalized();
28
- return _container.getParser();
29
- };
30
-
31
- /**
32
- * Returns the pre-processor configurator.
33
- *
34
- * @returns {TeqFw_Di_Api_Container_PreProcessor}
35
- */
36
- this.preProcessor = function () {
37
- assertNotFinalized();
38
- return _container.getPreProcessor();
39
- };
40
-
41
- /**
42
- * Returns the post-processor configurator.
43
- *
44
- * @returns {TeqFw_Di_Api_Container_PostProcessor}
45
- */
46
- this.postProcessor = function () {
47
- assertNotFinalized();
48
- return _container.getPostProcessor();
49
- };
50
-
51
- /**
52
- * Returns the resolver configurator.
53
- *
54
- * @returns {TeqFw_Di_Api_Container_Resolver}
55
- */
56
- this.resolver = function () {
57
- assertNotFinalized();
58
- return _container.getResolver();
59
- };
60
-
61
- /**
62
- * Enables test mode.
63
- *
64
- * @returns {void}
65
- */
66
- this.enableTestMode = function () {
67
- assertNotFinalized();
68
- _container.enableTestMode();
69
- };
70
-
71
- /**
72
- * Registers a singleton or a Node.js module replacement in test mode.
73
- *
74
- * @param {string} depId
75
- * @param {object} obj
76
- * @returns {void}
77
- */
78
- this.register = function (depId, obj) {
79
- assertNotFinalized();
80
- _container.register(depId, obj);
81
- };
82
-
83
- /**
84
- * Finalizes configuration and returns a runtime container instance.
85
- *
86
- * @returns {TeqFw_Di_Api_Container}
87
- */
88
- this.finalize = function () {
89
- _finalized = true;
90
- return _container;
91
- };
92
- }
93
- }
@@ -1,48 +0,0 @@
1
- /**
2
- * The parser for the runtime dependency ID contains multiple chunks. Each npm package can have its own format for
3
- * a `depId`. The parser calls the chunks one by one to parse the string ID as a structure and returns the first result.
4
- * If none of the chunks processed the `depId`, the parser calls the default chunk.
5
- */
6
- import DefChunk from './A/Parser/Chunk/Def.js';
7
-
8
- /**
9
- * @implements TeqFw_Di_Api_Container_Parser
10
- */
11
- export default class TeqFw_Di_Container_Parser {
12
- constructor() {
13
- // VARS
14
- /**
15
- * The default chunk to parse the depId if no other chunks have parsed this depId.
16
- *
17
- * @type {TeqFw_Di_Api_Container_Parser_Chunk}
18
- */
19
- let _defaultChunk = new DefChunk();
20
- /**
21
- * The array of the chunks to parse dependency IDs.
22
- * @type {TeqFw_Di_Api_Container_Parser_Chunk[]}
23
- */
24
- const _chunks = [];
25
-
26
- // INSTANCE METHODS
27
-
28
- this.addChunk = function (chunk) {
29
- _chunks.push(chunk);
30
- };
31
-
32
- this.parse = function (depId) {
33
- let res;
34
- for (const one of _chunks)
35
- if (one.canParse(depId)) {
36
- res = one.parse(depId);
37
- break;
38
- }
39
- if (!res)
40
- res = _defaultChunk?.parse(depId);
41
- return res;
42
- };
43
-
44
- this.setDefaultChunk = function (chunk) {
45
- _defaultChunk = chunk;
46
- };
47
- }
48
- };
@@ -1,32 +0,0 @@
1
- /**
2
- * The post-processor handles the result object after composition and before returning.
3
- *
4
- * @implements TeqFw_Di_Api_Container_PostProcessor
5
- */
6
- export default class TeqFw_Di_Container_PostProcessor {
7
-
8
- constructor() {
9
- // VARS
10
-
11
- /**
12
- * The array of the chunks to modify dependency IDs.
13
- * @type {TeqFw_Di_Api_Container_PostProcessor_Chunk[]}
14
- */
15
- const _chunks = [];
16
-
17
- // INSTANCE METHODS
18
-
19
- this.addChunk = function (chunk) {
20
- _chunks.push(chunk);
21
- };
22
-
23
- this.modify = async function (obj, depId, stack) {
24
- let res = obj;
25
- for (const one of _chunks) {
26
- res = one.modify(res, depId, stack);
27
- if (res instanceof Promise) res = await res;
28
- }
29
- return res;
30
- };
31
- }
32
- };
@@ -1,34 +0,0 @@
1
- /**
2
- * The preprocessor handles object keys after the parsing but before creating any objects.
3
- * A replacement rules can be implemented here.
4
- * Every handler is a function with 2 arguments:
5
- * - objectKey: current key after processing with other handlers;
6
- * - originalKey: the key before any processing;
7
- *
8
- * @implements TeqFw_Di_Api_Container_PreProcessor
9
- */
10
- export default class TeqFw_Di_Container_PreProcessor {
11
-
12
- constructor() {
13
- // VARS
14
-
15
- /**
16
- * The array of the chunks to modify dependency IDs.
17
- * @type {TeqFw_Di_Api_Container_PreProcessor_Chunk[]}
18
- */
19
- const _chunks = [];
20
-
21
- // INSTANCE METHODS
22
-
23
- this.addChunk = function (chunk) {
24
- _chunks.push(chunk);
25
- };
26
-
27
- this.modify = function (depId, stack) {
28
- let res = depId;
29
- for (const one of _chunks)
30
- res = one.modify(res, depId, stack);
31
- return res;
32
- };
33
- }
34
- };
@@ -1,80 +0,0 @@
1
- /**
2
- * The Resolver should convert ES6 module name into the path to the sources (file path or URL).
3
- *
4
- * This is a base resolver that considers that:
5
- * - module name is Zend1-compatible ('Vendor_Package_Module')
6
- * - every namespace is bound to some real path ('Vendor_Package_' => '.../node_modules/@vendor/package/src/...)
7
- * - every package has sources with the same extensions (*.js, *.mjs, *.es6, ...)
8
- * - namespaces can be nested (App_Web_ => ./@app/web/..., App_Web_Api_ => ./@app/web_api/...)
9
- */
10
-
11
- // VARS
12
- const KEY_EXT = 'ext';
13
- const KEY_NS = 'ns';
14
- const KEY_PATH = 'root';
15
- /**
16
- * Namespace parts separator.
17
- *
18
- * @type {string}
19
- */
20
- const NSS = '_';
21
-
22
- // MAIN
23
- /**
24
- * @implements {TeqFw_Di_Api_Container_Resolver}
25
- */
26
- export default class TeqFw_Di_Container_Resolver {
27
-
28
- constructor() {
29
- // VARS
30
- const _regNs = {};
31
- let _isWindows = false; // flag of the runtime env - win or *nix/web
32
- let _namespaces = [];
33
- let _ps = '/'; // web & unix path separator
34
-
35
- // INSTANCE METHODS
36
-
37
- this.addNamespaceRoot = function (ns, path, ext) {
38
- const lead = (_isWindows) ? path.replace(/^\\/, '') : path; // remove leading backslash for Win
39
- const norm = lead.replace(/\\/g, '/'); // replace all windows path separators
40
- const root = (_isWindows) ? `file://${norm}` : norm;
41
- _regNs[ns] = {
42
- [KEY_EXT]: ext ?? 'js',
43
- [KEY_NS]: ns,
44
- [KEY_PATH]: root,
45
- };
46
- _namespaces = Object.keys(_regNs).sort((a, b) => b.localeCompare(a));
47
- };
48
-
49
- /**
50
- * Convert the module name to the path of the source files .
51
- * @param {string} moduleName 'Vendor_Package_Module'
52
- * @returns {string} '/home/user/app/node_modules/@vendor/package/src/Module.js'
53
- */
54
- this.resolve = function (moduleName) {
55
- let root, ext, ns;
56
- for (ns of _namespaces) {
57
- if (moduleName.startsWith(ns)) {
58
- root = _regNs[ns][KEY_PATH];
59
- ext = _regNs[ns][KEY_EXT];
60
- break;
61
- }
62
- }
63
- if (root && ext) {
64
- let tail = moduleName.replace(ns, '');
65
- if (tail.indexOf(NSS) === 0) tail = tail.replace(NSS, '');
66
- const file = tail.replaceAll(NSS, _ps);
67
- return `${root}${_ps}${file}.${ext}`;
68
- } else return moduleName;
69
- };
70
-
71
- /**
72
- * 'true' - to use '\' as path separator to resolve paths.
73
- * @param {boolean} isWindows
74
- */
75
- this.setWindowsEnv = function (isWindows = true) {
76
- _isWindows = isWindows;
77
- _ps = (isWindows) ? '\\' : '/';
78
- };
79
- }
80
- };
package/src/Container.js DELETED
@@ -1,187 +0,0 @@
1
- /**
2
- * The Object Container (composition root).
3
- * We can use static imports in the Container.
4
- *
5
- * @namespace TeqFw_Di_Container
6
- */
7
- import Composer from './Container/A/Composer.js';
8
- import Defs from './Defs.js';
9
- import Parser from './Container/Parser.js';
10
- import PreProcessor from './Container/PreProcessor.js';
11
- import PostProcessor from './Container/PostProcessor.js';
12
- import Resolver from './Container/Resolver.js';
13
-
14
- // FUNCS
15
- /**
16
- * ID to store singletons in the internal registry.
17
- * @param {TeqFw_Di_DepId} key
18
- * @returns {string}
19
- */
20
- function getSingletonId(key) {
21
- return `${key.moduleName}#${key.exportName}`;
22
- }
23
-
24
- /**
25
- * Determines if an object, function, or primitive can be safely frozen.
26
- * @param {*} value - The value to check.
27
- * @returns {boolean} - Returns true if the value can be safely frozen.
28
- */
29
- function canBeFrozen(value) {
30
- // Primitives (except objects and functions) cannot be frozen
31
- if (value === null || typeof value !== 'object' && typeof value !== 'function') return false;
32
- // // ES modules cannot be frozen
33
- if (Object.prototype.toString.call(value) === '[object Module]') return false;
34
- // check is Object is already frozen
35
- return !Object.isFrozen(value);
36
- }
37
-
38
- // MAIN
39
- export default class TeqFw_Di_Container {
40
-
41
- constructor() {
42
- // VARS
43
- let _composer = new Composer();
44
- let _debug = false;
45
- let _parser = new Parser();
46
- let _postProcessor = new PostProcessor();
47
- let _preProcessor = new PreProcessor();
48
- let _testMode = false;
49
-
50
- /**
51
- * Registry for paths for loaded es6 modules.
52
- *
53
- * @type {Object<string, string>}
54
- */
55
- const _regPaths = {};
56
- /**
57
- * Registry to store singletons.
58
- * @type {Object<string, object>}
59
- */
60
- const _regSingles = {};
61
- /**
62
- * Registry to store mocks for Node.js libs in the Test mode.
63
- * @type {Object<string, object>}
64
- */
65
- const _regTestNodeLibs = {};
66
-
67
- let _resolver = new Resolver();
68
-
69
- // FUNCS
70
-
71
- function log() {
72
- if (_debug) console.log(...arguments);
73
- }
74
-
75
- // INSTANCE METHODS
76
-
77
- this.get = async function (depId, stack = []) {
78
- log(`Object '${depId}' is requested.`);
79
- // parse the `objectKey` and get the structured DTO
80
- const parsed = _parser.parse(depId);
81
- // modify the original key according to some rules (replacements, etc.)
82
- const key = _preProcessor.modify(parsed, stack);
83
- // return existing singleton
84
- if (key.life === Defs.LS) {
85
- const singleId = getSingletonId(key);
86
- if (_regSingles[singleId]) {
87
- log(`Existing singleton '${singleId}' is returned.`);
88
- return _regSingles[singleId];
89
- }
90
- }
91
- // return existing node lib in the Test mode
92
- if (key.isNodeModule && _testMode) {
93
- const nodeId = key.origin;
94
- if (_regTestNodeLibs[nodeId]) {
95
- log(`Existing nodejs lib '${nodeId}' is returned.`);
96
- return _regTestNodeLibs[nodeId];
97
- }
98
- }
99
- // resolve a path to es6 module if not resolved before
100
- if (!_regPaths[key.moduleName]) {
101
- log(`ES6 module '${key.moduleName}' is not resolved yet`);
102
- // convert the module name to the path to an es6-module file with a source
103
- _regPaths[key.moduleName] = _resolver.resolve(key.moduleName);
104
- }
105
-
106
- // load es6 module
107
- let module;
108
- const path = _regPaths[key.moduleName];
109
- try {
110
- module = await import(path);
111
- log(`ES6 module '${key.moduleName}' is loaded from '${path}'.`);
112
- } catch (e) {
113
- console.error(e?.message, `Object key: "${depId}".`, `Path: "${path}".`, `Stack: ${JSON.stringify(stack)}`);
114
- throw e;
115
- }
116
- // create an object using the composer, then modify it in post-processor
117
- let res = await _composer.create(key, module, stack, this);
118
- // freeze the result to prevent modifications
119
- if (canBeFrozen(res)) Object.freeze(res);
120
- res = await _postProcessor.modify(res, key, stack);
121
- log(`Object '${depId}' is created.`);
122
-
123
- // save singletons
124
- if (key.life === Defs.LS) {
125
- const singleId = getSingletonId(key);
126
- _regSingles[singleId] = res;
127
- log(`Object '${depId}' is saved as singleton.`);
128
- }
129
- return res;
130
- };
131
-
132
- /**
133
- * Enables test mode, allowing manual singleton registration.
134
- */
135
- this.enableTestMode = function () {
136
- _testMode = true;
137
- log('Test mode enabled');
138
- };
139
-
140
- this.getParser = () => _parser;
141
-
142
- this.getPreProcessor = () => _preProcessor;
143
-
144
- this.getPostProcessor = () => _postProcessor;
145
-
146
- this.getResolver = () => _resolver;
147
-
148
- /**
149
- * Registers a new singleton object in the Container.
150
- *
151
- * @param {string} depId - Dependency identifier. Must be a singleton identifier.
152
- * @param {object} obj - The object to register.
153
- */
154
- this.register = function (depId, obj) {
155
- if (!_testMode) throw new Error('Use enableTestMode() to allow it');
156
-
157
- if (!depId || !obj) throw new Error('Both params are required');
158
-
159
- const key = _parser.parse(depId);
160
- if ((key.life !== Defs.LS) && !key.isNodeModule) throw new Error(`Only node modules & singletons can be registered: '${depId}'`);
161
- if (key.life === Defs.LS) {
162
- const singleId = getSingletonId(key);
163
- if (_regSingles[singleId]) throw new Error(`'${depId}' is already registered`);
164
- _regSingles[singleId] = obj;
165
- } else if (key.isNodeModule) {
166
- const nodeId = key.origin;
167
- if (_regTestNodeLibs[nodeId]) throw new Error(`'${depId}' is already registered`);
168
- _regTestNodeLibs[nodeId] = obj;
169
- }
170
-
171
- log(`'${depId}' is registered`);
172
- };
173
-
174
- this.setDebug = function (data) {
175
- _debug = data;
176
- _composer.setDebug(data);
177
- };
178
-
179
- this.setParser = (data) => _parser = data;
180
-
181
- this.setPreProcessor = (data) => _preProcessor = data;
182
-
183
- this.setPostProcessor = (data) => _postProcessor = data;
184
-
185
- this.setResolver = (data) => _resolver = data;
186
- }
187
- };