@teqfw/di 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/ai/AGENTS.md +74 -0
- package/ai/concepts.md +66 -0
- package/ai/container.md +72 -0
- package/ai/dependency-id.md +116 -0
- package/ai/extensions.md +71 -0
- package/ai/usage.md +244 -0
- package/jsconfig.json +13 -0
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.0.3 - 2026-03-07 - Package agent interface
|
|
4
|
+
|
|
5
|
+
- Added `ai/` to the published package file list.
|
|
6
|
+
- Updated package version metadata to `2.0.3`.
|
|
7
|
+
|
|
8
|
+
## 2.0.2 - 2026-03-05
|
|
9
|
+
|
|
10
|
+
- Replaced `tsconfig.json` with `jsconfig.json` for JavaScript type-checking configuration.
|
|
11
|
+
- Updated package publish list to include `jsconfig.json` and exclude legacy `teqfw.json`.
|
|
12
|
+
- Fixed `test:unit` and `test:integration` scripts to run all `*.test.mjs` files in nested directories.
|
|
13
|
+
- Updated package version metadata to `2.0.2`.
|
|
14
|
+
|
|
3
15
|
## 2.0.1 - 2026-03-04
|
|
4
16
|
|
|
5
17
|
- Regenerated `types.d.ts` according to updated type-map conventions.
|
package/ai/AGENTS.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Version: 20260307
|
|
4
|
+
|
|
5
|
+
## Package Purpose
|
|
6
|
+
|
|
7
|
+
This directory provides the **Agent Interface** of the package. It contains a compact usage-oriented description intended for LLM agents that use the package as a dependency. The documents describe how the package is applied by external code and exclude development methodology, repository organization, testing infrastructure, and other internal aspects of the project.
|
|
8
|
+
|
|
9
|
+
The package implements a **dependency container for ES modules** based on runtime late binding. Dependencies between modules are resolved by the container using structured dependency identifiers and deterministic namespace resolution rules rather than static imports. The container dynamically loads ES modules, instantiates exported factories, and returns linked objects according to dependency identifier semantics.
|
|
10
|
+
|
|
11
|
+
## When to Use This Package
|
|
12
|
+
|
|
13
|
+
This package should be used when a system requires the following capabilities:
|
|
14
|
+
|
|
15
|
+
- runtime dependency injection
|
|
16
|
+
- late binding between ES modules
|
|
17
|
+
- deterministic dependency resolution
|
|
18
|
+
- controlled instantiation of components
|
|
19
|
+
- modules that remain environment-agnostic across Node.js and browser runtimes
|
|
20
|
+
|
|
21
|
+
The container serves as the **composition root** of the application. Application modules declare dependencies and receive instantiated objects from the container rather than resolving dependencies directly.
|
|
22
|
+
|
|
23
|
+
## Architectural Model
|
|
24
|
+
|
|
25
|
+
The system is based on four mechanisms:
|
|
26
|
+
|
|
27
|
+
- **Late binding of dependencies at runtime**, allowing modules to remain independent of specific implementations.
|
|
28
|
+
- **A container responsible for dependency resolution and instantiation**, which loads modules and produces linked objects.
|
|
29
|
+
- **Structured dependency identifiers**, interpreted by the container to determine how a dependency must be resolved.
|
|
30
|
+
- **Namespace mapping rules**, which deterministically translate identifiers into module locations.
|
|
31
|
+
|
|
32
|
+
Together these mechanisms form a deterministic runtime linking system for ES modules.
|
|
33
|
+
|
|
34
|
+
## Runtime Linking Model
|
|
35
|
+
|
|
36
|
+
The container acts as a runtime linker for ES modules.
|
|
37
|
+
|
|
38
|
+
Instead of relying on static `import` statements, modules declare dependency contracts through `__deps__` descriptors and CDC identifiers. When a dependency is requested, the container resolves the identifier, loads the required module, constructs the object graph, and returns the linked result.
|
|
39
|
+
|
|
40
|
+
In this model:
|
|
41
|
+
|
|
42
|
+
- ES modules provide implementations.
|
|
43
|
+
- CDC identifiers define dependency contracts.
|
|
44
|
+
- the container performs deterministic runtime linking.
|
|
45
|
+
|
|
46
|
+
This mechanism separates module implementation from dependency binding and allows systems to assemble component graphs dynamically while preserving deterministic behavior.
|
|
47
|
+
|
|
48
|
+
## Reading Order
|
|
49
|
+
|
|
50
|
+
Agents should read the documents in this directory in the following order:
|
|
51
|
+
|
|
52
|
+
1. **AGENTS.md** — overview of the package and navigation of the Agent Interface.
|
|
53
|
+
2. **concepts.md** — architectural concepts and design principles.
|
|
54
|
+
3. **container.md** — container responsibilities and dependency resolution pipeline.
|
|
55
|
+
4. **dependency-id.md** — syntax and semantics of dependency identifiers.
|
|
56
|
+
5. **extensions.md** — extension mechanisms such as preprocessors and wrappers.
|
|
57
|
+
6. **usage.md** — minimal usage scenarios and examples.
|
|
58
|
+
|
|
59
|
+
This sequence reflects the conceptual flow required to understand the package: architectural model → operational mechanism → dependency addressing → extension points → practical usage.
|
|
60
|
+
|
|
61
|
+
## Interface Scope
|
|
62
|
+
|
|
63
|
+
The documents in this directory define the supported usage semantics of the package. Behaviors not described here should be treated as undefined and should not be inferred. The interface intentionally contains only the information required to correctly apply the package in external code.
|
|
64
|
+
|
|
65
|
+
## Relation to TeqFW
|
|
66
|
+
|
|
67
|
+
The package follows architectural principles used in the **Tequila Framework (TeqFW)** platform, including:
|
|
68
|
+
|
|
69
|
+
- late binding between components
|
|
70
|
+
- separation of data structures and logic handlers
|
|
71
|
+
- namespace-based module organization
|
|
72
|
+
- development in modern JavaScript without compilation
|
|
73
|
+
|
|
74
|
+
Understanding the broader TeqFW ecosystem is not required to use the package, but the container follows the same architectural philosophy.
|
package/ai/concepts.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# concepts.md
|
|
2
|
+
|
|
3
|
+
Version: 20260307
|
|
4
|
+
|
|
5
|
+
## Purpose of the Container
|
|
6
|
+
|
|
7
|
+
The container provides a deterministic mechanism for linking ES modules at runtime. Instead of static imports between modules, dependencies are resolved through a central container that loads modules, instantiates exported factories, and returns linked objects. This approach separates module implementation from dependency resolution and allows systems to assemble components dynamically.
|
|
8
|
+
|
|
9
|
+
## Late Binding
|
|
10
|
+
|
|
11
|
+
Dependencies between modules are resolved at runtime rather than during module loading. Modules do not import their collaborators directly and therefore remain independent of concrete implementations. The container performs dependency resolution when an object is requested, which allows components to be replaced, extended, or mocked without modifying module code.
|
|
12
|
+
|
|
13
|
+
Late binding enables the following properties:
|
|
14
|
+
|
|
15
|
+
- reduced coupling between modules
|
|
16
|
+
- runtime replacement of implementations
|
|
17
|
+
- simplified testing through dependency substitution
|
|
18
|
+
- compatibility with different runtime environments
|
|
19
|
+
|
|
20
|
+
## Dependency Container
|
|
21
|
+
|
|
22
|
+
The container acts as the composition root of the application. It receives dependency requests expressed as identifiers, resolves them into module locations, loads the corresponding ES modules, and produces linked objects.
|
|
23
|
+
|
|
24
|
+
The container is responsible for:
|
|
25
|
+
|
|
26
|
+
- translating dependency identifiers into module references
|
|
27
|
+
- loading ES modules dynamically
|
|
28
|
+
- instantiating exported factories
|
|
29
|
+
- applying preprocessing and postprocessing steps
|
|
30
|
+
- returning fully linked objects to the caller
|
|
31
|
+
|
|
32
|
+
Application modules do not construct their dependencies directly and do not perform dependency resolution.
|
|
33
|
+
|
|
34
|
+
## Dependency Identifiers
|
|
35
|
+
|
|
36
|
+
Dependencies are addressed using structured identifiers interpreted by the container. An identifier describes how the dependency should be resolved and how the resulting object should be instantiated.
|
|
37
|
+
|
|
38
|
+
Dependency identifiers define:
|
|
39
|
+
|
|
40
|
+
- which module provides the dependency
|
|
41
|
+
- which export must be used
|
|
42
|
+
- whether the dependency represents a singleton or a new instance
|
|
43
|
+
|
|
44
|
+
The identifier syntax and resolution rules are described in **dependency-id.md**.
|
|
45
|
+
|
|
46
|
+
## Namespaces
|
|
47
|
+
|
|
48
|
+
Namespaces provide deterministic mapping between logical identifiers and module locations. Each package defines a namespace root that corresponds to a directory containing source modules.
|
|
49
|
+
|
|
50
|
+
The container resolves identifiers by applying namespace rules that translate identifier prefixes into filesystem paths. This mechanism allows modules to be referenced through stable logical names instead of file paths.
|
|
51
|
+
|
|
52
|
+
Namespace resolution ensures:
|
|
53
|
+
|
|
54
|
+
- predictable module addressing
|
|
55
|
+
- isolation between packages
|
|
56
|
+
- consistent mapping between identifiers and modules
|
|
57
|
+
|
|
58
|
+
## Immutable Linked Objects
|
|
59
|
+
|
|
60
|
+
Objects returned by the container represent linked components of the application and are treated as immutable values. After an object is created and linked, it is frozen to prevent runtime modification.
|
|
61
|
+
|
|
62
|
+
Immutability ensures that:
|
|
63
|
+
|
|
64
|
+
- components behave consistently after linking
|
|
65
|
+
- shared instances cannot be altered by consumers
|
|
66
|
+
- the container remains the single authority responsible for object construction
|
package/ai/container.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# container.md
|
|
2
|
+
|
|
3
|
+
Version: 20260307
|
|
4
|
+
|
|
5
|
+
## Role of the Container
|
|
6
|
+
|
|
7
|
+
The container provides the operational mechanism that links ES modules at runtime. It resolves dependency identifiers, loads modules, instantiates exported factories, and returns fully linked objects. The container serves as the composition root of the system and centralizes all dependency resolution.
|
|
8
|
+
|
|
9
|
+
Application modules do not resolve dependencies themselves and do not construct collaborators directly. Instead, they request dependencies from the container using dependency identifiers.
|
|
10
|
+
|
|
11
|
+
## Container Responsibilities
|
|
12
|
+
|
|
13
|
+
The container performs the following responsibilities:
|
|
14
|
+
|
|
15
|
+
- interpret dependency identifiers
|
|
16
|
+
- resolve identifiers into module locations
|
|
17
|
+
- load ES modules dynamically
|
|
18
|
+
- instantiate exported factories
|
|
19
|
+
- apply preprocess and postprocess handlers
|
|
20
|
+
- manage object lifecycle semantics
|
|
21
|
+
- freeze linked objects before returning them
|
|
22
|
+
|
|
23
|
+
These responsibilities ensure deterministic runtime linking between modules.
|
|
24
|
+
|
|
25
|
+
## Dependency Resolution Pipeline
|
|
26
|
+
|
|
27
|
+
When a dependency is requested, the container processes the request through a deterministic pipeline consisting of the following stages:
|
|
28
|
+
|
|
29
|
+
1. **Parse** — interpret the dependency identifier.
|
|
30
|
+
2. **Preprocess** — allow registered preprocess handlers to transform the identifier.
|
|
31
|
+
3. **Resolve** — translate the identifier into a module reference.
|
|
32
|
+
4. **Instantiate** — load the module and create the object using the selected export.
|
|
33
|
+
5. **Postprocess** — apply wrapper handlers to the created object.
|
|
34
|
+
6. **Lifecycle** — apply lifecycle semantics such as singleton caching or instance creation.
|
|
35
|
+
7. **Freeze** — freeze the resulting object before returning it.
|
|
36
|
+
|
|
37
|
+
This pipeline ensures that all dependencies are created and linked through a consistent process.
|
|
38
|
+
|
|
39
|
+
## Container API
|
|
40
|
+
|
|
41
|
+
The container exposes a minimal public interface used by application code and configuration code.
|
|
42
|
+
|
|
43
|
+
The core operations are:
|
|
44
|
+
|
|
45
|
+
- **register(identifier, value)** — register a predefined dependency or instance in the container.
|
|
46
|
+
- **get(identifier)** — resolve a dependency identifier and return the linked object.
|
|
47
|
+
- **addPreprocess(handler)** — register a handler that can transform dependency identifiers before resolution.
|
|
48
|
+
- **addPostprocess(handler)** — register a handler that can modify created objects after instantiation.
|
|
49
|
+
|
|
50
|
+
The exact semantics of dependency identifiers are defined in **dependency-id.md**.
|
|
51
|
+
|
|
52
|
+
## Container State Model
|
|
53
|
+
|
|
54
|
+
The container operates in three states:
|
|
55
|
+
|
|
56
|
+
- **initial** — the container is being configured and dependencies may be registered.
|
|
57
|
+
- **active** — the container resolves dependencies and produces linked objects.
|
|
58
|
+
- **failed** — the container has encountered an unrecoverable error.
|
|
59
|
+
|
|
60
|
+
If an error occurs during dependency resolution or any pipeline stage, the container transitions to the **failed** state. Once the container enters this state, all subsequent dependency requests are rejected.
|
|
61
|
+
|
|
62
|
+
This fail-fast behavior prevents partially linked systems from continuing execution.
|
|
63
|
+
|
|
64
|
+
## Object Linking and Freezing
|
|
65
|
+
|
|
66
|
+
Objects returned by the container represent linked components of the application. After an object is created and all pipeline stages are completed, the container freezes the object before returning it.
|
|
67
|
+
|
|
68
|
+
Freezing ensures that:
|
|
69
|
+
|
|
70
|
+
- linked objects cannot be modified by consumers
|
|
71
|
+
- shared instances remain stable
|
|
72
|
+
- the container remains the only authority responsible for object construction and linking
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# dependency-id.md
|
|
2
|
+
|
|
3
|
+
Version: 20260307
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
Dependencies in the container are addressed using **Canonical Dependency Codes (CDC)**. A CDC is a structured identifier interpreted by the container to determine which module must be loaded, which export must be used, and how the resulting object must be instantiated.
|
|
8
|
+
|
|
9
|
+
CDC provides a stable logical addressing mechanism that is independent of file paths and runtime environments.
|
|
10
|
+
|
|
11
|
+
## Canonical Form
|
|
12
|
+
|
|
13
|
+
A CDC follows the canonical structure:
|
|
14
|
+
|
|
15
|
+
```txt
|
|
16
|
+
[PlatformPrefix]ModuleName[__ExportName][Lifecycle][WrapperSuffixes]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The components have the following roles:
|
|
20
|
+
|
|
21
|
+
- **PlatformPrefix** — optional prefix identifying a platform module source.
|
|
22
|
+
- **ModuleName** — logical module identifier within a namespace.
|
|
23
|
+
- **ExportName** — optional named export selector.
|
|
24
|
+
- **Lifecycle** — marker defining instantiation semantics.
|
|
25
|
+
- **WrapperSuffixes** — optional sequence of wrapper identifiers.
|
|
26
|
+
|
|
27
|
+
Each CDC is interpreted deterministically by the container.
|
|
28
|
+
|
|
29
|
+
## Platform Prefix
|
|
30
|
+
|
|
31
|
+
A CDC may reference modules provided by the runtime platform or external packages.
|
|
32
|
+
|
|
33
|
+
The following prefixes are supported:
|
|
34
|
+
|
|
35
|
+
- **`node_`** — reference a built-in Node.js module.
|
|
36
|
+
- **`npm_`** — reference a module from the npm ecosystem.
|
|
37
|
+
|
|
38
|
+
Examples:
|
|
39
|
+
|
|
40
|
+
```txt
|
|
41
|
+
node_fs$
|
|
42
|
+
npm_lodash$
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If no platform prefix is present, the identifier refers to an application module resolved through namespace mapping.
|
|
46
|
+
|
|
47
|
+
## Module Identification
|
|
48
|
+
|
|
49
|
+
The **ModuleName** identifies the module that provides the dependency. Module identifiers use namespace-based naming and are translated into filesystem paths according to namespace resolution rules described in **container.md**.
|
|
50
|
+
|
|
51
|
+
Identifier segments separated by underscores correspond to directory boundaries in the module path.
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
|
|
55
|
+
```txt
|
|
56
|
+
App_Service_User
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
maps to a module located at:
|
|
60
|
+
|
|
61
|
+
```txt
|
|
62
|
+
AppRoot/Service/User.js
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Export Selection
|
|
66
|
+
|
|
67
|
+
A CDC may reference either the default export of a module or a named export.
|
|
68
|
+
|
|
69
|
+
Named exports are selected using a double underscore separator.
|
|
70
|
+
|
|
71
|
+
Examples:
|
|
72
|
+
|
|
73
|
+
```txt
|
|
74
|
+
App_Service_User$
|
|
75
|
+
App_Service_User__Factory$
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The first identifier resolves the module default export. The second resolves the named export `Factory`.
|
|
79
|
+
|
|
80
|
+
## Lifecycle Markers
|
|
81
|
+
|
|
82
|
+
Lifecycle markers define how the container instantiates and returns objects.
|
|
83
|
+
|
|
84
|
+
The following markers are supported:
|
|
85
|
+
|
|
86
|
+
- **`$`** — singleton lifecycle; the container creates the object once and returns the same instance for subsequent requests.
|
|
87
|
+
- **`$$`** — factory lifecycle; a new instance is created for each request.
|
|
88
|
+
- **`$$$`** — transient lifecycle; the container delegates lifecycle management entirely to the caller.
|
|
89
|
+
|
|
90
|
+
Lifecycle markers are appended at the end of the identifier.
|
|
91
|
+
|
|
92
|
+
## Wrapper Suffixes
|
|
93
|
+
|
|
94
|
+
Wrappers allow postprocessing of created objects. Wrapper identifiers are appended after the lifecycle marker and separated by underscores.
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
|
|
98
|
+
```txt
|
|
99
|
+
App_Service_User$$_wrapLog_wrapTrace
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
In this example the container creates a new instance and applies the wrappers `wrapLog` and `wrapTrace` during the postprocess stage.
|
|
103
|
+
|
|
104
|
+
Wrapper behavior is described in **extensions.md**.
|
|
105
|
+
|
|
106
|
+
## Resolution Semantics
|
|
107
|
+
|
|
108
|
+
When the container receives a CDC it performs the following interpretation steps:
|
|
109
|
+
|
|
110
|
+
1. determine the platform prefix and module namespace
|
|
111
|
+
2. resolve the module identifier into a module location
|
|
112
|
+
3. select the requested export
|
|
113
|
+
4. apply lifecycle semantics
|
|
114
|
+
5. apply wrapper suffixes if present
|
|
115
|
+
|
|
116
|
+
These interpretation steps integrate with the dependency resolution pipeline described in **container.md**.
|
package/ai/extensions.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# extensions.md
|
|
2
|
+
|
|
3
|
+
Version: 20260307
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The container supports controlled extension of the dependency resolution process through **preprocess handlers** and **postprocess wrappers**. These mechanisms allow the behavior of dependency resolution and object creation to be adjusted without modifying container internals.
|
|
8
|
+
|
|
9
|
+
Extensions are registered during container configuration and become part of the deterministic resolution pipeline.
|
|
10
|
+
|
|
11
|
+
## Extension Points
|
|
12
|
+
|
|
13
|
+
Two extension points are available:
|
|
14
|
+
|
|
15
|
+
- **Preprocess handlers** — transform dependency identifiers before resolution.
|
|
16
|
+
- **Postprocess wrappers** — modify or decorate created objects after instantiation.
|
|
17
|
+
|
|
18
|
+
These extension points correspond to the preprocess and postprocess stages of the container pipeline described in **container.md**.
|
|
19
|
+
|
|
20
|
+
## Preprocess Handlers
|
|
21
|
+
|
|
22
|
+
Preprocess handlers receive a dependency identifier before it is interpreted by the container. A handler may transform the identifier and return a modified value that continues through the resolution pipeline.
|
|
23
|
+
|
|
24
|
+
Typical uses of preprocess handlers include:
|
|
25
|
+
|
|
26
|
+
- rewriting dependency identifiers
|
|
27
|
+
- applying identifier aliases
|
|
28
|
+
- enforcing project-specific identifier conventions
|
|
29
|
+
|
|
30
|
+
Preprocess handlers operate only on identifiers and do not interact with instantiated objects.
|
|
31
|
+
|
|
32
|
+
## Postprocess Wrappers
|
|
33
|
+
|
|
34
|
+
Postprocess wrappers operate on objects created by the container. A wrapper receives the instantiated object and may return a modified or decorated version.
|
|
35
|
+
|
|
36
|
+
Wrappers are typically used for:
|
|
37
|
+
|
|
38
|
+
- logging or tracing
|
|
39
|
+
- instrumentation
|
|
40
|
+
- capability injection
|
|
41
|
+
- runtime adaptation of object behavior
|
|
42
|
+
|
|
43
|
+
Wrappers do not modify the dependency resolution logic itself and operate only after object instantiation.
|
|
44
|
+
|
|
45
|
+
## Wrapper Selection
|
|
46
|
+
|
|
47
|
+
Wrappers are applied when their identifiers appear in a CDC. Wrapper identifiers are appended after the lifecycle marker and separated by underscores.
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
|
|
51
|
+
```txt
|
|
52
|
+
App_Service_User$$_wrapLog_wrapTrace
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
In this example the container creates a new instance of the dependency and applies the wrappers `wrapLog` and `wrapTrace` during the postprocess stage.
|
|
56
|
+
|
|
57
|
+
## Execution Order
|
|
58
|
+
|
|
59
|
+
Multiple wrappers may be applied to a single dependency. Wrappers are executed in the order in which they appear in the CDC.
|
|
60
|
+
|
|
61
|
+
Each wrapper receives the object returned by the previous wrapper and may return a new object that becomes the input to the next wrapper.
|
|
62
|
+
|
|
63
|
+
## Extension Constraints
|
|
64
|
+
|
|
65
|
+
Extensions must satisfy the following constraints:
|
|
66
|
+
|
|
67
|
+
- they must be registered before the container becomes active
|
|
68
|
+
- they must be deterministic and free of side effects that alter container state
|
|
69
|
+
- they must not bypass lifecycle semantics enforced by the container
|
|
70
|
+
|
|
71
|
+
These constraints ensure that extensions do not compromise deterministic dependency resolution.
|
package/ai/usage.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# usage.md
|
|
2
|
+
|
|
3
|
+
Version: 20260307
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
This document describes typical usage patterns of the container. The examples illustrate how modules declare dependencies, how the container is configured in the composition root, and how Canonical Dependency Codes (CDC) control dependency resolution, instantiation, and behavioral modification.
|
|
8
|
+
|
|
9
|
+
The container performs deterministic runtime linking of ES modules and returns frozen object graphs constructed from explicitly declared dependency contracts.
|
|
10
|
+
|
|
11
|
+
## Module Structure
|
|
12
|
+
|
|
13
|
+
Modules intended for container linking export a default class and may export a static dependency descriptor named `__deps__`.
|
|
14
|
+
|
|
15
|
+
Dependencies are injected into the constructor as a single structured object. In TeqFW-style modules the public API is defined inside the constructor through assignments to `this`, while internal state may be held in constructor-local variables captured by closures.
|
|
16
|
+
|
|
17
|
+
Example module:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
// @ts-check
|
|
21
|
+
|
|
22
|
+
export const __deps__ = {
|
|
23
|
+
default: {
|
|
24
|
+
child: "App_Child$",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @typedef {Object} App_Root$Deps
|
|
30
|
+
* @property {App_Child} child
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export default class App_Root {
|
|
34
|
+
/**
|
|
35
|
+
* @param {App_Root$Deps} deps
|
|
36
|
+
*/
|
|
37
|
+
constructor({ child }) {
|
|
38
|
+
this.getName = function () {
|
|
39
|
+
return "root";
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
this.getChild = function () {
|
|
43
|
+
return child;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Dependency module:
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
// @ts-check
|
|
53
|
+
|
|
54
|
+
export default class App_Child {
|
|
55
|
+
constructor() {
|
|
56
|
+
this.getName = function () {
|
|
57
|
+
return "child";
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Rules:
|
|
64
|
+
|
|
65
|
+
- if `__deps__` is absent the module has no dependencies
|
|
66
|
+
- `__deps__.default` describes dependencies of the default export
|
|
67
|
+
- keys correspond to constructor dependency names
|
|
68
|
+
- values are CDC identifiers
|
|
69
|
+
- dependencies are resolved recursively before instantiation
|
|
70
|
+
|
|
71
|
+
## Container Configuration
|
|
72
|
+
|
|
73
|
+
The container is configured in the composition root of the application. Namespace roots define how CDC prefixes map to module locations.
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import path from "node:path";
|
|
77
|
+
import { fileURLToPath } from "node:url";
|
|
78
|
+
import Container from "@teqfw/di";
|
|
79
|
+
|
|
80
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
81
|
+
const __dirname = path.dirname(__filename);
|
|
82
|
+
|
|
83
|
+
const container = new Container();
|
|
84
|
+
|
|
85
|
+
container.addNamespaceRoot("App_", path.resolve(__dirname, "./src/App"), ".mjs");
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Configuration must be completed before the first dependency resolution.
|
|
89
|
+
|
|
90
|
+
## Resolving Dependencies
|
|
91
|
+
|
|
92
|
+
Dependencies are obtained using CDC identifiers.
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
const root = await container.get("App_Root$");
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The container:
|
|
99
|
+
|
|
100
|
+
- parses the CDC
|
|
101
|
+
- loads the module
|
|
102
|
+
- resolves dependencies declared in `__deps__`
|
|
103
|
+
- instantiates modules
|
|
104
|
+
- links the dependency graph
|
|
105
|
+
- freezes the produced value
|
|
106
|
+
|
|
107
|
+
Example usage:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
console.log(root.getName());
|
|
111
|
+
console.log(root.getChild().getName());
|
|
112
|
+
console.log(Object.isFrozen(root));
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Typical CDC Usage Patterns
|
|
116
|
+
|
|
117
|
+
CDC identifiers define how the container interprets a dependency. The following patterns represent common usage scenarios.
|
|
118
|
+
|
|
119
|
+
### Module As-Is
|
|
120
|
+
|
|
121
|
+
A CDC without lifecycle marker returns the module export as-is.
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
App_Service
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Typical usage:
|
|
128
|
+
|
|
129
|
+
- utility modules
|
|
130
|
+
- stateless helpers
|
|
131
|
+
- constant providers
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
const math = await container.get("App_Math");
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Singleton from Default Export
|
|
140
|
+
|
|
141
|
+
A lifecycle marker `$` creates a singleton instance.
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
App_Service$
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Typical usage:
|
|
148
|
+
|
|
149
|
+
- application services
|
|
150
|
+
- repositories
|
|
151
|
+
- infrastructure adapters
|
|
152
|
+
|
|
153
|
+
### Transient Instance
|
|
154
|
+
|
|
155
|
+
Marker `$$` creates a new instance for every request.
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
App_Service$$
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Typical usage:
|
|
162
|
+
|
|
163
|
+
- request objects
|
|
164
|
+
- short-lived workers
|
|
165
|
+
- task processors
|
|
166
|
+
|
|
167
|
+
### Named Export Resolution
|
|
168
|
+
|
|
169
|
+
CDC may reference a named export using the `__ExportName` segment.
|
|
170
|
+
|
|
171
|
+
```text
|
|
172
|
+
App_Module__build$
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Typical usage:
|
|
176
|
+
|
|
177
|
+
- builders
|
|
178
|
+
- factory entry points
|
|
179
|
+
- specialized constructors
|
|
180
|
+
|
|
181
|
+
### Wrapper Application
|
|
182
|
+
|
|
183
|
+
Wrappers modify the produced value through postprocess plugins.
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
App_Service$$_log_trace
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The container creates the dependency and applies wrappers in declared order.
|
|
190
|
+
|
|
191
|
+
Typical usage:
|
|
192
|
+
|
|
193
|
+
- logging
|
|
194
|
+
- tracing
|
|
195
|
+
- metrics collection
|
|
196
|
+
- behavioral instrumentation
|
|
197
|
+
|
|
198
|
+
### Platform Dependencies
|
|
199
|
+
|
|
200
|
+
CDC may reference runtime platform modules.
|
|
201
|
+
|
|
202
|
+
```text
|
|
203
|
+
node_fs
|
|
204
|
+
npm_lodash
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
These identifiers provide access to Node.js built-ins and npm packages.
|
|
208
|
+
|
|
209
|
+
### Root Graph Resolution
|
|
210
|
+
|
|
211
|
+
Applications typically resolve a single root dependency.
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
const root = await container.get("App_Root$");
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
The container recursively resolves all dependencies declared through `__deps__` and constructs the object graph.
|
|
218
|
+
|
|
219
|
+
## Wrapper-Based Behavioral Composition
|
|
220
|
+
|
|
221
|
+
Wrappers enable declarative behavioral composition at the dependency level. By attaching wrapper identifiers to CDC strings the caller can modify the runtime behavior of a dependency without modifying its module implementation.
|
|
222
|
+
|
|
223
|
+
Example:
|
|
224
|
+
|
|
225
|
+
```text
|
|
226
|
+
App_Service$$_log
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The service module remains unaware of logging. The wrapper introduces cross-cutting behavior during the container postprocess stage.
|
|
230
|
+
|
|
231
|
+
This mechanism enables patterns similar to aspect-oriented programming while preserving deterministic dependency resolution.
|
|
232
|
+
|
|
233
|
+
## Minimal Smoke Pattern
|
|
234
|
+
|
|
235
|
+
A minimal integration scenario demonstrating container operation:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
const container = new Container();
|
|
239
|
+
container.addNamespaceRoot("Fx_", FIXTURE_DIR, ".mjs");
|
|
240
|
+
|
|
241
|
+
const value = await container.get("Fx_Root$");
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The container loads modules, resolves dependencies, instantiates objects, and returns a frozen result.
|
package/jsconfig.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"baseUrl": ".",
|
|
4
|
+
"checkJs": true,
|
|
5
|
+
"forceConsistentCasingInFileNames": true,
|
|
6
|
+
"module": "nodenext",
|
|
7
|
+
"moduleResolution": "nodenext",
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"target": "ESNext"
|
|
11
|
+
},
|
|
12
|
+
"include": ["src", "test", "types.d.ts"]
|
|
13
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teqfw/di",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
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",
|
|
@@ -21,12 +21,13 @@
|
|
|
21
21
|
"url": "https://github.com/flancer64"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
|
+
"ai/",
|
|
24
25
|
"dist/",
|
|
25
26
|
"src/",
|
|
26
27
|
"CHANGELOG.md",
|
|
28
|
+
"jsconfig.json",
|
|
27
29
|
"LICENSE",
|
|
28
30
|
"README.md",
|
|
29
|
-
"teqfw.json",
|
|
30
31
|
"types.d.ts"
|
|
31
32
|
],
|
|
32
33
|
"main": "dist/umd.js",
|
|
@@ -52,8 +53,8 @@
|
|
|
52
53
|
"scripts": {
|
|
53
54
|
"rollup": "rollup -c",
|
|
54
55
|
"eslint": "npx eslint './src/**/*.mjs'",
|
|
55
|
-
"test:unit": "node --test
|
|
56
|
-
"test:integration": "node --test
|
|
56
|
+
"test:unit": "find test/unit -name '*.test.mjs' -print0 | xargs -0 node --test",
|
|
57
|
+
"test:integration": "find test/integration -name '*.test.mjs' -print0 | xargs -0 node --test"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
60
|
"@eslint/js": "^10.0.1",
|