@teqfw/di 0.32.0 → 0.34.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/RELEASE.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @teqfw/di releases
2
2
 
3
+ ## 0.34.0 – Strict Mode and Test Support
4
+
5
+ - Removed outdated interface `TeqFw_Di_Api_Container` and associated documentation.
6
+ - Unified the `get()` and `compose()` methods for dependency retrieval.
7
+ - Introduced strict mode by default: manual registration of dependencies is now **disabled** unless explicitly allowed.
8
+ - Added `enableTestMode()` method to the container, enabling safe, test-specific manual registration of singleton
9
+ dependencies.
10
+ - Improved error messages and safeguards against accidental overwrites in `register()`.
11
+ - Updated internal naming and documentation for better clarity and alignment with the platform's principles.
12
+ - Cleaned up ESLint and JSDoc inconsistencies in internal files.
13
+
14
+ ## 0.33.0 – Cleanup and Enhancements
15
+
16
+ - Removed deprecated documentation.
17
+ - Updated ESLint rules for consistency with current platform practices.
18
+ - Introduced `#` as a separator (in addition to `.`) between namespace and export names.
19
+
3
20
  ## 0.32.0 - added support for the `node:` prefix
4
21
 
5
22
  - **Added ability** to manually register objects in the container (`register`).
@@ -78,6 +95,6 @@
78
95
  ## 0.8.0
79
96
 
80
97
  * docs for plugin's teq-descriptor (see in `main` branch);
81
- * use object notation instead of array notation in namespace replacement statements of teq-descriptor (
82
- `@teqfw/di.replace` node format is changed in `./teqfw.json`);
98
+ * use object notation instead of array notation in namespace replacement statements of
99
+ teq-descriptor (`@teqfw/di.replace` node format is changed in `./teqfw.json`);
83
100
  * array is used as a container for upline dependencies in the 'SpecProxy' (object was);
@@ -9,5 +9,6 @@ rm -fr "${DIR_ROOT}/doc/"
9
9
  rm -fr "${DIR_ROOT}/docs/"
10
10
  rm -fr "${DIR_ROOT}/example/"
11
11
  rm -fr "${DIR_ROOT}/node_modules/"
12
+ rm -fr "${DIR_ROOT}/package-lock.json"
12
13
  rm -fr "${DIR_ROOT}/test/"
13
14
  rm -fr "${DIR_ROOT}/tmp/"
