@path-ioc/container 0.1.3 → 0.1.5
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 +33 -40
- package/README.zh-CN.md +35 -44
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
</p>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
-
> **
|
|
19
|
-
> `@path-ioc/
|
|
20
|
-
> `@path-ioc/
|
|
18
|
+
> ⚠️ **Historical Evolution Experimental Package / Not Recommended for Production**
|
|
19
|
+
> `@path-ioc/container` was developed as an experimental proof-of-concept and benchmark package to evaluate synchronous getter-based lazy loading against traditional JS IoC frameworks (such as InversifyJS). Due to strict sequential mutex constraints on property resolution, **it is not recommended for standard production applications and has no active iteration roadmap**.
|
|
20
|
+
> For production systems, please use [`@path-ioc/core`](../core) and [`@path-ioc/unplugin`](../unplugin) with `createModularContainer`.
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -27,34 +27,22 @@
|
|
|
27
27
|
|
|
28
28
|
In enterprise production (similar to Java Spring's default `eager-singleton`), full DAG topological preheating at boot time remains the gold standard for system stability, dependency integrity, and runtime throughput:
|
|
29
29
|
|
|
30
|
-
- **Canonical Core (`@path-ioc/core`)**: Strictly rigorous—static graph compilation, DFS Fail-Fast cyclic dependency rejection, and native `async/await` DAG concurrency. Rejects implicit cycle-breaking that masks architectural design flaws
|
|
31
|
-
- **Extended Container (`@path-ioc/container`)**:
|
|
30
|
+
- **Canonical Core (`@path-ioc/core`)**: Strictly rigorous—static graph compilation, DFS Fail-Fast cyclic dependency rejection, and native `async/await` DAG concurrency. Rejects implicit cycle-breaking that masks architectural design flaws;
|
|
31
|
+
- **Extended Container (`@path-ioc/container`)**: Exploratory—wraps high-order proxy containers to decompose "Loading Scope (`eager` | `demand`)" and "Execution Engine (`async` | `turbo`)" into a **2-dimensional orthogonal matrix**, evaluating traditional on-demand IoC patterns.
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
35
35
|
### 2. The Irreconcilable Physical Law: `async` vs. `turbo`
|
|
36
36
|
|
|
37
|
-
In the JavaScript
|
|
37
|
+
In the JavaScript single-threaded execution model, **"Asynchronous Initialization"** and **"Dynamic Runtime Dependency Sensing"** are physically irreconcilable:
|
|
38
38
|
|
|
39
|
-
- **Physical Reason**: JavaScript's Proxy dynamic getter (`
|
|
40
|
-
- If a module requires asynchronous setup (`async main`), its dependencies **must be statically declared beforehand** so the DAG scheduler can orchestrate topological `await` execution
|
|
39
|
+
- **Physical Reason**: JavaScript's Proxy dynamic getter (`container.foo`) is purely synchronous; it cannot suspend execution mid-flight to await an asynchronous microtask.
|
|
40
|
+
- If a module requires asynchronous setup (`async main`), its dependencies **must be statically declared beforehand** so the DAG scheduler can orchestrate topological `await` execution;
|
|
41
41
|
- If dynamic sensing without declared dependencies were permitted during `async` execution, accessing an unready node would inevitably yield unhandled dangling Promises or deadlock the event loop.
|
|
42
42
|
|
|
43
43
|
Path-IoC resolves this via physical separation:
|
|
44
|
-
- **`async` mode**: Explicit `dependencies` -> Compiled static DAG -> **100% Native Reactive Topological Concurrency
|
|
45
|
-
- **`turbo` mode
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## Selection Matrix
|
|
50
|
-
|
|
51
|
-
| Dimension | **NestJS** | **InversifyJS** | **Awilix** | **TSyringe** | **TypeDI** | **@path-ioc/container** |
|
|
52
|
-
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
53
|
-
| **Core Contract** | `@Injectable()` + TS Decorators + `reflect-metadata` | `@injectable()` + TS Decorators + `reflect-metadata` | Regex `.toString()` or param inspection + Proxy | `@singleton()` + TS Decorators + `reflect-metadata` | `@Service()` + TS Decorators + `reflect-metadata` | **Physical Path Contract + Pure ES Closures + Static Graph** |
|
|
54
|
-
| **Code Intrusion** | High (framework decorators everywhere) | High (bound to `@inject`) | Low (matches parameters) | High (Microsoft decorators) | High (TypeDI decorators) | **Zero** (Pure ES functions; runs independently) |
|
|
55
|
-
| **On-Demand / Lazy Paradigm** | Module-level explicit lazy loading (`LazyModuleLoader`) | Property-level lazy injection (`@lazyInject`) | Proxy-based on-demand instantiation (sync only) | Not supported | Not supported | **Forward Subgraph Slicing** (`extractSubModules` automatically extracts forward dependency slice) |
|
|
56
|
-
| **Cycle Resolution** | `forwardRef()` (breaks easily on async) | `@lazyInject()` deferred lookup | Dynamic Proxy Getter (sync only) | `delay()` with explicit token | `constructMany` deferred | **Turbo Mode Dynamic Getter** (Zero tokens; automatic sync cycle unwinding) |
|
|
57
|
-
| **Async Concurrency** | Serial pipeline; sequential blocking | `getAsync()` without native DAG | Async factory (`container.build()`) | None | None | **Dual Engine Scheduler** (`async` native DAG concurrency / `turbo` synchronous pass-through) |
|
|
44
|
+
- **`async` mode (Supports async, requires declared `dependencies`)**: Explicit `dependencies` -> Compiled static DAG -> **100% Native Reactive Topological Concurrency** (cascaded asynchronously via `Promise.all`);
|
|
45
|
+
- **`turbo` mode (Pure synchronous pass-through, supports omitting `dependencies`)**: **Strictly forbids asynchronous `main` factories** (throws `[Turbo Mode] Async module is not supported` if a Promise is returned). Because the synchronous call stack never suspends, **modules can completely omit `dependencies`**, relying on Proxy Dynamic Getters to synchronously traverse and hydrate dependencies on demand; calling-phase mutual references avoid deadlocks naturally, while initialization-phase circular deadlocks trigger a call stack overflow or are caught by static DFS Fail-Fast interception.
|
|
58
46
|
|
|
59
47
|
---
|
|
60
48
|
|
|
@@ -67,20 +55,32 @@ Path-IoC resolves this via physical separation:
|
|
|
67
55
|
│ mode: "async" │ mode: "turbo" │
|
|
68
56
|
│ (Reactive DAG Engine) │ (Zero-Promise Sync) │
|
|
69
57
|
┌───────────────────┼─────────────────────────┼─────────────────────────┤
|
|
70
|
-
│ strategy: "eager" │ ① Full Async Preheat │ ② Sync Preheat +
|
|
58
|
+
│ strategy: "eager" │ ① Full Async Preheat │ ② Sync Preheat + Direct│
|
|
71
59
|
│ (Full Load) │ (Mounts $ready handle) │ (Fast sync utilities) │
|
|
72
60
|
├───────────────────┼─────────────────────────┼─────────────────────────┤
|
|
73
61
|
│ strategy: "demand"│ ③ Subgraph On-Demand │ ④ Direct Sync Pass-thru│
|
|
74
|
-
│ (Instant Slice) │ (
|
|
62
|
+
│ (Instant Slice) │ (Strict Mutex Guard) │ (Ultra-fast CLI / Test)│
|
|
75
63
|
└───────────────────┴─────────────────────────┴─────────────────────────┘
|
|
76
64
|
```
|
|
77
65
|
|
|
78
|
-
| Paradigm (`strategy` $\times$ `mode`) | Trigger Mechanism | Key Characteristics |
|
|
66
|
+
| Paradigm (`strategy` $\times$ `mode`) | Trigger Mechanism | Key Characteristics | Intended Scenario |
|
|
79
67
|
| :--- | :--- | :--- | :--- |
|
|
80
|
-
| **`eager` + `async`** | Container creation | Full DAG topological concurrency preheat; non-enumerable `container.$ready` handle |
|
|
81
|
-
| **`eager` + `turbo`** | Container creation | Synchronously evaluates modules in topological order;
|
|
82
|
-
| **`demand` + `async`** | Property access (`container.UserPage`) | Traverses forward dependency tree to extract minimum slice
|
|
83
|
-
| **`demand` + `turbo`** | Property access (`container.foo`) | Instant synchronous evaluation without `await`;
|
|
68
|
+
| **`eager` + `async`** | Container creation | Full DAG topological concurrency preheat; non-enumerable `container.$ready` handle | Asynchronous pre-warming experiments |
|
|
69
|
+
| **`eager` + `turbo`** | Container creation | Synchronously evaluates modules in topological order; throws if an async module is encountered | Synchronous utility test suites |
|
|
70
|
+
| **`demand` + `async`** | Property access (`container.UserPage`) | Traverses forward dependency tree to extract minimum slice. **Warning**: Mutex-guarded; sequential `await` is required, and concurrent accesses (e.g. `Promise.all([container.a, container.b])`) will throw a mutex conflict error | Demand-driven async slicing tests |
|
|
71
|
+
| **`demand` + `turbo`** | Property access (`container.foo`) | Instant synchronous evaluation without `await`; strictly intercepts dependency cycles via DFS Fail-Fast | Synchronous batch pipelines, unit test isolation |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Selection Matrix
|
|
76
|
+
|
|
77
|
+
| Dimension | **NestJS** | **InversifyJS** | **Awilix** | **@path-ioc/core** *(Standard)* | **@path-ioc/container** *(Experimental)* |
|
|
78
|
+
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
79
|
+
| **Core Contract** | `@Injectable()` + Decorators | `@injectable()` + Decorators | Regex `.toString()` + Proxy | **Physical Path Contract + Pure Closures + Static Graph** | **Physical Path Contract + High-Order Proxy** |
|
|
80
|
+
| **Code Intrusion** | High | High | Low | **Zero** (Pure ES functions) | **Zero** (Pure ES functions) |
|
|
81
|
+
| **On-Demand / Lazy** | `LazyModuleLoader` | `@lazyInject` | Proxy on-demand (sync only) | Static DAG Preheating | **Forward Subgraph Slicing** (`extractSubModules`) |
|
|
82
|
+
| **Cycle Resolution** | `forwardRef()` (breaks on async) | `@lazyInject()` | Dynamic Proxy Getter (sync) | **DFS Fail-Fast Cycle Interception** | **DFS Fail-Fast** (No dynamic async cycle breaking) |
|
|
83
|
+
| **Production Ready** | Yes | Yes | Yes | **Recommended for Production** | ⚠️ **Experimental / Not Recommended** |
|
|
84
84
|
|
|
85
85
|
---
|
|
86
86
|
|
|
@@ -89,7 +89,7 @@ Path-IoC resolves this via physical separation:
|
|
|
89
89
|
In `demand` mode, when accessing `container.UserPage` for the first time, the container does not pull the entire registry. Instead, it calls `extractSubModules(graph, 'UserPage')`:
|
|
90
90
|
|
|
91
91
|
1. **Forward Traversal**: Starting from `UserPage`, performs depth-first recursion over `resolvedDepsMap`;
|
|
92
|
-
2. **Deterministic Isolation**: Strictly collects `UserPage` and its downstream dependencies, pruning unrelated branches;
|
|
92
|
+
2. **Deterministic Isolation**: Strictly collects `UserPage` and its downstream transitive dependencies, pruning unrelated branches;
|
|
93
93
|
3. **Local Compilation**: Compiles the minimal extracted subset into a sub-graph and executes it immediately.
|
|
94
94
|
|
|
95
95
|
```
|
|
@@ -102,6 +102,8 @@ Access container.UserPage
|
|
|
102
102
|
└── 2. Compile and instantiate local slice immediately
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
+
*(Note: `extractSubModules` is an internal private algorithm function, not exported publicly; it is dispatched automatically by the Proxy Getter in `demand` mode).*
|
|
106
|
+
|
|
105
107
|
---
|
|
106
108
|
|
|
107
109
|
## API Reference
|
|
@@ -131,16 +133,7 @@ export const createContainer: (
|
|
|
131
133
|
) => Record<string, unknown>;
|
|
132
134
|
```
|
|
133
135
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
### `extractSubModules(graph, ...entryPoints)`
|
|
137
|
-
|
|
138
|
-
```typescript
|
|
139
|
-
export const extractSubModules: (
|
|
140
|
-
graph: CompiledModuleGraph,
|
|
141
|
-
...entryPoints: string[]
|
|
142
|
-
) => { key: string; module: IOCModule }[];
|
|
143
|
-
```
|
|
136
|
+
*(Note: In `eager + async` mode, a non-enumerable `$ready: Promise<void>` is attached to the returned object).*
|
|
144
137
|
|
|
145
138
|
---
|
|
146
139
|
|
package/README.zh-CN.md
CHANGED
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
</p>
|
|
16
16
|
</div>
|
|
17
17
|
|
|
18
|
-
>
|
|
19
|
-
> `@path-ioc/
|
|
20
|
-
> `@path-ioc/
|
|
18
|
+
> ⚠️ **【历史演进实验包 / 非生产推荐包】**
|
|
19
|
+
> `@path-ioc/container` 是早期为了对比评测传统 JS IoC 框架(如 InversifyJS 等)的同步 getter 懒加载机制而实现的**概念对比与基准实验包**。由于内部存在严格的按需属性串行互斥约束,**无生产级通用推荐价值,目前无活跃迭代规划**。
|
|
20
|
+
> 生产业务工程统一推荐直接使用 [`@path-ioc/core`](../core) 与 [`@path-ioc/unplugin`](../unplugin) 的 `createModularContainer`。
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
|
|
28
28
|
在工业级生产实践中(如 Java Spring 默认 `eager-singleton`),启动期全量 DAG 拓扑预热是保持系统稳定性、依赖完整性与最高运行性能的黄金标准。因此:
|
|
29
29
|
|
|
30
|
-
- **正统 Core (`@path-ioc/core`)**:坚持绝对严谨——静态图编译、DFS 严谨 Fail-Fast 拦截循环依赖、原生 `async/await`
|
|
31
|
-
- **扩展 Container (`@path-ioc/container`)
|
|
30
|
+
- **正统 Core (`@path-ioc/core`)**:坚持绝对严谨——静态图编译、DFS 严谨 Fail-Fast 拦截循环依赖、原生 `async/await` 拓扑并发,杜绝隐式解环遮蔽架构设计缺陷;
|
|
31
|
+
- **扩展 Container (`@path-ioc/container`)**:探索性扩展——通过包装高阶 Proxy 代理容器,将“加载范围 (Eager/Demand)”与“执行引擎 (Async/Turbo)”拆解为 **2 维物理正交矩阵**,对标传统 IoC 框架的按需加载模式。
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
@@ -36,51 +36,51 @@
|
|
|
36
36
|
|
|
37
37
|
在 JavaScript/TypeScript 单线程模型中,**“异步初始化 (Async)”** 与 **“运行期动态依赖感知 (Dynamic Sensing)”** 在物理上是不可调和的:
|
|
38
38
|
|
|
39
|
-
- **物理原因**:JS 的 Proxy Dynamic Getter(`
|
|
39
|
+
- **物理原因**:JS 的 Proxy Dynamic Getter(`container.foo`)本质上是纯同步的,无法在纯同步代码中间“挂起线程去异步等待”。
|
|
40
40
|
- 如果一个模块需要异步初始化(`async main`),就**必须在唤醒前通过 `dependencies` 构建静态图**,才能编排 DAG 拓扑顺序提前 `await`;
|
|
41
41
|
- 如果允许不写 `dependencies` 靠运行期动态感知,那么在 `async` 模式下一旦访问未就绪的异步节点,就必然会导致返回 `Promise` 未决遗留或微任务死锁。
|
|
42
42
|
|
|
43
43
|
Path-IoC 通过物理分治实现优雅解法:
|
|
44
|
-
- **`async`
|
|
45
|
-
- **`turbo`
|
|
46
|
-
|
|
47
|
-
---
|
|
48
|
-
|
|
49
|
-
## 主流 IoC 框架选型对比矩阵 (Selection Matrix)
|
|
50
|
-
|
|
51
|
-
| 对比维度 | **NestJS** | **InversifyJS** | **Awilix** | **TSyringe** | **TypeDI** | **@path-ioc/container** *(Path-IoC 扩展)* |
|
|
52
|
-
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
53
|
-
| **底层依赖契约** | `@Injectable()` + TS 装饰器 + `reflect-metadata` | `@injectable()` + TS 装饰器 + `reflect-metadata` | 正则解析 `.toString()` 或形参匹配 + Proxy | `@singleton()` + TS 装饰器 + `reflect-metadata` | `@Service()` + TS 装饰器 + `reflect-metadata` | **物理路径契约 + 纯 ES 模块闭包 + 静态图** |
|
|
54
|
-
| **代码侵入性** | 强侵入 (强绑定 Nest 框架装饰器与概念) | 强侵入 (强绑定 `@inject` 装饰器) | 低侵入 (按形参匹配,无需装饰器) | 强侵入 (强绑定 Microsoft 装饰器) | 强侵入 (强绑定 TypeDI 装饰器) | **零侵入** (纯 ES 模块导出函数,脱离框架完全独立可跑) |
|
|
55
|
-
| **按需/懒加载范式** | 模块级显式懒加载 (`LazyModuleLoader`) | 属性级懒注入 (`@lazyInject`) | Proxy 模式按需实例化 (纯同步属性访问) | 不支持按需 (创建容器即解析全量 Class) | 不支持按需 (实例化即解析全量 Class) | **正向极简子图按需切片** (`extractSubModules` 触达点自动正向提取子图) |
|
|
56
|
-
| **循环依赖处理** | `forwardRef()` (遇 async 易死锁) | `@lazyInject()` 延迟解析 | Dynamic Proxy Getter (仅支持纯同步) | `delay()` (需显式 Token 包装) | `constructMany` 延迟解析 | **Turbo 模式 Dynamic Getter 无锁解环** (零 Token 包装,纯同步自动解环) |
|
|
57
|
-
| **异步初始化与调度** | 串行主导 (构造函数纯同步,依赖 `onModuleInit` 钩子) | 支持 `getAsync()` (无 native DAG 调度) | 支持异步工厂 (`container.build()`) | 无 native 异步调度 | 无 native 异步调度 | **拓扑并发 / 纯同步直通双引擎** (`async` 原生 DAG 并发 / `turbo` 纯同步直通) |
|
|
44
|
+
- **`async` 模式(支持异步,必须声明 `dependencies`)**:显式声明 `dependencies` 编排静态 DAG 拓扑图,获得 **100% 原生反应式拓扑并发**(通过 `Promise.all` 级联异步唤醒);
|
|
45
|
+
- **`turbo` 模式(纯同步直通,支持免写 `dependencies`)**:**严禁任何异步 `main` 工厂**(若返回 Promise 直接抛出 `[Turbo Mode] Async module is not supported` 异常)。由于纯同步调用栈无需挂起等待,**完全支持免写 `dependencies`**,依托 Proxy Dynamic Getter 在访问属性时同步深搜并求值装配;若仅在运行期方法中相互引用,可自然避免死锁;但若在 `main` 初始化期发生同步强依赖闭环,会触发调用栈溢出或由静态 DFS Fail-Fast 报错拦截。
|
|
58
46
|
|
|
59
47
|
---
|
|
60
48
|
|
|
61
49
|
## 4 大引擎调度范式矩阵 (Evaluation Matrix)
|
|
62
50
|
|
|
63
|
-
`@path-ioc/container` 通过 `strategy` (加载范围: `eager` | `demand`) $\times$ `mode` (执行引擎: `async` | `turbo`) 划分为 4
|
|
51
|
+
`@path-ioc/container` 通过 `strategy` (加载范围: `eager` | `demand`) $\times$ `mode` (执行引擎: `async` | `turbo`) 划分为 4 种调度范式:
|
|
64
52
|
|
|
65
53
|
```
|
|
66
54
|
┌─────────────────────────┬─────────────────────────┐
|
|
67
55
|
│ mode: "async" │ mode: "turbo" │
|
|
68
56
|
│ (反应式拓扑并发引擎) │ (纯同步直通零Promise) │
|
|
69
57
|
┌───────────────────┼─────────────────────────┼─────────────────────────┤
|
|
70
|
-
│ strategy: "eager" │ ① 全量异步拓扑预热 │ ② 纯同步预热 +
|
|
71
|
-
│ (全量加载) │ (挂载 $ready 句柄) │ (
|
|
58
|
+
│ strategy: "eager" │ ① 全量异步拓扑预热 │ ② 纯同步预热 + 直通 │
|
|
59
|
+
│ (全量加载) │ (挂载 $ready 句柄) │ (高频同步工具库/测试) │
|
|
72
60
|
├───────────────────┼─────────────────────────┼─────────────────────────┤
|
|
73
61
|
│ strategy: "demand"│ ③ 极简子图按需提取 │ ④ 纯同步直通零延迟 │
|
|
74
|
-
│ (极速按需) │ (
|
|
62
|
+
│ (极速按需) │ (严格串行互斥保护) │ (极速 CLI/同步测试) │
|
|
75
63
|
└───────────────────┴─────────────────────────┴─────────────────────────┘
|
|
76
64
|
```
|
|
77
65
|
|
|
78
|
-
| 组合范式 (`strategy` $\times$ `mode`) | 物理触发机制 | 关键物理特性 |
|
|
66
|
+
| 组合范式 (`strategy` $\times$ `mode`) | 物理触发机制 | 关键物理特性 | 适用场景说明 |
|
|
79
67
|
| :--- | :--- | :--- | :--- |
|
|
80
|
-
| **`eager` + `async`** | 容器创建时立刻触发 | 全量 DAG 拓扑并发预热,容器挂载非可枚举 `container.$ready` 追踪句柄 |
|
|
81
|
-
| **`eager` + `turbo`** | 容器创建时立刻触发 |
|
|
82
|
-
| **`demand` + `async`** | 外部按需触达触发 (`container.UserPage`) |
|
|
83
|
-
| **`demand` + `turbo`** | 外部按需触达触发 (`container.foo`) | 无需 `await`
|
|
68
|
+
| **`eager` + `async`** | 容器创建时立刻触发 | 全量 DAG 拓扑并发预热,容器挂载非可枚举 `container.$ready` 追踪句柄 | 异步全量预热评测 |
|
|
69
|
+
| **`eager` + `turbo`** | 容器创建时立刻触发 | 启动时按拓扑顺序纯同步完成求值预热;若遇异步模块直接抛错 | 纯同步工具库测试 |
|
|
70
|
+
| **`demand` + `async`** | 外部按需触达触发 (`container.UserPage`) | 触达点提取子图,属性访问返回 Promise。**警告**:存在串行互斥保护,必须串行 `await`,严禁使用 `Promise.all` 并发访问多个未就绪属性 | 按需异步切片实验 |
|
|
71
|
+
| **`demand` + `turbo`** | 外部按需触达触发 (`container.foo`) | 无需 `await` 属性访问,自动算子图纯同步求值;遇环同样 Fail-Fast 报错 | 极速 CLI 命令行工具、纯同步测试 |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 主流 IoC 框架选型对比矩阵 (Selection Matrix)
|
|
76
|
+
|
|
77
|
+
| 对比维度 | **NestJS** | **InversifyJS** | **Awilix** | **@path-ioc/core** *(生产推荐)* | **@path-ioc/container** *(实验包)* |
|
|
78
|
+
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
79
|
+
| **底层依赖契约** | `@Injectable()` + TS 装饰器 | `@injectable()` + TS 装饰器 | 正则匹配形参 + Proxy | **物理路径契约 + 纯闭包工厂 + 静态图** | **物理路径契约 + 高阶代理** |
|
|
80
|
+
| **代码侵入性** | 强侵入 (框架装饰器与基类) | 强侵入 (`@inject` 装饰器) | 低侵入 (按形参匹配) | **零侵入** (纯函数导出) | **零侵入** (纯函数导出) |
|
|
81
|
+
| **按需/懒加载范式** | `LazyModuleLoader` | `@lazyInject` | Proxy 模式按需实例化 (同步) | 启动期静态 DAG 预热 | **正向极简子图按需切片** (`extractSubModules`) |
|
|
82
|
+
| **循环依赖处理** | `forwardRef()` (遇 async 易死锁) | `@lazyInject()` 延迟解析 | Dynamic Proxy Getter (同步) | **DFS Fail-Fast 严格拦截** | **DFS Fail-Fast 严格拦截** |
|
|
83
|
+
| **生产推荐状态** | 推荐 | 推荐 | 推荐 | **生产标准推荐** | ⚠️ **历史实验包 / 非生产推荐** |
|
|
84
84
|
|
|
85
85
|
---
|
|
86
86
|
|
|
@@ -97,11 +97,13 @@ Path-IoC 通过物理分治实现优雅解法:
|
|
|
97
97
|
│
|
|
98
98
|
▼
|
|
99
99
|
[ 触达点识别 ] ───► extractSubModules(graph, "UserPage")
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
│
|
|
101
|
+
├── 1. 递归提取 UserPage 正向依赖 (UserService, UserApi...)
|
|
102
|
+
└── 2. 编译极简局部子图并即刻装配
|
|
103
103
|
```
|
|
104
104
|
|
|
105
|
+
*(注:`extractSubModules` 属于包内部私有算法函数,不对外导出,由 `demand` 模式 Proxy Getter 在访问属性时自动调度)*。
|
|
106
|
+
|
|
105
107
|
---
|
|
106
108
|
|
|
107
109
|
## API 参考 (API Reference)
|
|
@@ -133,18 +135,7 @@ export const createContainer: (
|
|
|
133
135
|
) => Record<string, unknown>;
|
|
134
136
|
```
|
|
135
137
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
### `extractSubModules(graph, ...entryPoints)`
|
|
139
|
-
|
|
140
|
-
底层正向子图切片提取器工具函数。
|
|
141
|
-
|
|
142
|
-
```typescript
|
|
143
|
-
export const extractSubModules: (
|
|
144
|
-
graph: CompiledModuleGraph,
|
|
145
|
-
...entryPoints: string[]
|
|
146
|
-
) => { key: string; module: IOCModule }[];
|
|
147
|
-
```
|
|
138
|
+
*(注:在 `eager + async` 模式下返回的对象挂载了不可枚举的 `$ready: Promise<void>`)*。
|
|
148
139
|
|
|
149
140
|
---
|
|
150
141
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@path-ioc/container",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "High-level Container extensions, demand proxy and turbo mode for Path-IoC",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ioc",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"access": "public"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@path-ioc/core": "0.1.
|
|
41
|
+
"@path-ioc/core": "0.1.5"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"tsup": "^8.0.0",
|