@path-ioc/container 0.1.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 ADDED
@@ -0,0 +1,149 @@
1
+ <div align="center">
2
+ <h1>@path-ioc/container</h1>
3
+ <p><b>High-Performance Dual-Engine Container & Subgraph Slice Scheduler for Path-IoC</b></p>
4
+ <p>Path-IoC 高阶按需容器与双引擎(Async 拓扑并发 / Turbo 纯同步直通)调度扩展</p>
5
+
6
+ <p>
7
+ <a href="https://www.npmjs.com/package/@path-ioc/container"><img src="https://img.shields.io/npm/v/@path-ioc/container.svg" alt="NPM version"></a>
8
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/npm/l/@path-ioc/container.svg" alt="License"></a>
9
+ <a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-5.0+-3178C6?logo=typescript&logoColor=white" alt="TypeScript"></a>
10
+ </p>
11
+ </div>
12
+
13
+ > **架构定位说明**:
14
+ > `@path-ioc/core` 静态图编译与原生 `async/await` 拓扑并发预热是 Path-IoC 架构的核心基石。
15
+ > `@path-ioc/container` 是 Path-IoC 架构治理开放与包容的对标扩展包。它证明了 Path-IoC 无需引入 TS 装饰器与 `reflect-metadata` 元数据包袱,即可原生兼容并超越传统 JS IoC 框架的 **“按需加载 (Sub-graph Slicing)”** 与 **“动态依赖感知 (Dynamic Resolution)”** 模式,提供物理完备的 **4 种全闭环调度范式**。
16
+
17
+ ---
18
+
19
+ ## 核心设计哲学:物理分治与按需切片 (Architecture Philosophy)
20
+
21
+ ### 1. 正统静态拓扑 vs 扩展按需代理
22
+
23
+ 在工业级生产实践中(如 Java Spring 默认 `eager-singleton`),启动期全量 DAG 拓扑预热是保持系统稳定性、依赖完整性与最高运行性能的黄金标准。因此:
24
+
25
+ - **正统 Core (`@path-ioc/core`)**:坚持绝对严谨——静态图编译、DFS 严谨 Fail-Fast 拦截循环依赖、原生 `async/await` 拓扑并发,杜绝隐式解环遮蔽架构漏洞。
26
+ - **扩展 Container (`@path-ioc/container`)**:展现开放包容——通过包装高阶 Proxy 代理容器,将“加载范围 (Eager/Demand)”与“执行引擎 (Async/Turbo)”拆解为 **2 维物理正交矩阵**,以能力降级的方式完美对标传统 IoC 框架的按需与依赖感知模式。
27
+
28
+ ---
29
+
30
+ ### 2. 物理不可调和律与物理分治 (`async` vs `turbo`)
31
+
32
+ 在 JavaScript/TypeScript 单线程模型中,**“异步初始化 (Async)”** 与 **“运行期动态依赖感知 (Dynamic Sensing)”** 在物理上是不可调和的:
33
+
34
+ - **物理原因**:JS 的 Proxy Dynamic Getter(`c.foo`)本质上是纯同步的,无法在纯同步代码中间“挂起线程去异步等待”。
35
+ - 如果一个模块需要异步初始化(`async main`),就**必须在唤醒前通过 `dependencies` 构建静态图**,才能编排 DAG 拓扑顺序提前 `await`;
36
+ - 如果允许不写 `dependencies` 靠运行期动态感知,那么在 `async` 模式下一旦访问未就绪的异步节点,就必然会导致返回 `Promise` 未决遗留或微任务死锁。
37
+
38
+ Path-IoC 通过物理分治实现优雅解法:
39
+ - **`async` 模式**:显式声明 `dependencies` -> 编译静态 DAG 图 -> 获得 **100% 原生反应式拓扑并发**;
40
+ - **`turbo` 模式**:免写 `dependencies` -> 运行期 Proxy Dynamic Getter 动态感知 -> 获得 **零配置直通与纯同步无锁解环**。
41
+
42
+ ---
43
+
44
+ ## 主流 IoC 框架选型对比矩阵 (Selection Matrix)
45
+
46
+ | 对比维度 | **NestJS** | **InversifyJS** | **Awilix** | **TSyringe** | **TypeDI** | **@path-ioc/container** *(Path-IoC 扩展)* |
47
+ | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
48
+ | **底层依赖契约** | `@Injectable()` + TS 装饰器 + `reflect-metadata` | `@injectable()` + TS 装饰器 + `reflect-metadata` | 正则解析 `.toString()` 或形参匹配 + Proxy | `@singleton()` + TS 装饰器 + `reflect-metadata` | `@Service()` + TS 装饰器 + `reflect-metadata` | **物理路径契约 + 纯 ES 模块闭包 + 静态图** |
49
+ | **代码侵入性** | 强侵入 (强绑定 Nest 框架装饰器与概念) | 强侵入 (强绑定 `@inject` 装饰器) | 低侵入 (按形参匹配,无需装饰器) | 强侵入 (强绑定 Microsoft 装饰器) | 强侵入 (强绑定 TypeDI 装饰器) | **零侵入** (纯 ES 模块导出函数,脱离框架完全独立可跑) |
50
+ | **按需/懒加载范式** | 模块级显式懒加载 (`LazyModuleLoader`) | 属性级懒注入 (`@lazyInject`) | Proxy 模式按需实例化 (纯同步属性访问) | 不支持按需 (创建容器即解析全量 Class) | 不支持按需 (实例化即解析全量 Class) | **正向极简子图按需切片** (`extractSubModules` 触达点自动正向提取子图) |
51
+ | **循环依赖处理** | `forwardRef()` (遇 async 易死锁) | `@lazyInject()` 延迟解析 | Dynamic Proxy Getter (仅支持纯同步) | `delay()` (需显式 Token 包装) | `constructMany` 延迟解析 | **Turbo 模式 Dynamic Getter 无锁解环** (零 Token 包装,纯同步自动解环) |
52
+ | **异步初始化与调度** | 串行主导 (构造函数纯同步,依赖 `onModuleInit` 钩子) | 支持 `getAsync()` (无 native DAG 调度) | 支持异步工厂 (`container.build()`) | 无 native 异步调度 | 无 native 异步调度 | **拓扑并发 / 纯同步直通双引擎** (`async` 原生 DAG 并发 / `turbo` 纯同步直通) |
53
+
54
+ ---
55
+
56
+ ## 4 大引擎调度范式矩阵 (Evaluation Matrix)
57
+
58
+ `@path-ioc/container` 通过 `strategy` (加载范围: `eager` | `demand`) $\times$ `mode` (执行引擎: `async` | `turbo`) 划分为 4 种物理完备的调度范式:
59
+
60
+ ```
61
+ ┌─────────────────────────┬─────────────────────────┐
62
+ │ mode: "async" │ mode: "turbo" │
63
+ │ (反应式拓扑并发引擎) │ (纯同步直通零Promise) │
64
+ ┌───────────────────┼─────────────────────────┼─────────────────────────┤
65
+ │ strategy: "eager" │ ① 全量异步拓扑预热 │ ② 纯同步预热 + 动态感知│
66
+ │ (全量加载) │ (挂载 $ready 句柄) │ (高频同步工具库/APP) │
67
+ ├───────────────────┼─────────────────────────┼─────────────────────────┤
68
+ │ strategy: "demand"│ ③ 极简子图按需提取 │ ④ 纯同步直通零延迟 │
69
+ │ (极速按需) │ (大型 SPA/单点熔断防护)│ (极速 CLI/同步工作流) │
70
+ └───────────────────┴─────────────────────────┴─────────────────────────┘
71
+ ```
72
+
73
+ | 组合范式 (`strategy` $\times$ `mode`) | 物理触发机制 | 关键物理特性 | 最佳适用场景 |
74
+ | :--- | :--- | :--- | :--- |
75
+ | **`eager` + `async`** | 容器创建时立刻触发 | 全量 DAG 拓扑并发预热,容器挂载非可枚举 `container.$ready` 追踪句柄 | 标准 Server / 后台微服务,启动时即全量预热连接池与缓存 |
76
+ | **`eager` + `turbo`** | 容器创建时立刻触发 | 启动时按拓扑顺序纯同步完成求值预热,保持对运行期动态新增/替换模块的感知 | 纯同步客户端 App、复杂全量初始化的工具库 |
77
+ | **`demand` + `async`** | 外部按需触达触发 (`container.UserPage`) | 触达点顺藤摸瓜提取子图,严格单点熔断防护,彻底杜绝幽灵 Bug | 大型 SPA 单页应用路由按需懒加载、冷启动极致优化 |
78
+ | **`demand` + `turbo`** | 外部按需触达触发 (`container.foo`) | 无需 `await` 属性访问,自动算子图纯同步求值,支持依赖感知与无锁解环 | 极速 CLI 命令行工具、纯同步高性能管线、单测隔离 |
79
+
80
+ ---
81
+
82
+ ## 核心机制:正向极简子图按需切片提取 (Subgraph Slice Extraction)
83
+
84
+ 在 `demand` 按需模式下,当调用方首次访问 `container.UserPage` 时,容器不会拉起全量模块,而是调用 `extractSubModules(graph, 'UserPage')`:
85
+
86
+ 1. **顺藤摸瓜**:从触达点 `UserPage` 出发,基于静态图中的 `resolvedDepsMap` 深度优先递归检索其所有正向依赖;
87
+ 2. **纯粹确定**:严格仅收集 `UserPage` 及其下游直属与间接依赖模块,排除无关模块,保证 100% 确定性;
88
+ 3. **极简编译**:将抽取的极简模块子集重新编译为局部子图,即刻完成装配。
89
+
90
+ ```
91
+ 访问 container.UserPage
92
+ │
93
+ ▼
94
+ [ 触达点识别 ] ───► extractSubModules(graph, "UserPage")
95
+ │
96
+ ├── 1. 递归提取 UserPage 正向依赖 (UserService, UserApi...)
97
+ └── 2. 编译极简局部子图并即刻装配
98
+ ```
99
+
100
+ ---
101
+
102
+ ## API 参考 (API Reference)
103
+
104
+ ### `createContainer(graph, options)`
105
+
106
+ 创建高阶代理容器。
107
+
108
+ ```typescript
109
+ export interface CreateContainerOptions {
110
+ /**
111
+ * 加载策略:
112
+ * - "eager": 全量预热 (容器创建时立刻初始化全量模块)
113
+ * - "demand": 极速按需 (基于 container.prop 属性访问按需切片拉起)
114
+ */
115
+ strategy: "eager" | "demand";
116
+
117
+ /**
118
+ * 执行引擎:
119
+ * - "async": 反应式异步拓扑并发引擎
120
+ * - "turbo": 纯同步直通零 Promise 引擎
121
+ */
122
+ mode: "async" | "turbo";
123
+ }
124
+
125
+ export const createContainer: (
126
+ graph: CompiledModuleGraph,
127
+ options: CreateContainerOptions
128
+ ) => Record<string, unknown>;
129
+ ```
130
+
131
+ ---
132
+
133
+ ### `extractSubModules(graph, ...entryPoints)`
134
+
135
+ 底层正向子图切片提取器工具函数。
136
+
137
+ ```typescript
138
+ export const extractSubModules: (
139
+ graph: CompiledModuleGraph,
140
+ ...entryPoints: string[]
141
+ ) => { key: string; module: IOCModule }[];
142
+ ```
143
+
144
+ ---
145
+
146
+ ## 许可证 (License)
147
+
148
+ Released under the [MIT License](./LICENSE).
149
+ Copyright © 2026 [Path-IoC Organization](https://github.com/path-ioc) & Lian HanLin.
package/dist/index.cjs ADDED
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createContainer: () => createContainer
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var import_core = require("@path-ioc/core");
27
+
28
+ // src/extract.ts
29
+ var extractSubModules = (graph, ...entryPoints) => {
30
+ const subgraphNames = /* @__PURE__ */ new Set();
31
+ const queue = /* @__PURE__ */ new Set();
32
+ for (const entry of entryPoints) {
33
+ let fullName = entry;
34
+ if (!graph.fullNameToModuleMap.has(fullName)) {
35
+ const mapped = graph.moduleDeclarationNames.find(
36
+ (n) => n.endsWith("/" + entry) || n === entry
37
+ );
38
+ if (mapped) fullName = mapped;
39
+ else continue;
40
+ }
41
+ queue.add(fullName);
42
+ }
43
+ while (queue.size > 0) {
44
+ const current = Array.from(queue);
45
+ queue.clear();
46
+ for (const name of current) {
47
+ if (!subgraphNames.has(name)) {
48
+ subgraphNames.add(name);
49
+ const deps = graph.resolvedDepsMap.get(name) || [];
50
+ for (const dep of deps) {
51
+ if (!subgraphNames.has(dep)) {
52
+ queue.add(dep);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ }
58
+ return Array.from(subgraphNames).map((name) => {
59
+ const decl = graph.fullNameToModuleMap.get(name);
60
+ return decl ? { key: name, module: decl.rawModule } : null;
61
+ }).filter(Boolean);
62
+ };
63
+
64
+ // src/index.ts
65
+ var bindModuleResult = (targetContainer, instantiated, mod, result) => {
66
+ targetContainer[mod.fullName] = result;
67
+ if (!targetContainer.hasOwnProperty(mod.name)) {
68
+ targetContainer[mod.name] = result;
69
+ }
70
+ instantiated.add(mod.fullName);
71
+ instantiated.add(mod.name);
72
+ };
73
+ var findModuleDecl = (graph, prop) => {
74
+ if (graph.fullNameToModuleMap.has(prop)) {
75
+ return graph.fullNameToModuleMap.get(prop);
76
+ }
77
+ const fullName = graph.moduleDeclarationNames.find(
78
+ (n) => n.endsWith("/" + prop) || n === prop
79
+ );
80
+ return fullName ? graph.fullNameToModuleMap.get(fullName) : void 0;
81
+ };
82
+ var resolveTurboSync = (graph, targetContainer, instantiated, containerProxy, prop) => {
83
+ const decl = findModuleDecl(graph, prop);
84
+ if (!decl || decl.skip) return void 0;
85
+ if (instantiated.has(decl.fullName)) return targetContainer[decl.fullName];
86
+ const subModules = extractSubModules(graph, decl.fullName);
87
+ const uninitSubModules = subModules.filter((m) => !instantiated.has(m.key));
88
+ if (uninitSubModules.length > 0) {
89
+ const subGraph = (0, import_core.compileModuleGraph)(subModules);
90
+ for (const m of subGraph.sortedDeclarations) {
91
+ if (instantiated.has(m.fullName) || m.skip) continue;
92
+ const result = m.main(containerProxy, graph.moduleDeclarationNames);
93
+ if (result instanceof Promise) {
94
+ throw new Error(
95
+ `[Turbo Mode] Async module '${m.fullName}' is not supported in turbo mode. Use async mode instead.`
96
+ );
97
+ }
98
+ bindModuleResult(targetContainer, instantiated, m, result);
99
+ }
100
+ }
101
+ return targetContainer[decl.fullName];
102
+ };
103
+ var createAsyncEagerContainer = (graph) => {
104
+ const targetContainer = { $logs: [] };
105
+ const readyPromise = (0, import_core.instantiateModuleContainer)(graph, targetContainer).then(
106
+ () => {
107
+ }
108
+ );
109
+ Object.defineProperty(targetContainer, "$ready", {
110
+ value: readyPromise,
111
+ writable: false,
112
+ enumerable: false,
113
+ configurable: false
114
+ });
115
+ return targetContainer;
116
+ };
117
+ var createAsyncDemandContainer = (graph) => {
118
+ const targetContainer = { $logs: [] };
119
+ const instantiated = /* @__PURE__ */ new Set();
120
+ let activeInitPromise = null;
121
+ let activeInitModuleName = null;
122
+ const loadSubgraph = async (prop) => {
123
+ const decl = findModuleDecl(graph, prop);
124
+ if (!decl) return;
125
+ const fullName = decl.fullName;
126
+ if (instantiated.has(fullName)) return;
127
+ if (activeInitPromise) {
128
+ throw new Error(
129
+ `[Path-IoC Error] Concurrent subgraph initialization detected! Cannot access '${prop}' while '${activeInitModuleName}' is still initializing. Ensure 'await container.${activeInitModuleName}' completes before accessing another module in demand+async mode.`
130
+ );
131
+ }
132
+ activeInitModuleName = prop;
133
+ activeInitPromise = (async () => {
134
+ const subModules = extractSubModules(graph, fullName);
135
+ const uninitSubModules = subModules.filter(
136
+ (m) => !instantiated.has(m.key)
137
+ );
138
+ if (uninitSubModules.length > 0) {
139
+ const subGraph = (0, import_core.compileModuleGraph)(subModules);
140
+ await (0, import_core.instantiateModuleContainer)(subGraph, targetContainer);
141
+ for (const m of subGraph.sortedDeclarations) {
142
+ instantiated.add(m.fullName);
143
+ instantiated.add(m.name);
144
+ }
145
+ }
146
+ })();
147
+ try {
148
+ await activeInitPromise;
149
+ } finally {
150
+ activeInitPromise = null;
151
+ activeInitModuleName = null;
152
+ }
153
+ };
154
+ return new Proxy(targetContainer, {
155
+ get(targetObj, prop) {
156
+ if (typeof prop !== "string") return Reflect.get(targetObj, prop);
157
+ if (targetObj.hasOwnProperty(prop)) return Reflect.get(targetObj, prop);
158
+ const loadPromise = loadSubgraph(prop);
159
+ return loadPromise.then(() => Reflect.get(targetObj, prop));
160
+ }
161
+ });
162
+ };
163
+ var createTurboEagerContainer = (graph) => {
164
+ const targetContainer = { $logs: [] };
165
+ const instantiated = /* @__PURE__ */ new Set();
166
+ const containerProxy = new Proxy(targetContainer, {
167
+ get(targetObj, prop) {
168
+ if (typeof prop === "string" && !targetObj.hasOwnProperty(prop)) {
169
+ return resolveTurboSync(
170
+ graph,
171
+ targetContainer,
172
+ instantiated,
173
+ containerProxy,
174
+ prop
175
+ );
176
+ }
177
+ return Reflect.get(targetObj, prop);
178
+ }
179
+ });
180
+ for (const m of graph.sortedDeclarations) {
181
+ if (!m.skip && !instantiated.has(m.fullName)) {
182
+ const result = m.main(containerProxy, graph.moduleDeclarationNames);
183
+ if (result instanceof Promise) {
184
+ throw new Error(
185
+ `[Turbo Mode] Async module '${m.fullName}' is not supported in turbo mode. Use async mode instead.`
186
+ );
187
+ }
188
+ bindModuleResult(targetContainer, instantiated, m, result);
189
+ }
190
+ }
191
+ return containerProxy;
192
+ };
193
+ var createTurboDemandContainer = (graph) => {
194
+ const targetContainer = { $logs: [] };
195
+ const instantiated = /* @__PURE__ */ new Set();
196
+ const containerProxy = new Proxy(targetContainer, {
197
+ get(targetObj, prop) {
198
+ if (typeof prop === "string" && !targetObj.hasOwnProperty(prop)) {
199
+ return resolveTurboSync(
200
+ graph,
201
+ targetContainer,
202
+ instantiated,
203
+ containerProxy,
204
+ prop
205
+ );
206
+ }
207
+ return Reflect.get(targetObj, prop);
208
+ }
209
+ });
210
+ return containerProxy;
211
+ };
212
+ var createContainer = (graph, options) => {
213
+ const { strategy, mode } = options;
214
+ if (strategy === "eager" && mode === "async") {
215
+ return createAsyncEagerContainer(graph);
216
+ }
217
+ if (strategy === "demand" && mode === "async") {
218
+ return createAsyncDemandContainer(graph);
219
+ }
220
+ if (strategy === "eager" && mode === "turbo") {
221
+ return createTurboEagerContainer(graph);
222
+ }
223
+ if (strategy === "demand" && mode === "turbo") {
224
+ return createTurboDemandContainer(graph);
225
+ }
226
+ throw new Error(
227
+ `[Path-IoC Container Error] Invalid container options: strategy='${strategy}', mode='${mode}'.`
228
+ );
229
+ };
230
+ // Annotate the CommonJS export names for ESM import in node:
231
+ 0 && (module.exports = {
232
+ createContainer
233
+ });
@@ -0,0 +1,9 @@
1
+ import { CompiledModuleGraph } from '@path-ioc/core';
2
+
3
+ interface CreateContainerOptions {
4
+ strategy: "eager" | "demand";
5
+ mode: "async" | "turbo";
6
+ }
7
+ declare const createContainer: (graph: CompiledModuleGraph, options: CreateContainerOptions) => Record<string, unknown>;
8
+
9
+ export { type CreateContainerOptions, createContainer };
@@ -0,0 +1,9 @@
1
+ import { CompiledModuleGraph } from '@path-ioc/core';
2
+
3
+ interface CreateContainerOptions {
4
+ strategy: "eager" | "demand";
5
+ mode: "async" | "turbo";
6
+ }
7
+ declare const createContainer: (graph: CompiledModuleGraph, options: CreateContainerOptions) => Record<string, unknown>;
8
+
9
+ export { type CreateContainerOptions, createContainer };
package/dist/index.js ADDED
@@ -0,0 +1,211 @@
1
+ // src/index.ts
2
+ import {
3
+ instantiateModuleContainer,
4
+ compileModuleGraph
5
+ } from "@path-ioc/core";
6
+
7
+ // src/extract.ts
8
+ var extractSubModules = (graph, ...entryPoints) => {
9
+ const subgraphNames = /* @__PURE__ */ new Set();
10
+ const queue = /* @__PURE__ */ new Set();
11
+ for (const entry of entryPoints) {
12
+ let fullName = entry;
13
+ if (!graph.fullNameToModuleMap.has(fullName)) {
14
+ const mapped = graph.moduleDeclarationNames.find(
15
+ (n) => n.endsWith("/" + entry) || n === entry
16
+ );
17
+ if (mapped) fullName = mapped;
18
+ else continue;
19
+ }
20
+ queue.add(fullName);
21
+ }
22
+ while (queue.size > 0) {
23
+ const current = Array.from(queue);
24
+ queue.clear();
25
+ for (const name of current) {
26
+ if (!subgraphNames.has(name)) {
27
+ subgraphNames.add(name);
28
+ const deps = graph.resolvedDepsMap.get(name) || [];
29
+ for (const dep of deps) {
30
+ if (!subgraphNames.has(dep)) {
31
+ queue.add(dep);
32
+ }
33
+ }
34
+ }
35
+ }
36
+ }
37
+ return Array.from(subgraphNames).map((name) => {
38
+ const decl = graph.fullNameToModuleMap.get(name);
39
+ return decl ? { key: name, module: decl.rawModule } : null;
40
+ }).filter(Boolean);
41
+ };
42
+
43
+ // src/index.ts
44
+ var bindModuleResult = (targetContainer, instantiated, mod, result) => {
45
+ targetContainer[mod.fullName] = result;
46
+ if (!targetContainer.hasOwnProperty(mod.name)) {
47
+ targetContainer[mod.name] = result;
48
+ }
49
+ instantiated.add(mod.fullName);
50
+ instantiated.add(mod.name);
51
+ };
52
+ var findModuleDecl = (graph, prop) => {
53
+ if (graph.fullNameToModuleMap.has(prop)) {
54
+ return graph.fullNameToModuleMap.get(prop);
55
+ }
56
+ const fullName = graph.moduleDeclarationNames.find(
57
+ (n) => n.endsWith("/" + prop) || n === prop
58
+ );
59
+ return fullName ? graph.fullNameToModuleMap.get(fullName) : void 0;
60
+ };
61
+ var resolveTurboSync = (graph, targetContainer, instantiated, containerProxy, prop) => {
62
+ const decl = findModuleDecl(graph, prop);
63
+ if (!decl || decl.skip) return void 0;
64
+ if (instantiated.has(decl.fullName)) return targetContainer[decl.fullName];
65
+ const subModules = extractSubModules(graph, decl.fullName);
66
+ const uninitSubModules = subModules.filter((m) => !instantiated.has(m.key));
67
+ if (uninitSubModules.length > 0) {
68
+ const subGraph = compileModuleGraph(subModules);
69
+ for (const m of subGraph.sortedDeclarations) {
70
+ if (instantiated.has(m.fullName) || m.skip) continue;
71
+ const result = m.main(containerProxy, graph.moduleDeclarationNames);
72
+ if (result instanceof Promise) {
73
+ throw new Error(
74
+ `[Turbo Mode] Async module '${m.fullName}' is not supported in turbo mode. Use async mode instead.`
75
+ );
76
+ }
77
+ bindModuleResult(targetContainer, instantiated, m, result);
78
+ }
79
+ }
80
+ return targetContainer[decl.fullName];
81
+ };
82
+ var createAsyncEagerContainer = (graph) => {
83
+ const targetContainer = { $logs: [] };
84
+ const readyPromise = instantiateModuleContainer(graph, targetContainer).then(
85
+ () => {
86
+ }
87
+ );
88
+ Object.defineProperty(targetContainer, "$ready", {
89
+ value: readyPromise,
90
+ writable: false,
91
+ enumerable: false,
92
+ configurable: false
93
+ });
94
+ return targetContainer;
95
+ };
96
+ var createAsyncDemandContainer = (graph) => {
97
+ const targetContainer = { $logs: [] };
98
+ const instantiated = /* @__PURE__ */ new Set();
99
+ let activeInitPromise = null;
100
+ let activeInitModuleName = null;
101
+ const loadSubgraph = async (prop) => {
102
+ const decl = findModuleDecl(graph, prop);
103
+ if (!decl) return;
104
+ const fullName = decl.fullName;
105
+ if (instantiated.has(fullName)) return;
106
+ if (activeInitPromise) {
107
+ throw new Error(
108
+ `[Path-IoC Error] Concurrent subgraph initialization detected! Cannot access '${prop}' while '${activeInitModuleName}' is still initializing. Ensure 'await container.${activeInitModuleName}' completes before accessing another module in demand+async mode.`
109
+ );
110
+ }
111
+ activeInitModuleName = prop;
112
+ activeInitPromise = (async () => {
113
+ const subModules = extractSubModules(graph, fullName);
114
+ const uninitSubModules = subModules.filter(
115
+ (m) => !instantiated.has(m.key)
116
+ );
117
+ if (uninitSubModules.length > 0) {
118
+ const subGraph = compileModuleGraph(subModules);
119
+ await instantiateModuleContainer(subGraph, targetContainer);
120
+ for (const m of subGraph.sortedDeclarations) {
121
+ instantiated.add(m.fullName);
122
+ instantiated.add(m.name);
123
+ }
124
+ }
125
+ })();
126
+ try {
127
+ await activeInitPromise;
128
+ } finally {
129
+ activeInitPromise = null;
130
+ activeInitModuleName = null;
131
+ }
132
+ };
133
+ return new Proxy(targetContainer, {
134
+ get(targetObj, prop) {
135
+ if (typeof prop !== "string") return Reflect.get(targetObj, prop);
136
+ if (targetObj.hasOwnProperty(prop)) return Reflect.get(targetObj, prop);
137
+ const loadPromise = loadSubgraph(prop);
138
+ return loadPromise.then(() => Reflect.get(targetObj, prop));
139
+ }
140
+ });
141
+ };
142
+ var createTurboEagerContainer = (graph) => {
143
+ const targetContainer = { $logs: [] };
144
+ const instantiated = /* @__PURE__ */ new Set();
145
+ const containerProxy = new Proxy(targetContainer, {
146
+ get(targetObj, prop) {
147
+ if (typeof prop === "string" && !targetObj.hasOwnProperty(prop)) {
148
+ return resolveTurboSync(
149
+ graph,
150
+ targetContainer,
151
+ instantiated,
152
+ containerProxy,
153
+ prop
154
+ );
155
+ }
156
+ return Reflect.get(targetObj, prop);
157
+ }
158
+ });
159
+ for (const m of graph.sortedDeclarations) {
160
+ if (!m.skip && !instantiated.has(m.fullName)) {
161
+ const result = m.main(containerProxy, graph.moduleDeclarationNames);
162
+ if (result instanceof Promise) {
163
+ throw new Error(
164
+ `[Turbo Mode] Async module '${m.fullName}' is not supported in turbo mode. Use async mode instead.`
165
+ );
166
+ }
167
+ bindModuleResult(targetContainer, instantiated, m, result);
168
+ }
169
+ }
170
+ return containerProxy;
171
+ };
172
+ var createTurboDemandContainer = (graph) => {
173
+ const targetContainer = { $logs: [] };
174
+ const instantiated = /* @__PURE__ */ new Set();
175
+ const containerProxy = new Proxy(targetContainer, {
176
+ get(targetObj, prop) {
177
+ if (typeof prop === "string" && !targetObj.hasOwnProperty(prop)) {
178
+ return resolveTurboSync(
179
+ graph,
180
+ targetContainer,
181
+ instantiated,
182
+ containerProxy,
183
+ prop
184
+ );
185
+ }
186
+ return Reflect.get(targetObj, prop);
187
+ }
188
+ });
189
+ return containerProxy;
190
+ };
191
+ var createContainer = (graph, options) => {
192
+ const { strategy, mode } = options;
193
+ if (strategy === "eager" && mode === "async") {
194
+ return createAsyncEagerContainer(graph);
195
+ }
196
+ if (strategy === "demand" && mode === "async") {
197
+ return createAsyncDemandContainer(graph);
198
+ }
199
+ if (strategy === "eager" && mode === "turbo") {
200
+ return createTurboEagerContainer(graph);
201
+ }
202
+ if (strategy === "demand" && mode === "turbo") {
203
+ return createTurboDemandContainer(graph);
204
+ }
205
+ throw new Error(
206
+ `[Path-IoC Container Error] Invalid container options: strategy='${strategy}', mode='${mode}'.`
207
+ );
208
+ };
209
+ export {
210
+ createContainer
211
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@path-ioc/container",
3
+ "version": "0.1.1",
4
+ "description": "High-level Container extensions, demand proxy and turbo mode for Path-IoC",
5
+ "keywords": [
6
+ "ioc",
7
+ "container",
8
+ "dependency-injection",
9
+ "proxy",
10
+ "lazy-load"
11
+ ],
12
+ "homepage": "https://path-ioc.dev",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/path-ioc/path-ioc.git",
16
+ "directory": "packages/container"
17
+ },
18
+ "bugs": {
19
+ "url": "https://github.com/path-ioc/path-ioc/issues"
20
+ },
21
+ "author": "Lian HanLin",
22
+ "license": "MIT",
23
+ "main": "dist/index.cjs",
24
+ "module": "dist/index.js",
25
+ "types": "dist/index.d.ts",
26
+ "type": "module",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.js",
31
+ "require": "./dist/index.cjs"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "peerDependencies": {
41
+ "@path-ioc/core": "0.1.1"
42
+ },
43
+ "devDependencies": {
44
+ "tsup": "^8.0.0",
45
+ "typescript": "^5.4.5"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup src/index.ts --format cjs,esm --dts",
49
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch"
50
+ }
51
+ }