package/dist/esm.js CHANGED
@@ -1 +1 @@
1
- var e={COMP_A:"A",COMP_F:"F",ID:"container",ID_FQN:"TeqFw_Di_Container$",LIFE_I:"I",LIFE_S:"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.value))throw new Error(`Circular dependency for '${o.value}'. Parents are: ${JSON.stringify(r)}`);if(o.exportName){const a=[...r,o.value],{[o.exportName]:u}=s;if(o.composition===e.COMP_F){if("function"==typeof u){const s=n(u);s.length&&(c=`Deps for object '${o.value}' are: ${JSON.stringify(s)}`,t&&console.log(c));const r={};for(const e of s)r[e]=await i.compose(e,a);const l=e.isClass(u)?new u(r):u(r);return l instanceof Promise?await l:l}return Object.assign({},u)}return u}return s;var c},this.setDebug=function(e){t=e}}}class i{exportName;composition;life;moduleName;value;wrappers=[]}const c=/^((([A-Z])[A-Za-z0-9_]*)((\.)?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?)$/;class a{canParse(e){return!0}parse(t){const o=new i;o.value=t;const s=c.exec(t);if(s&&(o.moduleName=s[2],"."===s[5]?"$"===s[7]||"$$"===s[7]?(o.composition=e.COMP_F,o.exportName=s[6],o.life="$"===s[7]?e.LIFE_S:e.LIFE_I):(o.composition=e.COMP_A,o.life=e.LIFE_S,o.exportName=""!==s[6]?s[6]:"default"):"$"===s[7]||"$$"===s[7]?(o.composition=e.COMP_F,o.exportName="default",o.life="$"===s[7]?e.LIFE_S:e.LIFE_I):(o.composition=void 0,o.exportName=void 0,o.life=void 0),s[11]&&(o.wrappers=s[11].split(","))),o.composition===e.COMP_A&&o.life===e.LIFE_I)throw new Error(`Export is not a function and should be used as a singleton only: '${o.value}'.`);return o}}class u{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 l{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 p="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]={ext:r??"js",ns:s,[p]: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][p],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 d(e){return`${e.moduleName}#${e.exportName}`}class h{constructor(){let t=new r,o=!1,s=new u,n=new l,i=new f;const c={},a={};let p=new m;function h(){o&&console.log(...arguments)}this.get=async function(e,t=[]){return this.compose(e,t)},this.compose=async function(o,r=[]){if(h(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return h("Container itself is returned."),a[e.ID];const u=s.parse(o),l=n.modify(u,r);if(l.life===e.LIFE_S){const e=d(l);if(a[e])return h(`Existing singleton '${e}' is returned.`),a[e]}let f;c[l.moduleName]||(h(`ES6 module '${l.moduleName}' is not resolved yet`),c[l.moduleName]=p.resolve(l.moduleName));const m=c[l.moduleName];try{f=await import(m),h(`ES6 module '${l.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(l,f,r,this);if($=await i.modify($,l,r),h(`Object '${o}' is created.`),l.life===e.LIFE_S){const e=d(l);a[e]=$,h(`Object '${o}' is saved as singleton.`)}return $},this.getParser=()=>s,this.getPreProcessor=()=>n,this.getPostProcessor=()=>i,this.getResolver=()=>p,this.setDebug=function(e){o=e,t.setDebug(e)},this.setParser=e=>s=e,this.setPreProcessor=e=>n=e,this.setPostProcessor=e=>i=e,this.setResolver=e=>p=e,a[e.ID]=this}}export{h as default};
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;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 $(e){return`${e.moduleName}#${e.exportName}`}class g{constructor(){let t=new r,o=!1,n=new l,s=new f,i=new u,c=!1;const a={},d={};let p=new m;function h(){o&&console.log(...arguments)}this.get=async function(o,r=[]){if(h(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return h("Container itself is returned."),d[e.ID];const c=n.parse(o),l=i.modify(c,r);if(l.life===e.LS){const e=$(l);if(d[e])return h(`Existing singleton '${e}' is returned.`),d[e]}let u;a[l.moduleName]||(h(`ES6 module '${l.moduleName}' is not resolved yet`),a[l.moduleName]=p.resolve(l.moduleName));const f=a[l.moduleName];try{u=await import(f),h(`ES6 module '${l.moduleName}' is loaded from '${f}'.`)}catch(e){throw console.error(e?.message,`Object key: "${o}".`,`Path: "${f}".`,`Stack: ${JSON.stringify(r)}`),e}let m=await t.create(l,u,r,this);var g;if(null===(g=m)||"object"!=typeof g&&"function"!=typeof g||"[object Module]"===Object.prototype.toString.call(g)||Object.isFrozen(g)||Object.freeze(m),m=await s.modify(m,l,r),h(`Object '${o}' is created.`),l.life===e.LS){const e=$(l);d[e]=m,h(`Object '${o}' is saved as singleton.`)}return m},this.enableTestMode=function(){c=!0,h("Test mode enabled")},this.getParser=()=>n,this.getPreProcessor=()=>i,this.getPostProcessor=()=>s,this.getResolver=()=>p,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)throw new Error(`Only singletons can be registered: '${t}'`);const r=$(s);if(d[r])throw new Error(`'${t}' is already registered`);d[r]=o,h(`'${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=>p=e,d[e.ID]=this}}export{g as default};
package/dist/umd.js CHANGED
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).TeqFw_Di_Container=t()}(this,(function(){"use strict";var e={COMP_A:"A",COMP_F:"F",ID:"container",ID_FQN:"TeqFw_Di_Container$",LIFE_I:"I",LIFE_S:"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.value))throw new Error(`Circular dependency for '${o.value}'. Parents are: ${JSON.stringify(r)}`);if(o.exportName){const a=[...r,o.value],{[o.exportName]:u}=n;if(o.composition===e.COMP_F){if("function"==typeof u){const n=s(u);n.length&&(c=`Deps for object '${o.value}' are: ${JSON.stringify(n)}`,t&&console.log(c));const r={};for(const e of n)r[e]=await i.compose(e,a);const l=e.isClass(u)?new u(r):u(r);return l instanceof Promise?await l:l}return Object.assign({},u)}return u}return n;var c},this.setDebug=function(e){t=e}}}class i{exportName;composition;life;moduleName;value;wrappers=[]}const c=/^((([A-Z])[A-Za-z0-9_]*)((\.)?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?)$/;class a{canParse(e){return!0}parse(t){const o=new i;o.value=t;const n=c.exec(t);if(n&&(o.moduleName=n[2],"."===n[5]?"$"===n[7]||"$$"===n[7]?(o.composition=e.COMP_F,o.exportName=n[6],o.life="$"===n[7]?e.LIFE_S:e.LIFE_I):(o.composition=e.COMP_A,o.life=e.LIFE_S,o.exportName=""!==n[6]?n[6]:"default"):"$"===n[7]||"$$"===n[7]?(o.composition=e.COMP_F,o.exportName="default",o.life="$"===n[7]?e.LIFE_S:e.LIFE_I):(o.composition=void 0,o.exportName=void 0,o.life=void 0),n[11]&&(o.wrappers=n[11].split(","))),o.composition===e.COMP_A&&o.life===e.LIFE_I)throw new Error(`Export is not a function and should be used as a singleton only: '${o.value}'.`);return o}}class u{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 l{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 p="root";class d{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]={ext:r??"js",ns:n,[p]: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][p],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 m(e){return`${e.moduleName}#${e.exportName}`}return class{constructor(){let t=new r,o=!1,n=new u,s=new l,i=new f;const c={},a={};let p=new d;function h(){o&&console.log(...arguments)}this.get=async function(e,t=[]){return this.compose(e,t)},this.compose=async function(o,r=[]){if(h(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return h("Container itself is returned."),a[e.ID];const u=n.parse(o),l=s.modify(u,r);if(l.life===e.LIFE_S){const e=m(l);if(a[e])return h(`Existing singleton '${e}' is returned.`),a[e]}let f;c[l.moduleName]||(h(`ES6 module '${l.moduleName}' is not resolved yet`),c[l.moduleName]=p.resolve(l.moduleName));const d=c[l.moduleName];try{f=await import(d),h(`ES6 module '${l.moduleName}' is loaded from '${d}'.`)}catch(e){throw console.error(e?.message,`Object key: "${o}".`,`Path: "${d}".`,`Stack: ${JSON.stringify(r)}`),e}let $=await t.create(l,f,r,this);if($=await i.modify($,l,r),h(`Object '${o}' is created.`),l.life===e.LIFE_S){const e=m(l);a[e]=$,h(`Object '${o}' is saved as singleton.`)}return $},this.getParser=()=>n,this.getPreProcessor=()=>s,this.getPostProcessor=()=>i,this.getResolver=()=>p,this.setDebug=function(e){o=e,t.setDebug(e)},this.setParser=e=>n=e,this.setPreProcessor=e=>s=e,this.setPostProcessor=e=>i=e,this.setResolver=e=>p=e,a[e.ID]=this}}}));
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]:u}=n;if(o.composition===e.CF){if("function"==typeof u){const n=s(u);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 l=e.isClass(u)?new u(r):u(r);return l instanceof Promise?await l:l}return Object.assign({},u)}return u}return n;var c},this.setDebug=function(e){t=e}}}class i{exportName;composition;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 u{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 l{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 u,s=new f,i=new l,c=!1;const a={},d={};let p=new m;function h(){o&&console.log(...arguments)}this.get=async function(o,r=[]){if(h(`Object '${o}' is requested.`),o===e.ID||o===e.ID_FQN)return h("Container itself is returned."),d[e.ID];const c=n.parse(o),u=i.modify(c,r);if(u.life===e.LS){const e=g(u);if(d[e])return h(`Existing singleton '${e}' is returned.`),d[e]}let l;a[u.moduleName]||(h(`ES6 module '${u.moduleName}' is not resolved yet`),a[u.moduleName]=p.resolve(u.moduleName));const f=a[u.moduleName];try{l=await import(f),h(`ES6 module '${u.moduleName}' is loaded from '${f}'.`)}catch(e){throw console.error(e?.message,`Object key: "${o}".`,`Path: "${f}".`,`Stack: ${JSON.stringify(r)}`),e}let m=await t.create(u,l,r,this);var w;if(null===(w=m)||"object"!=typeof w&&"function"!=typeof w||"[object Module]"===Object.prototype.toString.call(w)||Object.isFrozen(w)||Object.freeze(m),m=await s.modify(m,u,r),h(`Object '${o}' is created.`),u.life===e.LS){const e=g(u);d[e]=m,h(`Object '${o}' is saved as singleton.`)}return m},this.enableTestMode=function(){c=!0,h("Test mode enabled")},this.getParser=()=>n,this.getPreProcessor=()=>i,this.getPostProcessor=()=>s,this.getResolver=()=>p,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)throw new Error(`Only singletons can be registered: '${t}'`);const r=g(s);if(d[r])throw new Error(`'${t}' is already registered`);d[r]=o,h(`'${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=>p=e,d[e.ID]=this}}}));
@@ -0,0 +1,37 @@
1
+ // eslint.config.js
2
+ import js from '@eslint/js';
3
+
4
+ /** @type {import('eslint').Linter.FlatConfig[]} */
5
+ export default [
6
+ js.configs.recommended,
7
+ {
8
+ languageOptions: {
9
+ ecmaVersion: 2022, // required for public class fields
10
+ sourceType: 'module',
11
+ globals: {
12
+ console: 'readonly',
13
+ },
14
+ },
15
+ rules: {
16
+ 'camelcase': [
17
+ 'warn',
18
+ {
19
+ properties: 'never',
20
+ ignoreDestructuring: true,
21
+ allow: ['^([A-Z][a-zA-Z0-9]*_)+[A-Z][a-zA-Z0-9]*$'],
22
+ },
23
+ ],
24
+ 'indent': ['error', 4],
25
+ 'linebreak-style': ['error', 'unix'],
26
+ 'quotes': ['error', 'single'],
27
+ 'semi': ['error', 'always'],
28
+ },
29
+ },
30
+ // Special rule override for interface declarations
31
+ {
32
+ files: ['./src/Api/**/*.js'],
33
+ rules: {
34
+ 'no-unused-vars': 'off',
35
+ },
36
+ },
37
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teqfw/di",
3
- "version": "0.32.0",
3
+ "version": "0.34.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",
@@ -28,13 +28,16 @@
28
28
  },
29
29
  "scripts": {
30
30
  "rollup": "rollup -c",
31
+ "eslint": "eslint './src/**/*.js'",
31
32
  "test": "mocha --recursive './test/**/*.test.mjs'"
32
33
  },
33
34
  "devDependencies": {
35
+ "@eslint/js": "^9.25.0",
34
36
  "@rollup/plugin-node-resolve": "^16.0.1",
37
+ "@rollup/plugin-terser": "^0.4.4",
38
+ "eslint": "^9.25.0",
35
39
  "mocha": "^11.1.0",
36
- "rollup": "^4.36.0",
37
- "@rollup/plugin-terser": "^0.4.4"
40
+ "rollup": "^4.36.0"
38
41
  },
39
42
  "mocha": {
40
43
  "spec": "./test/**/*.test.mjs",
@@ -8,8 +8,8 @@
8
8
  import Defs from '../../../../Defs.js';
9
9
 
10
10
  // VARS
11
- const FUNC = /(function)*\s*\w*\s*\(\s*\{([^\}]*)\}/s;
12
- const CLASS = /constructor\s*\(\s*\{([^\}]*)\}/s;
11
+ const FUNC = /(function)*\s*\w*\s*\(\s*\{([^}]*)}/s;
12
+ const CLASS = /constructor\s*\(\s*\{([^}]*)}/s;
13
13
 
14
14
  // FUNCS
15
15
 
@@ -32,7 +32,7 @@ function _analyze(params) {
32
32
  fn(spec);
33
33
  } catch (e) {
34
34
  const msg = `Cannot analyze the deps specification:${params}\n`
35
- + `\nPlease, be sure that spec does not contain extra ')' in a comments.`
35
+ + '\nPlease, be sure that spec does not contain extra \')\' in a comments.'
36
36
  + `\n\nError: ${e}`;
37
37
  throw new Error(msg);
38
38
  }
@@ -23,7 +23,7 @@ export default class TeqFw_Di_Container_A_Composer {
23
23
  * Returns or creates and returns the requested object.
24
24
  *
25
25
  * @param {TeqFw_Di_DepId} depId
26
- * @param {Object} module - imported es6 module
26
+ * @param {object} module - imported es6 module
27
27
  * @param {string[]} stack - array of the parent objects IDs to prevent dependency loop
28
28
  * @param {TeqFw_Di_Container} container - to create dependencies for requested object
29
29
  * @returns {Promise<*>}
@@ -42,7 +42,7 @@ export default class TeqFw_Di_Container_A_Composer {
42
42
  if (deps.length) log(`Deps for object '${depId.origin}' are: ${JSON.stringify(deps)}`);
43
43
  const spec = {};
44
44
  for (const dep of deps)
45
- spec[dep] = await container.compose(dep, stackNew);
45
+ spec[dep] = await container.get(dep, stackNew);
46
46
  // create a new object with the factory function
47
47
  const res = (Defs.isClass(exp)) ? new exp(spec) : exp(spec);
48
48
  if (res instanceof Promise)
@@ -10,16 +10,16 @@ import Dto from '../../../../DepId.js';
10
10
  import Defs from '../../../../Defs.js';
11
11
 
12
12
  // VARS
13
- /** @type {RegExp} expression for default object key */
14
- const REGEXP = /^(node:)?((@?[A-Za-z0-9_\-]+\/?[A-Za-z0-9_\-]*))((\.)?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?$/;
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
15
 
16
16
  /**
17
17
  * @implements TeqFw_Di_Api_Container_Parser_Chunk
18
18
  */
19
19
  export default class TeqFw_Di_Container_A_Parser_Chunk_Def {
20
20
 
21
- canParse(depId) {
22
- // default parser always tries to parse the depId
21
+ canParse() {
22
+ // the default parser always tries to parse the depId
23
23
  return true;
24
24
  }
25
25
 
@@ -31,22 +31,22 @@ export default class TeqFw_Di_Container_A_Parser_Chunk_Def {
31
31
  res.isNodeModule = Boolean(parts[1]); // Detect 'node:' prefix
32
32
  res.moduleName = parts[2].replace(/^node:/, ''); // Remove 'node:' prefix
33
33
 
34
- if (parts[5] === '.') {
34
+ if ((parts[4] === '.') || (parts[4] === '#')) {
35
35
  // Ns_Module.export or node:package.export
36
- if ((parts[7] === '$') || (parts[7] === '$$')) {
36
+ if ((parts[6] === '$') || (parts[6] === '$$')) {
37
37
  res.composition = Defs.CF;
38
- res.exportName = parts[6];
39
- res.life = (parts[7] === '$') ? Defs.LS : Defs.LI;
38
+ res.exportName = parts[5];
39
+ res.life = (parts[6] === '$') ? Defs.LS : Defs.LI;
40
40
  } else {
41
41
  res.composition = Defs.CA;
42
42
  res.life = Defs.LS;
43
- res.exportName = (parts[6] !== '') ? parts[6] : 'default';
43
+ res.exportName = (parts[5] !== '') ? parts[5] : 'default';
44
44
  }
45
- } else if ((parts[7] === '$') || parts[7] === '$$') {
45
+ } else if ((parts[6] === '$') || parts[6] === '$$') {
46
46
  // Ns_Module$$ or node:package$$
47
47
  res.composition = Defs.CF;
48
48
  res.exportName = 'default';
49
- res.life = (parts[7] === '$') ? Defs.LS : Defs.LI;
49
+ res.life = (parts[6] === '$') ? Defs.LS : Defs.LI;
50
50
  } else {
51
51
  // Ns_Module or node:package (ES6 module)
52
52
  res.composition = undefined;
@@ -55,8 +55,8 @@ export default class TeqFw_Di_Container_A_Parser_Chunk_Def {
55
55
  }
56
56
 
57
57
  // Wrappers handling
58
- if (parts[11]) {
59
- res.wrappers = parts[11].split(',');
58
+ if (parts[10]) {
59
+ res.wrappers = parts[10].split(',');
60
60
  }
61
61
  }
62
62
 
@@ -9,16 +9,16 @@ import Dto from '../../../../DepId.js';
9
9
  import Defs from '../../../../Defs.js';
10
10
 
11
11
  // VARS
12
- /** @type {RegExp} expression for 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])?)?)?$/;
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
14
 
15
15
  /**
16
16
  * @implements TeqFw_Di_Api_Container_Parser_Chunk
17
17
  */
18
18
  export default class TeqFw_Di_Container_A_Parser_Chunk_V02X {
19
19
 
20
- canParse(depId) {
21
- // default parser always tries to parse the depId
20
+ canParse() {
21
+ // the default parser always tries to parse the depId
22
22
  return true;
23
23
  }
24
24
 
@@ -30,29 +30,30 @@ export default class TeqFw_Di_Container_A_Parser_Chunk_V02X {
30
30
  res.isNodeModule = Boolean(parts[1]); // Check if it starts with 'node:'
31
31
  res.moduleName = parts[2].replace(/^node:/, ''); // Remove 'node:' if present
32
32
 
33
- if (parts[6] === '.') {
33
+ if (parts[5] === '.') {
34
34
  // App_Service.export or node:package.export
35
- if (parts[9] === '$') {
35
+ if (parts[8] === '$') {
36
36
  // App_Service.export$ or node:package.export$
37
37
  res.composition = Defs.CF;
38
- res.exportName = parts[7];
39
- res.life = (parts[11] === Defs.LI) ? Defs.LI : Defs.LS;
38
+ res.exportName = parts[6];
39
+ res.life = (parts[10] === Defs.LI) ? Defs.LI : Defs.LS;
40
40
  } else {
41
- res.composition = (!parts[9] || parts[9] === Defs.CA) ? Defs.CA : Defs.CF;
42
- res.exportName = parts[7];
43
- res.life = (!parts[9] || parts[11] === Defs.LS) ? Defs.LS : Defs.LI;
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
44
  }
45
- } else if (parts[9] === '$') {
45
+ } else if (parts[8] === '$') {
46
46
  // App_Logger$FS or node:package$
47
- res.composition = (!parts[10] || parts[10] === Defs.CF) ? Defs.CF : Defs.CA;
47
+ res.composition = (!parts[9] || parts[9] === Defs.CF) ? Defs.CF : Defs.CA;
48
48
  res.exportName = 'default';
49
- res.life = parts[11] ? (parts[11] === Defs.LS ? Defs.LS : Defs.LI) : (res.composition === Defs.CF ? Defs.LS : Defs.LI);
49
+ res.life = parts[10] ? (parts[10] === Defs.LS ? Defs.LS : Defs.LI) : (res.composition === Defs.CF ? Defs.LS : Defs.LI);
50
50
  } else {
51
51
  // App_Service or node:package (ES6 module)
52
52
  res.composition = undefined;
53
53
  res.exportName = undefined;
54
54
  res.life = undefined;
55
55
  }
56
+
56
57
  }
57
58
 
58
59
  // Enforce singleton for non-factory exports
package/src/Container.js CHANGED
@@ -1,5 +1,7 @@
1
1
  /**
2
2
  * The Object Container (composition root).
3
+ * We can use static imports in the Container.
4
+ *
3
5
  * @namespace TeqFw_Di_Container
4
6
  */
5
7
  import Composer from './Container/A/Composer.js';
@@ -33,11 +35,7 @@ function canBeFrozen(value) {
33
35
  return !Object.isFrozen(value);
34
36
  }
35
37
 
36
-
37
38
  // MAIN
38
- /**
39
- * @implements TeqFw_Di_Api_Container
40
- */
41
39
  export default class TeqFw_Di_Container {
42
40
 
43
41
  constructor() {
@@ -45,8 +43,9 @@ export default class TeqFw_Di_Container {
45
43
  let _composer = new Composer();
46
44
  let _debug = false;
47
45
  let _parser = new Parser();
48
- let _preProcessor = new PreProcessor();
49
46
  let _postProcessor = new PostProcessor();
47
+ let _preProcessor = new PreProcessor();
48
+ let _testMode = false;
50
49
 
51
50
  /**
52
51
  * Registry for paths for loaded es6 modules.
@@ -56,7 +55,7 @@ export default class TeqFw_Di_Container {
56
55
  const _regPaths = {};
57
56
  /**
58
57
  * Registry to store singletons.
59
- * @type {Object<string, *>}
58
+ * @type {Object<string, object>}
60
59
  */
61
60
  const _regSingles = {};
62
61
  let _resolver = new Resolver();
@@ -69,30 +68,19 @@ export default class TeqFw_Di_Container {
69
68
 
70
69
  // INSTANCE METHODS
71
70
 
72
- this.get = async function (runtimeDepId, stack = []) {
73
- return this.compose(runtimeDepId, stack);
74
- };
75
-
76
- /**
77
- * This method is 'private' for the npm package. It is used in the Composer only.
78
- *
79
- * @param {string} depId runtime dependency ID
80
- * @param {string[]} stack set of the depId to detect circular dependencies
81
- * @returns {Promise<*>}
82
- */
83
- this.compose = async function (depId, stack = []) {
71
+ this.get = async function (depId, stack = []) {
84
72
  log(`Object '${depId}' is requested.`);
85
- // return container itself if requested
73
+ // return the container itself if requested
86
74
  if (
87
75
  (depId === Defs.ID) ||
88
76
  (depId === Defs.ID_FQN)
89
77
  ) {
90
- log(`Container itself is returned.`);
78
+ log('Container itself is returned.');
91
79
  return _regSingles[Defs.ID];
92
80
  }
93
81
  // parse the `objectKey` and get the structured DTO
94
82
  const parsed = _parser.parse(depId);
95
- // modify original key according to some rules (replacements, etc.)
83
+ // modify the original key according to some rules (replacements, etc.)
96
84
  const key = _preProcessor.modify(parsed, stack);
97
85
  // return existing singleton
98
86
  if (key.life === Defs.LS) {
@@ -102,10 +90,10 @@ export default class TeqFw_Di_Container {
102
90
  return _regSingles[singleId];
103
91
  }
104
92
  }
105
- // resolve path to es6 module if not resolved before
93
+ // resolve a path to es6 module if not resolved before
106
94
  if (!_regPaths[key.moduleName]) {
107
95
  log(`ES6 module '${key.moduleName}' is not resolved yet`);
108
- // convert module name to the path to es6-module file with a sources
96
+ // convert the module name to the path to an es6-module file with a source
109
97
  _regPaths[key.moduleName] = _resolver.resolve(key.moduleName);
110
98
  }
111
99
 
@@ -124,9 +112,9 @@ export default class TeqFw_Di_Container {
124
112
  );
125
113
  throw e;
126
114
  }
127
- // create object using the composer then modify it in post-processor
115
+ // create an object using the composer, then modify it in post-processor
128
116
  let res = await _composer.create(key, module, stack, this);
129
- // freeze the result to prevent modifications (TODO: should we have configuration for the feature?)
117
+ // freeze the result to prevent modifications
130
118
  if (canBeFrozen(res)) Object.freeze(res);
131
119
  res = await _postProcessor.modify(res, key, stack);
132
120
  log(`Object '${depId}' is created.`);
@@ -140,6 +128,14 @@ export default class TeqFw_Di_Container {
140
128
  return res;
141
129
  };
142
130
 
131
+ /**
132
+ * Enables test mode, allowing manual singleton registration.
133
+ */
134
+ this.enableTestMode = function () {
135
+ _testMode = true;
136
+ log('Test mode enabled');
137
+ };
138
+
143
139
  this.getParser = () => _parser;
144
140
 
145
141
  this.getPreProcessor = () => _preProcessor;
@@ -149,21 +145,28 @@ export default class TeqFw_Di_Container {
149
145
  this.getResolver = () => _resolver;
150
146
 
151
147
  /**
152
- * Register new object in the Container.
153
- * @param {string} depId
154
- * @param {Object} obj
148
+ * Registers a new singleton object in the Container.
149
+ *
150
+ * @param {string} depId - Dependency identifier. Must be a singleton identifier.
151
+ * @param {object} obj - The object to register.
155
152
  */
156
153
  this.register = function (depId, obj) {
157
- if (!depId || !obj) throw new Error('depId and object are required');
154
+ if (!_testMode)
155
+ throw new Error('Use enableTestMode() to allow it');
156
+
157
+ if (!depId || !obj)
158
+ throw new Error('Both params are required');
159
+
158
160
  const key = _parser.parse(depId);
159
- if (key.life === Defs.LS) {
160
- const singleId = getSingletonId(key);
161
- _regSingles[singleId] = obj;
162
- log(`Object '${depId}' is registered manually as singleton.`);
163
- } else {
164
- // TODO: factory function also should be added manually
165
- throw new Error(`Only singletons can be registered manually. Given: ${depId}`);
166
- }
161
+ if (key.life !== Defs.LS)
162
+ throw new Error(`Only singletons can be registered: '${depId}'`);
163
+
164
+ const singleId = getSingletonId(key);
165
+ if (_regSingles[singleId])
166
+ throw new Error(`'${depId}' is already registered`);
167
+
168
+ _regSingles[singleId] = obj;
169
+ log(`'${depId}' is registered`);
167
170
  };
168
171
 
169
172
  this.setDebug = function (data) {
package/.eslintrc.mjs DELETED
File without changes
@@ -1,69 +0,0 @@
1
- /**
2
- * Interface for the Object Container.
3
- *
4
- * This is not executable code, it is just for documentation purposes (similar to .h files in the C/C++ language).
5
- * @interface
6
- */
7
- export default class TeqFw_Di_Api_Container {
8
- /**
9
- * Gets or creates a runtime object by ID.
10
- *
11
- * @param {string} runtimeDepId - The ID of the runtime object.
12
- * @param {string[]} [stack]
13
- * @returns {Promise<*>} - A promise that resolves to the runtime object.
14
- */
15
- get(runtimeDepId, stack) {};
16
-
17
- /**
18
- * @returns {TeqFw_Di_Api_Container_Parser}
19
- */
20
- getParser() {};
21
-
22
- /**
23
- * @returns {TeqFw_Di_Api_Container_PreProcessor}
24
- */
25
- getPreProcessor() {};
26
-
27
- /**
28
- * @returns {TeqFw_Di_Api_Container_PostProcessor}
29
- */
30
- getPostProcessor() {};
31
-
32
- /**
33
- * @returns {TeqFw_Di_Container_Resolver} - the default resolver
34
- */
35
- getResolver() {};
36
-
37
- /**
38
- * Registers an object (module, singleton, factory, or prototype) by dependency ID.
39
- * @param {string} depId
40
- * @param {Object} obj
41
- */
42
- register(depId, obj) {};
43
-
44
- /**
45
- * Enable disable debug output for the object composition process.
46
- * @param {boolean} data
47
- */
48
- setDebug(data) {};
49
-
50
- /**
51
- * @param {TeqFw_Di_Api_Container_Parser} data
52
- */
53
- setParser(data) {};
54
-
55
- /**
56
- * @param {TeqFw_Di_Api_Container_PreProcessor} data
57
- */
58
- setPreProcessor(data) {};
59
-
60
- /**
61
- * @param {TeqFw_Di_Api_Container_PostProcessor} data
62
- */
63
- setPostProcessor(data) {};
64
-
65
- /**
66
- * @param {TeqFw_Di_Api_Container_Resolver} data
67
- */
68
- setResolver(data) {};
69
- };