@path-ioc/container 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +33 -40
  2. package/README.zh-CN.md +35 -44
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -15,9 +15,9 @@
15
15
  </p>
16
16
  </div>
17
17
 
18
- > **Architectural Purpose**:
19
- > `@path-ioc/core` static graph compilation and native `async/await` topological preheating form the bedrock of Path-IoC.
20
- > `@path-ioc/container` is the companion extension proving that without TypeScript decorators or `reflect-metadata`, Path-IoC natively accommodates and surpasses traditional JS IoC frameworks in **"On-Demand Subgraph Slicing"** and **"Dynamic Dependency Resolution"**, delivering **4 physically closed-loop scheduling paradigms**.
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`)**: Open and versatile—wraps high-order proxy containers to decompose "Loading Scope (`eager` | `demand`)" and "Execution Engine (`async` | `turbo`)" into a **2-dimensional orthogonal matrix**, matching traditional on-demand IoC patterns.
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/TypeScript single-threaded execution model, **"Asynchronous Initialization"** and **"Dynamic Runtime Dependency Sensing"** are physically irreconcilable:
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 (`c.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.
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**: Zero `dependencies` declarations -> Runtime Proxy Dynamic Getters -> **Zero-config pass-through & lock-free cycle unwinding**.
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 + Dynamic│
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) │ (Large SPA / Fuse Prot)│ (Ultra-fast CLI / Test)│
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 | Best Suited For |
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 | Standard servers / microservices, preheating pools and caches at startup |
81
- | **`eager` + `turbo`** | Container creation | Synchronously evaluates modules in topological order; tracks dynamically added modules | Synchronous client applications, complex monolithic toolchains |
82
- | **`demand` + `async`** | Property access (`container.UserPage`) | Traverses forward dependency tree to extract minimum slice; strict fuse protection | Large SPAs, route-level code splitting, extreme cold-start optimization |
83
- | **`demand` + `turbo`** | Property access (`container.foo`) | Instant synchronous evaluation without `await`; supports dynamic sensing & unwinding | Ultra-fast CLI tools, synchronous pipelines, unit test isolation |
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/core` 静态图编译与原生 `async/await` 拓扑并发预热是 Path-IoC 架构的核心基石。
20
- > `@path-ioc/container` 是 Path-IoC 架构治理开放与包容的对标扩展包。它证明了 Path-IoC 无需引入 TS 装饰器与 `reflect-metadata` 元数据包袱,即可原生兼容并超越传统 JS IoC 框架的 **“按需加载 (Sub-graph Slicing)”** 与 **“动态依赖感知 (Dynamic Resolution)”** 模式,提供物理完备的 **4 种全闭环调度范式**。
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`)**:展现开放包容——通过包装高阶 Proxy 代理容器,将“加载范围 (Eager/Demand)”与“执行引擎 (Async/Turbo)”拆解为 **2 维物理正交矩阵**,以能力降级的方式完美对标传统 IoC 框架的按需与依赖感知模式。
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(`c.foo`)本质上是纯同步的,无法在纯同步代码中间“挂起线程去异步等待”。
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` 模式**:显式声明 `dependencies` -> 编译静态 DAG 图 -> 获得 **100% 原生反应式拓扑并发**;
45
- - **`turbo` 模式**:免写 `dependencies` -> 运行期 Proxy Dynamic Getter 动态感知 -> 获得 **零配置直通与纯同步无锁解环**。
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 句柄) │ (高频同步工具库/APP) │
58
+ │ strategy: "eager" │ ① 全量异步拓扑预热 │ ② 纯同步预热 + 直通 │
59
+ │ (全量加载) │ (挂载 $ready 句柄) │ (高频同步工具库/测试) │
72
60
  ├───────────────────┼─────────────────────────┼─────────────────────────┤
73
61
  │ strategy: "demand"│ ③ 极简子图按需提取 │ ④ 纯同步直通零延迟 │
74
- │ (极速按需) │ (大型 SPA/单点熔断防护)│ (极速 CLI/同步工作流) │
62
+ │ (极速按需) │ (严格串行互斥保护) │ (极速 CLI/同步测试) │
75
63
  └───────────────────┴─────────────────────────┴─────────────────────────┘
76
64
  ```
77
65
 
78
- | 组合范式 (`strategy` $\times$ `mode`) | 物理触发机制 | 关键物理特性 | 最佳适用场景 |
66
+ | 组合范式 (`strategy` $\times$ `mode`) | 物理触发机制 | 关键物理特性 | 适用场景说明 |
79
67
  | :--- | :--- | :--- | :--- |
80
- | **`eager` + `async`** | 容器创建时立刻触发 | 全量 DAG 拓扑并发预热,容器挂载非可枚举 `container.$ready` 追踪句柄 | 标准 Server / 后台微服务,启动时即全量预热连接池与缓存 |
81
- | **`eager` + `turbo`** | 容器创建时立刻触发 | 启动时按拓扑顺序纯同步完成求值预热,保持对运行期动态新增/替换模块的感知 | 纯同步客户端 App、复杂全量初始化的工具库 |
82
- | **`demand` + `async`** | 外部按需触达触发 (`container.UserPage`) | 触达点顺藤摸瓜提取子图,严格单点熔断防护,彻底杜绝幽灵 Bug | 大型 SPA 单页应用路由按需懒加载、冷启动极致优化 |
83
- | **`demand` + `turbo`** | 外部按需触达触发 (`container.foo`) | 无需 `await` 属性访问,自动算子图纯同步求值,支持依赖感知与无锁解环 | 极速 CLI 命令行工具、纯同步高性能管线、单测隔离 |
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
- ├── 1. 递归提取 UserPage 正向依赖 (UserService, UserApi...)
102
- └── 2. 编译极简局部子图并即刻装配
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",
3
+ "version": "0.1.4",
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.3"
41
+ "@path-ioc/core": "0.1.4"
42
42
  },
43
43
  "devDependencies": {
44
44
  "tsup": "^8.0.0",