@teqfw/di 0.36.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/esm.js +1 -1
- package/dist/umd.js +1 -1
- package/package.json +25 -10
- package/src/Container.js +0 -8
- package/src/Defs.js +0 -3
- package/PHILOSOPHY.md +0 -175
- package/RELEASE.md +0 -116
- package/bin/release/clean.sh +0 -14
- package/eslint.config.js +0 -37
- package/rollup.config.js +0 -21
- package/teqfw.json +0 -9
package/README.md
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
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.
|
package/dist/esm.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e={CA:"A",CF:"F",
|
|
1
|
+
var e={CA:"A",CF:"F",LI:"I",LS:"S",isClass(e){const t=Object.getOwnPropertyDescriptor(e,"prototype");return t&&!t.writable}};const t=/(function)*\s*\w*\s*\(\s*\{([^}]*)}/s,o=/constructor\s*\(\s*\{([^}]*)}/s;function s(e){const t=[];try{const o=new Function(`{${e}}`,"return");o(new Proxy({},{get:(e,o)=>t.push(o)}))}catch(t){throw new Error(`Cannot analyze the deps specification:${e}\n\nPlease, be sure that spec does not contain extra ')' in a comments.\n\nError: ${t}`)}return t}function n(n){return"function"==typeof n?e.isClass(n)?function(e){const t=[],n=e.toString(),r=o.exec(n);return r&&t.push(...s(r[1])),t}(n):function(e){const o=[],n=e.toString(),r=t.exec(n);return r&&o.push(...s(r[2])),o}(n):[]}class r{constructor(){let t=!1;this.create=async function(o,s,r,i){if(r.includes(o.origin))throw new Error(`Circular dependency for '${o.origin}'. Parents are: ${JSON.stringify(r)}`);if(o.exportName){const a=[...r,o.origin],{[o.exportName]:l}=s;if(o.composition===e.CF){if("function"==typeof l){const s=n(l);s.length&&(c=`Deps for object '${o.origin}' are: ${JSON.stringify(s)}`,t&&console.log(c));const r={};for(const e of s)r[e]=await i.get(e,a);const u=e.isClass(l)?new l(r):l(r);return u instanceof Promise?await u:u}return Object.assign({},l)}return l}return s;var c},this.setDebug=function(e){t=e}}}class i{exportName;composition;isNodeModule;life;moduleName;origin;wrappers=[]}const c=/^(node:)?(@?[A-Za-z0-9_-]+\/?[A-Za-z0-9_-]*)(([.#])?([A-Za-z0-9_]*)((\$)?(\$)?)?)?(\(([A-Za-z0-9_,]*)\))?$/;class a{canParse(){return!0}parse(t){const o=new i;o.origin=t;const s=c.exec(t);if(s&&(o.isNodeModule=Boolean(s[1]),o.moduleName=s[2].replace(/^node:/,""),"."===s[4]||"#"===s[4]?"$"===s[6]||"$$"===s[6]?(o.composition=e.CF,o.exportName=s[5],o.life="$"===s[6]?e.LS:e.LI):(o.composition=e.CA,o.life=e.LS,o.exportName=""!==s[5]?s[5]:"default"):"$"===s[6]||"$$"===s[6]?(o.composition=e.CF,o.exportName="default",o.life="$"===s[6]?e.LS:e.LI):(o.composition=void 0,o.exportName=void 0,o.life=void 0),s[10]&&(o.wrappers=s[10].split(","))),o.composition===e.CA&&o.life===e.LI)throw new Error(`Export is not a function and should be used as a singleton only: '${o.origin}'.`);return o}}class l{constructor(){let e=new a;const t=[];this.addChunk=function(e){t.push(e)},this.parse=function(o){let s;for(const e of t)if(e.canParse(o)){s=e.parse(o);break}return s||(s=e?.parse(o)),s},this.setDefaultChunk=function(t){e=t}}}class u{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=function(t,o){let s=t;for(const n of e)s=n.modify(s,t,o);return s}}}class f{constructor(){const e=[];this.addChunk=function(t){e.push(t)},this.modify=async function(t,o,s){let n=t;for(const t of e)n=t.modify(n,o,s),n instanceof Promise&&(n=await n);return n}}}const d="ext",p="ns",h="root";class m{constructor(){const e={};let t=!1,o=[],s="/";this.addNamespaceRoot=function(s,n,r){const i=(t?n.replace(/^\\/,""):n).replace(/\\/g,"/"),c=t?`file://${i}`:i;e[s]={[d]:r??"js",[p]:s,[h]:c},o=Object.keys(e).sort((e,t)=>t.localeCompare(e))},this.resolve=function(t){let n,r,i;for(i of o)if(t.startsWith(i)){n=e[i][h],r=e[i].ext;break}if(n&&r){let e=t.replace(i,"");0===e.indexOf("_")&&(e=e.replace("_",""));const o=e.replaceAll("_",s);return`${n}${s}${o}.${r}`}return t},this.setWindowsEnv=function(e=!0){t=e,s=e?"\\":"/"}}}function g(e){return`${e.moduleName}#${e.exportName}`}class ${constructor(){let t=new r,o=!1,s=new l,n=new f,i=new u,c=!1;const a={},d={},p={};let h=new m;function $(){o&&console.log(...arguments)}this.get=async function(o,r=[]){$(`Object '${o}' is requested.`);const l=s.parse(o),u=i.modify(l,r);if(u.life===e.LS){const e=g(u);if(d[e])return $(`Existing singleton '${e}' is returned.`),d[e]}if(u.isNodeModule&&c){const e=u.origin;if(p[e])return $(`Existing nodejs lib '${e}' is returned.`),p[e]}let f;a[u.moduleName]||($(`ES6 module '${u.moduleName}' is not resolved yet`),a[u.moduleName]=h.resolve(u.moduleName));const m=a[u.moduleName];try{f=await import(m),$(`ES6 module '${u.moduleName}' is loaded from '${m}'.`)}catch(e){throw console.error(e?.message,`Object key: "${o}".`,`Path: "${m}".`,`Stack: ${JSON.stringify(r)}`),e}let w=await t.create(u,f,r,this);var y;if(null===(y=w)||"object"!=typeof y&&"function"!=typeof y||"[object Module]"===Object.prototype.toString.call(y)||Object.isFrozen(y)||Object.freeze(w),w=await n.modify(w,u,r),$(`Object '${o}' is created.`),u.life===e.LS){const e=g(u);d[e]=w,$(`Object '${o}' is saved as singleton.`)}return w},this.enableTestMode=function(){c=!0,$("Test mode enabled")},this.getParser=()=>s,this.getPreProcessor=()=>i,this.getPostProcessor=()=>n,this.getResolver=()=>h,this.register=function(t,o){if(!c)throw new Error("Use enableTestMode() to allow it");if(!t||!o)throw new Error("Both params are required");const n=s.parse(t);if(n.life!==e.LS&&!n.isNodeModule)throw new Error(`Only node modules & singletons can be registered: '${t}'`);if(n.life===e.LS){const e=g(n);if(d[e])throw new Error(`'${t}' is already registered`);d[e]=o}else if(n.isNodeModule){const e=n.origin;if(p[e])throw new Error(`'${t}' is already registered`);p[e]=o}$(`'${t}' is registered`)},this.setDebug=function(e){o=e,t.setDebug(e)},this.setParser=e=>s=e,this.setPreProcessor=e=>i=e,this.setPostProcessor=e=>n=e,this.setResolver=e=>h=e}}export{$ as default};
|
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,
|
|
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",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=[]){w(`Object '${o}' is requested.`);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}}});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teqfw/di",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.1",
|
|
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",
|
|
@@ -20,7 +20,20 @@
|
|
|
20
20
|
"email": "alex@flancer64.com",
|
|
21
21
|
"url": "https://github.com/flancer64"
|
|
22
22
|
},
|
|
23
|
-
"
|
|
23
|
+
"files": [
|
|
24
|
+
"dist/",
|
|
25
|
+
"src/",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"main": "dist/umd.js",
|
|
30
|
+
"module": "dist/esm.js",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"import": "./dist/esm.js",
|
|
34
|
+
"require": "./dist/umd.js"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
24
37
|
"type": "module",
|
|
25
38
|
"repository": {
|
|
26
39
|
"type": "git",
|
|
@@ -29,18 +42,20 @@
|
|
|
29
42
|
"scripts": {
|
|
30
43
|
"rollup": "rollup -c",
|
|
31
44
|
"eslint": "eslint './src/**/*.js'",
|
|
32
|
-
"test": "
|
|
45
|
+
"test": "node --test"
|
|
33
46
|
},
|
|
34
47
|
"devDependencies": {
|
|
35
|
-
"@eslint/js": "^9.
|
|
48
|
+
"@eslint/js": "^9.33.0",
|
|
36
49
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
|
37
50
|
"@rollup/plugin-terser": "^0.4.4",
|
|
38
|
-
"eslint": "^9.
|
|
39
|
-
"
|
|
40
|
-
|
|
51
|
+
"eslint": "^9.33.0",
|
|
52
|
+
"rollup": "^4.47.1"
|
|
53
|
+
},
|
|
54
|
+
"engines": {
|
|
55
|
+
"node": ">=20"
|
|
41
56
|
},
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
57
|
+
"funding": {
|
|
58
|
+
"type": "individual",
|
|
59
|
+
"url": "https://github.com/sponsors/teqfw"
|
|
45
60
|
}
|
|
46
61
|
}
|
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
|
|
package/PHILOSOPHY.md
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
# Philosophy of the TeqFW Platform
|
|
2
|
-
|
|
3
|
-
**The philosophy of Tequila Framework (TeqFW)** is my personal approach to organizing web application development. I,
|
|
4
|
-
Alex Gusev, have shaped this approach based on my own experience, which focuses on **modular monoliths** with a single
|
|
5
|
-
database. This document reflects that specific context and does not aim to be universal.
|
|
6
|
-
|
|
7
|
-
Some of the principles presented may be applicable more broadly, while others may be irrelevant (or even
|
|
8
|
-
counterproductive) outside monolithic architectures. It is important to keep this limitation in mind when interpreting
|
|
9
|
-
the material.
|
|
10
|
-
|
|
11
|
-
The document is intended to provide cognitive context for both human and artificial intelligences. It addresses both
|
|
12
|
-
specific aspects of web development and more general software architecture issues, emphasizing the reduction of
|
|
13
|
-
excessive complexity, improved structuring, and adaptability to changes.
|
|
14
|
-
|
|
15
|
-
**Tequila Framework (TeqFW)** is not a finished product but an evolving experimental platform. It serves as a testbed
|
|
16
|
-
for the principles outlined here and is actively used in development.
|
|
17
|
-
|
|
18
|
-
## Core Principles of TeqFW
|
|
19
|
-
|
|
20
|
-
1. **Use a Unified Development Language:** JavaScript (ES6+) is used on both the client and server sides, ensuring code
|
|
21
|
-
integrity, reducing duplication, and lowering cognitive load.
|
|
22
|
-
|
|
23
|
-
2. **Enable Late Binding for Flexibility:** Dynamic dependency management through an object container and ES6 module
|
|
24
|
-
namespaces. This reduces tight coupling between modules, simplifies system expansion, and makes the code more
|
|
25
|
-
adaptable.
|
|
26
|
-
|
|
27
|
-
3. **Design for Evolutionary Code Resilience:** Code is designed with inevitable changes in mind to minimize adaptation
|
|
28
|
-
costs and facilitate expansion without modifying existing components.
|
|
29
|
-
|
|
30
|
-
4. **Separate Data and Logic Functionally:** Isolation of data structures (DTO) and logic handlers. This approach makes
|
|
31
|
-
code easier to test, maintain, and reuse.
|
|
32
|
-
|
|
33
|
-
5. **Use Namespaces for Structure and Isolation:** Each type of entity—npm packages, ES6 modules, database tables, CLI
|
|
34
|
-
commands, or configurations—has its own namespace. This ensures clear project structure, reduces conflicts, and
|
|
35
|
-
simplifies code navigation.
|
|
36
|
-
|
|
37
|
-
6. **Favor Pure JavaScript Without Compilation:** Using modern JavaScript (ES6+) without TypeScript or version
|
|
38
|
-
downgrading. The code remains transparent and accessible, simplifying maintenance, library integration, and speeding
|
|
39
|
-
up development.
|
|
40
|
-
|
|
41
|
-
7. **Optimize Code and Docs for LLMs:** Code and documentation are organized to be easily analyzed and supplemented by
|
|
42
|
-
language models. Clear structure, standardized templates, and predictable conventions simplify automation and code
|
|
43
|
-
generation.
|
|
44
|
-
|
|
45
|
-
## Principles in Detail
|
|
46
|
-
|
|
47
|
-
### Use a Unified Development Language
|
|
48
|
-
|
|
49
|
-
Using modern JavaScript (ES6+) at all application levels eliminates the need to switch between different languages and
|
|
50
|
-
simplifies knowledge sharing among developers. This is especially important in small teams and projects with limited
|
|
51
|
-
resources, where minimizing cognitive load accelerates work.
|
|
52
|
-
|
|
53
|
-
Browsers support only JavaScript (among high-level languages), and thanks to Node.js, it has become widespread in server
|
|
54
|
-
development. This enables writing **isomorphic code** that can be reused on both client and server sides, reducing logic
|
|
55
|
-
duplication.
|
|
56
|
-
|
|
57
|
-
A unified language simplifies code maintenance and accelerates the onboarding of new developers into the project.
|
|
58
|
-
|
|
59
|
-
### Enable Late Binding for Flexibility
|
|
60
|
-
|
|
61
|
-
Late binding ensures architectural flexibility by allowing dynamic dependency management without tight coupling between
|
|
62
|
-
modules. Instead of direct imports, ES6 module namespaces and an object container are used to handle instantiation and
|
|
63
|
-
component replacement at runtime.
|
|
64
|
-
|
|
65
|
-
This approach reduces the risk of application "breakage" due to changes, simplifies system expansion, and makes the code
|
|
66
|
-
more adaptable. Components can be replaced without deep refactoring, and the dependency mechanism remains transparent
|
|
67
|
-
and predictable.
|
|
68
|
-
|
|
69
|
-
Late binding also improves testability: modules can be replaced with stubs or alternative implementations, making it
|
|
70
|
-
easier to isolate tests. In team development, this simplifies understanding of complex dependencies and makes system
|
|
71
|
-
maintenance more manageable.
|
|
72
|
-
|
|
73
|
-
### Design for Evolutionary Code Resilience
|
|
74
|
-
|
|
75
|
-
Code is designed to adapt to inevitable changes in requirements, APIs, and data with minimal overhead. This is achieved
|
|
76
|
-
through approaches that allow **expanding functionality without significant modifications to existing code**.
|
|
77
|
-
|
|
78
|
-
Key techniques:
|
|
79
|
-
|
|
80
|
-
- **Flexible input data processing.** Using function argument destructuring and the "ignore unknown attributes"
|
|
81
|
-
principle in data structures allows adding new parameters and properties without modifying existing handlers.
|
|
82
|
-
- **Clear interaction contracts.** Separating interfaces from implementations reduces the impact of changes while
|
|
83
|
-
maintaining system predictability.
|
|
84
|
-
- **Late binding.** Components depend on abstractions rather than specific implementations, enabling code adaptation
|
|
85
|
-
without directly modifying dependencies (see **Enable Late Binding for Flexibility** principle).
|
|
86
|
-
|
|
87
|
-
These methods make the code less fragile and allow the system to evolve while reducing complexity and refactoring
|
|
88
|
-
volume.
|
|
89
|
-
|
|
90
|
-
### Separate Data and Logic Functionally
|
|
91
|
-
|
|
92
|
-
Code is divided into **data structures (DTO) and logic handlers**, eliminating state within handlers and making them
|
|
93
|
-
independent of data. DTOs contain all necessary information and are passed between handlers that perform operations on
|
|
94
|
-
them.
|
|
95
|
-
|
|
96
|
-
This approach offers several key advantages:
|
|
97
|
-
|
|
98
|
-
- **Handlers can be singletons.** Since they do not store state, they are execution-context-independent and can be
|
|
99
|
-
shared across the entire application.
|
|
100
|
-
- **The program consists of data processing nodes.** The code is structured as a set of functions that receive data,
|
|
101
|
-
process it, and pass it on.
|
|
102
|
-
- **Changeability through pure logic.** Logic remains separate from data structures, allowing modifications without
|
|
103
|
-
affecting handlers and vice versa.
|
|
104
|
-
- **Minimized side effects.** Handlers do not depend on global state, making the system more predictable.
|
|
105
|
-
|
|
106
|
-
### Use Namespaces for Structure and Isolation
|
|
107
|
-
|
|
108
|
-
Namespaces ensure a clear project structure and prevent conflicts by allowing each entity to reserve its name and build
|
|
109
|
-
its own hierarchy. This principle applies at all levels:
|
|
110
|
-
|
|
111
|
-
- **Packages and modules.** npm packages and ES6 modules are organized into predictable namespaces, avoiding dependency
|
|
112
|
-
conflicts.
|
|
113
|
-
- **Files and classes.** File and class names reflect their purpose and relationships with other components, simplifying
|
|
114
|
-
project navigation and structure.
|
|
115
|
-
- **Database tables.** Table names are structured to avoid collisions and logically group data.
|
|
116
|
-
- **Endpoints and APIs.** Namespaces are used in routing and APIs, ensuring consistent addressing.
|
|
117
|
-
- **Configurations and CLI commands.** Settings and commands are organized hierarchically to prevent duplication.
|
|
118
|
-
|
|
119
|
-
Code is designed to operate in an environment with other code. Each unit within its namespace reserves a name and builds
|
|
120
|
-
a downward hierarchy, creating a predictable interaction structure.
|
|
121
|
-
|
|
122
|
-
### Favor Pure JavaScript Without Compilation
|
|
123
|
-
|
|
124
|
-
Tequila Framework uses modern JavaScript (ES6+) without version downgrading or strict TypeScript typing. The code
|
|
125
|
-
remains in its original form, making it transparent, accessible, and easy to maintain.
|
|
126
|
-
|
|
127
|
-
Key characteristics:
|
|
128
|
-
|
|
129
|
-
- **No compilation.** Developers work with pure JavaScript without intermediate transformations, speeding up debugging
|
|
130
|
-
and simplifying maintenance.
|
|
131
|
-
- **JSDoc instead of TypeScript.** JSDoc annotations allow IDEs to understand data structures and provide
|
|
132
|
-
autocompletion, maintaining flexibility without strict typing.
|
|
133
|
-
- **Maximum compatibility.** The code easily integrates with any libraries and tools, as it does not require adaptation
|
|
134
|
-
to strict type contracts.
|
|
135
|
-
- **Fast development.** Changes are immediately reflected in the code without requiring a rebuild, increasing
|
|
136
|
-
development speed.
|
|
137
|
-
|
|
138
|
-
### Optimize Code and Docs for LLMs
|
|
139
|
-
|
|
140
|
-
Architecture, code, and documentation are designed for easy analysis and use by language models (LLM). This improves the
|
|
141
|
-
efficiency of **automatic code completion, generation of template solutions, and integration with AI tools**.
|
|
142
|
-
|
|
143
|
-
For this, the following practices are applied:
|
|
144
|
-
|
|
145
|
-
- **Predictable project structure.** Clear file organization, logical naming, and standardized conventions.
|
|
146
|
-
- **Unified code templates.** Repetitive structures and a predictable format help models understand and supplement the
|
|
147
|
-
code.
|
|
148
|
-
- **Optimized abstraction depth.** Code is organized to maintain modularity while avoiding excessive nesting.
|
|
149
|
-
- **Automated annotations.** JSDoc and standardized comments ensure precise code generation and documentation.
|
|
150
|
-
|
|
151
|
-
This approach allows LLM agents to:
|
|
152
|
-
|
|
153
|
-
- Quickly analyze code and suggest corrections.
|
|
154
|
-
- Automatically supplement documentation and comment code.
|
|
155
|
-
- Generate new modules according to the project's architectural standards.
|
|
156
|
-
- Simplify CI/CD integration by checking code compliance with style and conventions.
|
|
157
|
-
|
|
158
|
-
LLMs become part of the development process, helping not only with writing code but also keeping it up to date, reducing
|
|
159
|
-
developers' routine workload.
|
|
160
|
-
|
|
161
|
-
## Conclusion
|
|
162
|
-
|
|
163
|
-
The principles outlined in this document form an approach to **modular monolith** development, focused on
|
|
164
|
-
predictability, structure, and adaptability. They enable building architectures where code remains flexible,
|
|
165
|
-
transparent, and easily extensible.
|
|
166
|
-
|
|
167
|
-
These ideas do not require complex theoretical justifications or significant time investments for validation. They aim
|
|
168
|
-
to simplify integration, reduce unnecessary complexity, and enhance the potential for automation. **Standardized
|
|
169
|
-
structures, predictable namespaces, and development without transpilation** create an environment where the code is
|
|
170
|
-
understandable both for developers and language models (LLM).
|
|
171
|
-
|
|
172
|
-
The **Tequila Framework (TeqFW)** platform demonstrates these principles in action. While still evolving, it already
|
|
173
|
-
supports real-world development. This approach may serve not only as a foundation for proprietary tools, but also as
|
|
174
|
-
inspiration for rethinking conventional software architecture, prioritizing clarity, modularity, and adaptability over
|
|
175
|
-
unnecessary complexity.
|
package/RELEASE.md
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
# @teqfw/di releases
|
|
2
|
-
|
|
3
|
-
## 0.36.0 – New Preprocessor for Dynamic Dependency Rewriting
|
|
4
|
-
|
|
5
|
-
- Introduced `TeqFw_Di_Pre_Replace`, a new preprocessor allowing runtime substitution of dependency identifiers before
|
|
6
|
-
object creation, enabling advanced customization scenarios.
|
|
7
|
-
- Improved documentation (`README.md`): added concrete examples of mocking native Node.js modules during testing, better
|
|
8
|
-
illustrating the power of test mode and late binding.
|
|
9
|
-
|
|
10
|
-
## 0.35.0 – Support for Node.js Module Mocking in Test Mode
|
|
11
|
-
|
|
12
|
-
- DI container (test mode): extended support to register not only singleton instances but also mock implementations of
|
|
13
|
-
native Node.js modules, enabling more flexible and robust unit testing.
|
|
14
|
-
- Added missing property `TeqFw_Di_DepId.isNodeModule` to properly identify Node.js modules in the dependency resolution
|
|
15
|
-
process.
|
|
16
|
-
- Improved documentation (`README.md` and `PHILOSOPHY.md`) to clarify the design philosophy of TeqFW and explain the
|
|
17
|
-
immutability and test mode features in greater detail.
|
|
18
|
-
|
|
19
|
-
## 0.34.0 – Strict Mode and Test Support
|
|
20
|
-
|
|
21
|
-
- Removed outdated interface `TeqFw_Di_Api_Container` and associated documentation.
|
|
22
|
-
- Unified the `get()` and `compose()` methods for dependency retrieval.
|
|
23
|
-
- Introduced strict mode by default: manual registration of dependencies is now **disabled** unless explicitly allowed.
|
|
24
|
-
- Added `enableTestMode()` method to the container, enabling safe, test-specific manual registration of singleton
|
|
25
|
-
dependencies.
|
|
26
|
-
- Improved error messages and safeguards against accidental overwrites in `register()`.
|
|
27
|
-
- Updated internal naming and documentation for better clarity and alignment with the platform's principles.
|
|
28
|
-
- Cleaned up ESLint and JSDoc inconsistencies in internal files.
|
|
29
|
-
|
|
30
|
-
## 0.33.0 – Cleanup and Enhancements
|
|
31
|
-
|
|
32
|
-
- Removed deprecated documentation.
|
|
33
|
-
- Updated ESLint rules for consistency with current platform practices.
|
|
34
|
-
- Introduced `#` as a separator (in addition to `.`) between namespace and export names.
|
|
35
|
-
|
|
36
|
-
## 0.32.0 - added support for the `node:` prefix
|
|
37
|
-
|
|
38
|
-
- **Added ability** to manually register objects in the container (`register`).
|
|
39
|
-
- **Optimized dependency parsing**, added support for the `node:` prefix.
|
|
40
|
-
- **Updated singleton handling**, fixed issues with `Defs.LS`.
|
|
41
|
-
- **Added protection against object modification**, freezing objects when possible (`Object.freeze`).
|
|
42
|
-
- **Updated dependencies** (Mocha, Rollup, Rollup plugins).
|
|
43
|
-
|
|
44
|
-
## 0.31.0
|
|
45
|
-
|
|
46
|
-
* Added optional `stack` parameter to the `get` method for improved dependency tracking and debugging.
|
|
47
|
-
* Fixed browser example in the README by updating the usage of `window.TeqFw_Di_Container` for compatibility.
|
|
48
|
-
|
|
49
|
-
## 0.30.2
|
|
50
|
-
|
|
51
|
-
* Enhanced the README with updates from ChatGPT.
|
|
52
|
-
* Unified JSDoc annotations.
|
|
53
|
-
|
|
54
|
-
## 0.30.1
|
|
55
|
-
|
|
56
|
-
* Improve the README.
|
|
57
|
-
|
|
58
|
-
## 0.30.0
|
|
59
|
-
|
|
60
|
-
* New format of the depId for default parser (`Ns_Module.export$$(post)`).
|
|
61
|
-
* The rollup is added.
|
|
62
|
-
|
|
63
|
-
## 0.22.0
|
|
64
|
-
|
|
65
|
-
* Add Windows paths to the Resolver.
|
|
66
|
-
|
|
67
|
-
## 0.21.1
|
|
68
|
-
|
|
69
|
-
* Fix the dependency key signature in `TeqFw_Di_Container_A_Parser_Chunk_Def`.
|
|
70
|
-
|
|
71
|
-
## 0.21.0
|
|
72
|
-
|
|
73
|
-
* Restructured modules in the package.
|
|
74
|
-
* Documentation update.
|
|
75
|
-
|
|
76
|
-
## 0.20.1
|
|
77
|
-
|
|
78
|
-
* Changed regex for parameter extraction in the Spec Analyzer.
|
|
79
|
-
* Removed leading namespace separator in the Resolver.
|
|
80
|
-
* Added `teqfw.json` descriptor to add npm-package to DI container as a sources root in `teqfw/web`.
|
|
81
|
-
|
|
82
|
-
## 0.20.0
|
|
83
|
-
|
|
84
|
-
* Fully redesigned package with simplified composition of objects in the container. Spec Analyzer is used instead of a
|
|
85
|
-
proxy object.
|
|
86
|
-
|
|
87
|
-
## 0.12.1
|
|
88
|
-
|
|
89
|
-
* Hotfix for Windows delimiters.
|
|
90
|
-
|
|
91
|
-
## 0.12.0
|
|
92
|
-
|
|
93
|
-
* Standardized comments style for improved code readability and maintenance.
|
|
94
|
-
|
|
95
|
-
## 0.11.0
|
|
96
|
-
|
|
97
|
-
* Restructure `/@teqfw/di/replace` node in `teqfw.json`.
|
|
98
|
-
* Remove `TeqFw_Di_Shared_Api_Enum_Area` enumeration.
|
|
99
|
-
* Fix example code (`npm run example`).
|
|
100
|
-
|
|
101
|
-
## 0.10.0
|
|
102
|
-
|
|
103
|
-
* Improve error messaging.
|
|
104
|
-
* Use '.' instead of '#' in depIDs (Vnd_Plugin#export => Vnd_Plugin.export). Both variants are available for now.
|
|
105
|
-
* Experimental proxy for deps are added (Vnd_Plugin.export@@).
|
|
106
|
-
|
|
107
|
-
## 0.9.0
|
|
108
|
-
|
|
109
|
-
* `TeqFw_Di_Shared_Api_Enum_Area` enumeration is added;
|
|
110
|
-
|
|
111
|
-
## 0.8.0
|
|
112
|
-
|
|
113
|
-
* docs for plugin's teq-descriptor (see in `main` branch);
|
|
114
|
-
* use object notation instead of array notation in namespace replacement statements of
|
|
115
|
-
teq-descriptor (`@teqfw/di.replace` node format is changed in `./teqfw.json`);
|
|
116
|
-
* array is used as a container for upline dependencies in the 'SpecProxy' (object was);
|
package/bin/release/clean.sh
DELETED
|
@@ -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/eslint.config.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
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/rollup.config.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import resolve from '@rollup/plugin-node-resolve';
|
|
2
|
-
import terser from '@rollup/plugin-terser';
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
input: 'src/Container.js',
|
|
6
|
-
output: [
|
|
7
|
-
{
|
|
8
|
-
file: 'dist/esm.js',
|
|
9
|
-
format: 'es'
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
file: 'dist/umd.js',
|
|
13
|
-
format: 'umd',
|
|
14
|
-
name: 'TeqFw_Di_Container',
|
|
15
|
-
}
|
|
16
|
-
],
|
|
17
|
-
plugins: [
|
|
18
|
-
resolve(),
|
|
19
|
-
terser()
|
|
20
|
-
]
|
|
21
|
-
};
|