@zhin.js/runtime 1.0.12 → 1.0.14
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/lib/config-composer.d.ts +2 -0
- package/lib/config-composer.js +17 -37
- package/lib/hmr-coordinator.d.ts +1 -1
- package/lib/hmr-coordinator.js +82 -9
- package/lib/host-config-schema.json +123 -0
- package/lib/plugin-scope-assembler.d.ts +4 -1
- package/lib/plugin-scope-assembler.js +4 -1
- package/lib/root-runtime.d.ts +2 -1
- package/lib/root-runtime.js +27 -48
- package/lib/runtime-generation.d.ts +7 -3
- package/lib/runtime-generation.js +8 -1
- package/lib/slot-generation-preparer.d.ts +1 -1
- package/lib/slot-generation-preparer.js +6 -9
- package/lib/subtree-generation-preparer.d.ts +1 -1
- package/lib/subtree-generation-preparer.js +12 -15
- package/lib/topology-generation-preparer.d.ts +1 -1
- package/lib/topology-generation-preparer.js +14 -17
- package/package.json +14 -13
package/lib/config-composer.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { PluginId } from '@zhin.js/plugin-runtime';
|
|
2
2
|
import type { ProjectGraph } from './project-graph.js';
|
|
3
|
+
/** Host configuration keys consumed by Runtime composition and Host installers. */
|
|
4
|
+
export declare const HOST_CONFIG_KEYS: readonly string[];
|
|
3
5
|
export type JsonSchema = Readonly<Record<string, unknown>>;
|
|
4
6
|
export type RuntimeConfigDocument = Readonly<Record<string, unknown>>;
|
|
5
7
|
export interface ComposedConfig {
|
package/lib/config-composer.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
4
|
+
import hostConfigSchema from './host-config-schema.json' with { type: 'json' };
|
|
5
|
+
function deepFreeze(value) {
|
|
6
|
+
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
7
|
+
for (const nested of Object.values(value))
|
|
8
|
+
deepFreeze(nested);
|
|
9
|
+
Object.freeze(value);
|
|
10
|
+
}
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
const HOST_CONFIG_SCHEMA = deepFreeze(hostConfigSchema);
|
|
14
|
+
/** Host configuration keys consumed by Runtime composition and Host installers. */
|
|
15
|
+
export const HOST_CONFIG_KEYS = Object.freeze(Object.keys(HOST_CONFIG_SCHEMA.properties));
|
|
4
16
|
export class ConfigSchemaCollisionError extends Error {
|
|
5
17
|
plugin;
|
|
6
18
|
instanceKey;
|
|
@@ -43,6 +55,7 @@ export class ConfigComposer {
|
|
|
43
55
|
type: 'object',
|
|
44
56
|
additionalProperties: false,
|
|
45
57
|
properties: {
|
|
58
|
+
...HOST_CONFIG_SCHEMA.properties,
|
|
46
59
|
plugin: withDefault(rootOwn),
|
|
47
60
|
plugins: {
|
|
48
61
|
type: 'object',
|
|
@@ -50,45 +63,10 @@ export class ConfigComposer {
|
|
|
50
63
|
default: {},
|
|
51
64
|
properties: Object.fromEntries(childSchemas.map(([key, schema]) => [key, withDefault(schema)])),
|
|
52
65
|
},
|
|
53
|
-
http: Object.freeze({
|
|
54
|
-
type: 'object',
|
|
55
|
-
additionalProperties: true,
|
|
56
|
-
}),
|
|
57
|
-
database: Object.freeze({
|
|
58
|
-
type: 'object',
|
|
59
|
-
additionalProperties: true,
|
|
60
|
-
}),
|
|
61
|
-
ai: Object.freeze({
|
|
62
|
-
type: 'object',
|
|
63
|
-
additionalProperties: true,
|
|
64
|
-
}),
|
|
65
|
-
mcp: Object.freeze({
|
|
66
|
-
type: 'object',
|
|
67
|
-
additionalProperties: true,
|
|
68
|
-
}),
|
|
69
|
-
a2a: Object.freeze({
|
|
70
|
-
type: 'object',
|
|
71
|
-
additionalProperties: true,
|
|
72
|
-
}),
|
|
73
|
-
speech: Object.freeze({
|
|
74
|
-
type: 'object',
|
|
75
|
-
additionalProperties: true,
|
|
76
|
-
}),
|
|
77
|
-
htmlRenderer: Object.freeze({
|
|
78
|
-
type: 'object',
|
|
79
|
-
additionalProperties: true,
|
|
80
|
-
}),
|
|
81
|
-
assistant: Object.freeze({
|
|
82
|
-
type: 'object',
|
|
83
|
-
additionalProperties: true,
|
|
84
|
-
}),
|
|
85
|
-
log_level: Object.freeze({
|
|
86
|
-
type: ['string', 'number'],
|
|
87
|
-
}),
|
|
88
66
|
},
|
|
89
67
|
});
|
|
90
68
|
const document = structuredClone(input);
|
|
91
|
-
const
|
|
69
|
+
const ajv = new Ajv2020({
|
|
92
70
|
allErrors: true,
|
|
93
71
|
useDefaults: true,
|
|
94
72
|
strict: true,
|
|
@@ -96,7 +74,9 @@ export class ConfigComposer {
|
|
|
96
74
|
// 不触发 allowUnionTypes);保留此项是面向未来插件 schema 可能出现的
|
|
97
75
|
// anyOf/oneOf 标量 union,避免届时 Ajv strict 模式直接报错。
|
|
98
76
|
allowUnionTypes: true,
|
|
99
|
-
})
|
|
77
|
+
});
|
|
78
|
+
ajv.addKeyword({ keyword: 'x-descriptionZh', schemaType: 'string' });
|
|
79
|
+
const validate = ajv.compile(effectiveSchema);
|
|
100
80
|
if (!validate(document)) {
|
|
101
81
|
throw new ConfigValidationError(formatErrors(validate.errors ?? []), source);
|
|
102
82
|
}
|
package/lib/hmr-coordinator.d.ts
CHANGED
package/lib/hmr-coordinator.js
CHANGED
|
@@ -5,12 +5,19 @@ export class HmrCoordinator {
|
|
|
5
5
|
#waiters = [];
|
|
6
6
|
#draining;
|
|
7
7
|
#unwatch;
|
|
8
|
+
#closing = false;
|
|
9
|
+
#restartRequired = false;
|
|
10
|
+
#stopResult;
|
|
8
11
|
constructor(options) {
|
|
9
12
|
this.options = options;
|
|
10
13
|
}
|
|
11
14
|
start() {
|
|
12
15
|
if (this.#unwatch)
|
|
13
16
|
throw new Error('HmrCoordinator is already started');
|
|
17
|
+
if (this.#closing)
|
|
18
|
+
throw new Error('HmrCoordinator has been stopped');
|
|
19
|
+
if (this.#restartRequired)
|
|
20
|
+
throw new Error('HmrCoordinator requires a process restart');
|
|
14
21
|
if (!this.options.modules.watch) {
|
|
15
22
|
throw new Error('ModuleRuntime does not provide a file watcher');
|
|
16
23
|
}
|
|
@@ -21,10 +28,23 @@ export class HmrCoordinator {
|
|
|
21
28
|
return () => this.stop();
|
|
22
29
|
}
|
|
23
30
|
stop() {
|
|
31
|
+
if (this.#stopResult)
|
|
32
|
+
return this.#stopResult;
|
|
33
|
+
this.#closing = true;
|
|
24
34
|
this.#unwatch?.();
|
|
25
35
|
this.#unwatch = undefined;
|
|
36
|
+
this.#stopResult = (async () => {
|
|
37
|
+
await this.#draining;
|
|
38
|
+
})();
|
|
39
|
+
return this.#stopResult;
|
|
26
40
|
}
|
|
27
41
|
enqueue(source) {
|
|
42
|
+
if (this.#closing) {
|
|
43
|
+
return Promise.reject(new Error('HMR coordinator is stopping'));
|
|
44
|
+
}
|
|
45
|
+
if (this.#restartRequired) {
|
|
46
|
+
return Promise.reject(new Error('HMR coordinator requires a process restart'));
|
|
47
|
+
}
|
|
28
48
|
this.#pending.add(source);
|
|
29
49
|
const completed = new Promise((resolve, reject) => {
|
|
30
50
|
this.#waiters.push({ resolve, reject });
|
|
@@ -50,14 +70,16 @@ export class HmrCoordinator {
|
|
|
50
70
|
this.#pending.clear();
|
|
51
71
|
const forcedRestart = changed.filter((source) => this.options.modules.requiresProcessRestart?.(source));
|
|
52
72
|
if (forcedRestart.length > 0) {
|
|
53
|
-
|
|
73
|
+
this.#restartRequired = true;
|
|
74
|
+
this.#pending.clear();
|
|
75
|
+
this.#notifyRestart(Object.freeze({
|
|
54
76
|
kind: 'process',
|
|
55
77
|
changed: Object.freeze(changed),
|
|
56
78
|
reasons: Object.freeze([
|
|
57
79
|
`Module loader cannot safely invalidate: ${forcedRestart.join(', ')}`,
|
|
58
80
|
]),
|
|
59
81
|
}));
|
|
60
|
-
|
|
82
|
+
break;
|
|
61
83
|
}
|
|
62
84
|
const dependencyPort = this.options.modules.affectedSources
|
|
63
85
|
? {
|
|
@@ -65,11 +87,14 @@ export class HmrCoordinator {
|
|
|
65
87
|
}
|
|
66
88
|
: undefined;
|
|
67
89
|
const plan = new InvalidationPlanner(this.options.ownership(), dependencyPort).plan(changed);
|
|
68
|
-
await this.options.onPlan?.(plan);
|
|
69
90
|
if (plan.kind === 'process') {
|
|
70
|
-
|
|
71
|
-
|
|
91
|
+
this.#restartRequired = true;
|
|
92
|
+
this.#pending.clear();
|
|
93
|
+
this.#notifyPlan(plan);
|
|
94
|
+
this.#notifyRestart(plan);
|
|
95
|
+
break;
|
|
72
96
|
}
|
|
97
|
+
this.#notifyPlan(plan);
|
|
73
98
|
if (plan.kind === 'none')
|
|
74
99
|
continue;
|
|
75
100
|
const startedAt = performance.now();
|
|
@@ -77,15 +102,30 @@ export class HmrCoordinator {
|
|
|
77
102
|
await this.options.modules.invalidate?.(source);
|
|
78
103
|
}
|
|
79
104
|
const restart = await this.options.runtime.reload(plan);
|
|
80
|
-
if (restart)
|
|
81
|
-
|
|
105
|
+
if (restart) {
|
|
106
|
+
this.#restartRequired = true;
|
|
107
|
+
this.#pending.clear();
|
|
108
|
+
this.#notifyRestart(restart);
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
82
111
|
else {
|
|
83
112
|
// reload resolves only after RootController has committed the new
|
|
84
113
|
// generation. Read ownership now so failed transactions never make
|
|
85
114
|
// newly discovered workspace packages observable to the watcher.
|
|
86
115
|
this.#syncWatchRoots();
|
|
87
116
|
const durationMs = Number((performance.now() - startedAt).toFixed(1));
|
|
88
|
-
|
|
117
|
+
try {
|
|
118
|
+
await this.options.onReload?.(plan, durationMs);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
// A projection/observer cannot change an already committed reload.
|
|
122
|
+
try {
|
|
123
|
+
await this.options.onError(error);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Diagnostic reporting is deliberately outside the outcome.
|
|
127
|
+
}
|
|
128
|
+
}
|
|
89
129
|
}
|
|
90
130
|
}
|
|
91
131
|
this.#resolveWaiters();
|
|
@@ -105,7 +145,7 @@ export class HmrCoordinator {
|
|
|
105
145
|
this.#draining = undefined;
|
|
106
146
|
// A source may arrive after the loop observed an empty queue but before
|
|
107
147
|
// this promise settled. Keep its waiter attached to a fresh transaction.
|
|
108
|
-
if (this.#pending.size > 0)
|
|
148
|
+
if (!this.#closing && !this.#restartRequired && this.#pending.size > 0)
|
|
109
149
|
this.#ensureDrain();
|
|
110
150
|
}
|
|
111
151
|
}
|
|
@@ -120,4 +160,37 @@ export class HmrCoordinator {
|
|
|
120
160
|
#syncWatchRoots() {
|
|
121
161
|
this.options.modules.updateWatchRoots?.(this.options.ownership().watchRoots());
|
|
122
162
|
}
|
|
163
|
+
#notifyRestart(plan) {
|
|
164
|
+
// Restart is a committed control outcome. Invoke the observer without
|
|
165
|
+
// awaiting it so a Process Host may await RootHost.stop() without waiting
|
|
166
|
+
// on the HMR drain that is currently delivering this notification.
|
|
167
|
+
try {
|
|
168
|
+
void Promise.resolve(this.options.onRestartRequired(plan)).catch((error) => {
|
|
169
|
+
this.#reportDiagnostic(error);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
this.#reportDiagnostic(error);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
#notifyPlan(plan) {
|
|
177
|
+
if (!this.options.onPlan)
|
|
178
|
+
return;
|
|
179
|
+
try {
|
|
180
|
+
void Promise.resolve(this.options.onPlan(plan)).catch((error) => {
|
|
181
|
+
this.#reportDiagnostic(error);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
this.#reportDiagnostic(error);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
#reportDiagnostic(error) {
|
|
189
|
+
try {
|
|
190
|
+
void Promise.resolve(this.options.onError(error)).catch(() => undefined);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Diagnostic reporting cannot change an invalidation outcome.
|
|
194
|
+
}
|
|
195
|
+
}
|
|
123
196
|
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"additionalProperties": false,
|
|
5
|
+
"properties": {
|
|
6
|
+
"http": { "type": "object", "additionalProperties": true, "description": "HTTP, Console, REST/RPC/SSE, and Webhook Host.", "x-descriptionZh": "HTTP、Console、REST/RPC/SSE 与 Webhook Host。" },
|
|
7
|
+
"database": { "type": "object", "additionalProperties": true, "description": "Database Host and dialect connection options.", "x-descriptionZh": "Database Host 与方言连接参数。" },
|
|
8
|
+
"ai": {
|
|
9
|
+
"type": "object",
|
|
10
|
+
"additionalProperties": true,
|
|
11
|
+
"description": "Providers, Agents, sessions, memory, tools, and execution security.",
|
|
12
|
+
"x-descriptionZh": "Provider、Agent、会话、记忆、工具与执行安全策略。",
|
|
13
|
+
"properties": {
|
|
14
|
+
"agent": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"additionalProperties": true,
|
|
17
|
+
"description": "Agent execution, queueing, tool, and model policies.",
|
|
18
|
+
"x-descriptionZh": "Agent 执行、排队、工具与模型策略。",
|
|
19
|
+
"properties": {
|
|
20
|
+
"inboundQueue": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"additionalProperties": true,
|
|
23
|
+
"description": "Inbound turn queue policy.",
|
|
24
|
+
"x-descriptionZh": "入站回合排队策略。",
|
|
25
|
+
"properties": {
|
|
26
|
+
"groupMode": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": ["supersede", "fifo"],
|
|
29
|
+
"description": "Replace an older queued group turn, or process all turns in arrival order.",
|
|
30
|
+
"x-descriptionZh": "覆盖较早的群聊排队回合,或按到达顺序处理全部回合。"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"execSecurity": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"enum": ["deny", "allowlist", "full"],
|
|
37
|
+
"description": "Shell command security boundary.",
|
|
38
|
+
"x-descriptionZh": "Shell 命令安全边界。"
|
|
39
|
+
},
|
|
40
|
+
"execPreset": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"enum": ["readonly", "network", "development", "custom"],
|
|
43
|
+
"description": "Command allowlist preset used outside full mode.",
|
|
44
|
+
"x-descriptionZh": "非 full 模式使用的命令白名单预设。"
|
|
45
|
+
},
|
|
46
|
+
"execApprovalMode": { "type": "string", "enum": ["ask", "allow", "deny"], "description": "Approval policy for main Agent commands.", "x-descriptionZh": "主 Agent 命令的审批策略。" },
|
|
47
|
+
"subagentExecApprovalMode": { "type": "string", "enum": ["ask", "allow", "deny"], "description": "Approval policy for sub-Agent commands.", "x-descriptionZh": "子 Agent 命令的审批策略。" },
|
|
48
|
+
"workerExecApprovalMode": { "type": "string", "enum": ["ask", "allow", "deny"], "description": "Approval policy for worker commands.", "x-descriptionZh": "Worker 命令的审批策略。" },
|
|
49
|
+
"taskExecApprovalMode": { "type": "string", "enum": ["ask", "allow", "deny"], "description": "Approval policy for task commands.", "x-descriptionZh": "Task 命令的审批策略。" },
|
|
50
|
+
"toolExecution": {
|
|
51
|
+
"type": "string",
|
|
52
|
+
"enum": ["parallel", "sequential", "tiered"],
|
|
53
|
+
"description": "How tool calls in one model step are scheduled.",
|
|
54
|
+
"x-descriptionZh": "同一模型步骤中的工具调用调度方式。"
|
|
55
|
+
},
|
|
56
|
+
"modelSizeHint": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"enum": ["", "small", "medium", "large"],
|
|
59
|
+
"description": "Optional model-size hint; an empty string clears the hint.",
|
|
60
|
+
"x-descriptionZh": "可选模型尺寸提示;空字符串表示清除提示。"
|
|
61
|
+
},
|
|
62
|
+
"promptCacheRetention": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"enum": ["in_memory", "24h"],
|
|
65
|
+
"description": "Provider prompt-cache retention policy.",
|
|
66
|
+
"x-descriptionZh": "Provider Prompt Cache 保留策略。"
|
|
67
|
+
},
|
|
68
|
+
"steeringMode": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"enum": ["one-at-a-time", "all"],
|
|
71
|
+
"description": "Process steering messages one at a time or drain all pending messages together.",
|
|
72
|
+
"x-descriptionZh": "逐条处理 Steering 消息,或一次取出全部待处理消息。"
|
|
73
|
+
},
|
|
74
|
+
"followUpMode": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"enum": ["one-at-a-time", "all"],
|
|
77
|
+
"description": "Process follow-up messages one at a time or drain all pending messages together.",
|
|
78
|
+
"x-descriptionZh": "逐条处理 Follow-up 消息,或一次取出全部待处理消息。"
|
|
79
|
+
},
|
|
80
|
+
"outputSchema": {
|
|
81
|
+
"anyOf": [
|
|
82
|
+
{ "type": "boolean" },
|
|
83
|
+
{ "type": "string", "enum": ["segments"] },
|
|
84
|
+
{ "type": "object" }
|
|
85
|
+
],
|
|
86
|
+
"description": "Structured final-output mode: false for text, true or segments for canonical message segments, or a custom JSON Schema object.",
|
|
87
|
+
"x-descriptionZh": "结构化最终输出:false 表示文本,true 或 segments 表示规范消息段,也可传入自定义 JSON Schema 对象。"
|
|
88
|
+
},
|
|
89
|
+
"schedule": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"additionalProperties": true,
|
|
92
|
+
"description": "Unattended Schedule execution policy.",
|
|
93
|
+
"x-descriptionZh": "无人值守 Schedule 执行策略。",
|
|
94
|
+
"properties": {
|
|
95
|
+
"security": {
|
|
96
|
+
"type": "object",
|
|
97
|
+
"additionalProperties": true,
|
|
98
|
+
"description": "Schedule command security policy.",
|
|
99
|
+
"x-descriptionZh": "Schedule 命令安全策略。",
|
|
100
|
+
"properties": {
|
|
101
|
+
"execPreset": {
|
|
102
|
+
"type": "string",
|
|
103
|
+
"enum": ["readonly", "network"],
|
|
104
|
+
"description": "Schedule Jobs may use only the read-only or network preset.",
|
|
105
|
+
"x-descriptionZh": "Schedule Job 只能使用只读或网络预设。"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"mcp": { "type": "object", "additionalProperties": true, "description": "Expose Bot tools through an MCP Server.", "x-descriptionZh": "把 Bot 工具公开为 MCP Server。" },
|
|
116
|
+
"a2a": { "type": "object", "additionalProperties": true, "description": "A2A Agent Card, remote execution, and Workroom callbacks.", "x-descriptionZh": "A2A Agent Card、远程执行与 Workroom 回调。" },
|
|
117
|
+
"speech": { "type": "object", "additionalProperties": true, "description": "Speech-to-text and text-to-speech Host.", "x-descriptionZh": "语音识别与语音合成 Host。" },
|
|
118
|
+
"htmlRenderer": { "type": "object", "additionalProperties": true, "description": "HTML and image rendering options.", "x-descriptionZh": "HTML/图片渲染参数。" },
|
|
119
|
+
"assistant": { "type": "object", "additionalProperties": true, "description": "Scheduled jobs, event ingress, and failure notifications.", "x-descriptionZh": "调度任务、事件入口和失败通知。" },
|
|
120
|
+
"log_level": { "type": ["string", "number"], "description": "Runtime log level.", "x-descriptionZh": "Runtime 日志级别。" },
|
|
121
|
+
"plugin": { "type": "object", "description": "Root Plugin configuration; replaced by its project schema during composition.", "x-descriptionZh": "Root Plugin 配置;组合时由项目 Schema 替换。" }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
@@ -8,6 +8,8 @@ import { type PrimaryConfig } from './primary-config.js';
|
|
|
8
8
|
import type { RuntimeConfigDocument } from './config-composer.js';
|
|
9
9
|
export type PluginConfigResolver = (node: PluginGraphNode) => unknown;
|
|
10
10
|
export interface RootResourceContext {
|
|
11
|
+
/** Exact shadow generation being assembled; never inferred from a latest snapshot. */
|
|
12
|
+
readonly generation: number;
|
|
11
13
|
readonly signal: AbortSignal;
|
|
12
14
|
readonly resources: Scope;
|
|
13
15
|
readonly lifecycle: DisposeStack;
|
|
@@ -36,13 +38,14 @@ export declare class PluginScopeAssembler {
|
|
|
36
38
|
private readonly configResolver;
|
|
37
39
|
private readonly environment;
|
|
38
40
|
private readonly primaryConfigDocument;
|
|
41
|
+
private readonly generation;
|
|
39
42
|
private readonly installResources?;
|
|
40
43
|
private readonly isolation?;
|
|
41
44
|
readonly scopes: Map<PluginId, Scope>;
|
|
42
45
|
readonly tree: Map<PluginId, PluginNodeSnapshot>;
|
|
43
46
|
readonly config: Map<PluginId, unknown>;
|
|
44
47
|
readonly resources: Map<PluginId, ReadonlyMap<TokenId, unknown>>;
|
|
45
|
-
constructor(modules: ModuleRuntime, configResolver: PluginConfigResolver, environment: RuntimeEnvironment, primaryConfigDocument: RuntimeConfigDocument, installResources?: RootResourceInstaller | undefined, environmentLayers?: EnvironmentLayers, seed?: PluginAssemblySeed, isolation?: IsolatedPluginRuntimePort | undefined);
|
|
48
|
+
constructor(modules: ModuleRuntime, configResolver: PluginConfigResolver, environment: RuntimeEnvironment, primaryConfigDocument: RuntimeConfigDocument, generation: number, installResources?: RootResourceInstaller | undefined, environmentLayers?: EnvironmentLayers, seed?: PluginAssemblySeed, isolation?: IsolatedPluginRuntimePort | undefined);
|
|
46
49
|
removeSubtrees(roots: readonly PluginId[]): void;
|
|
47
50
|
installSetupFeatureAliases(aliases: ReadonlyMap<string, FeatureId>): void;
|
|
48
51
|
setupTree(node: PluginGraphNode, signal: AbortSignal): Promise<void>;
|
|
@@ -9,6 +9,7 @@ export class PluginScopeAssembler {
|
|
|
9
9
|
configResolver;
|
|
10
10
|
environment;
|
|
11
11
|
primaryConfigDocument;
|
|
12
|
+
generation;
|
|
12
13
|
installResources;
|
|
13
14
|
isolation;
|
|
14
15
|
scopes;
|
|
@@ -21,11 +22,12 @@ export class PluginScopeAssembler {
|
|
|
21
22
|
#admission = createGenerationAdmissionGate();
|
|
22
23
|
#envStores;
|
|
23
24
|
#setupFeatureAliases = new Map();
|
|
24
|
-
constructor(modules, configResolver, environment, primaryConfigDocument, installResources, environmentLayers = {}, seed, isolation) {
|
|
25
|
+
constructor(modules, configResolver, environment, primaryConfigDocument, generation, installResources, environmentLayers = {}, seed, isolation) {
|
|
25
26
|
this.modules = modules;
|
|
26
27
|
this.configResolver = configResolver;
|
|
27
28
|
this.environment = environment;
|
|
28
29
|
this.primaryConfigDocument = primaryConfigDocument;
|
|
30
|
+
this.generation = generation;
|
|
29
31
|
this.installResources = installResources;
|
|
30
32
|
this.isolation = isolation;
|
|
31
33
|
this.#envStores = new EnvStoreFactory(environment, environmentLayers);
|
|
@@ -68,6 +70,7 @@ export class PluginScopeAssembler {
|
|
|
68
70
|
const config = createPrimaryConfig(this.primaryConfigDocument, environment);
|
|
69
71
|
scope.provide(primaryConfigToken, config);
|
|
70
72
|
await this.installResources?.({
|
|
73
|
+
generation: this.generation,
|
|
71
74
|
signal,
|
|
72
75
|
resources: scope,
|
|
73
76
|
lifecycle: scope.disposers,
|
package/lib/root-runtime.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { IsolatedPluginRuntimePort } from './isolation.js';
|
|
|
8
8
|
import type { ModuleRuntime } from './module-runtime.js';
|
|
9
9
|
import { type PluginConfigResolver, type RootResourceInstaller } from './plugin-scope-assembler.js';
|
|
10
10
|
import { HmrCoordinator, type HmrCoordinatorOptions } from './hmr-coordinator.js';
|
|
11
|
+
import { type RuntimeGenerationState } from './runtime-generation.js';
|
|
11
12
|
import { RootProcessRestartExecutor, type ProcessRestartAdapter } from './process-restart.js';
|
|
12
13
|
import { SourceOwnershipIndex } from './source-ownership.js';
|
|
13
14
|
export type { PluginConfigResolver, RootResourceContext, RootResourceInstaller, } from './plugin-scope-assembler.js';
|
|
@@ -27,7 +28,7 @@ export declare class RootRuntime {
|
|
|
27
28
|
constructor(options: RootRuntimeOptions);
|
|
28
29
|
get snapshot(): RuntimeSnapshot;
|
|
29
30
|
get snapshots(): SnapshotReader;
|
|
30
|
-
onGenerationCommit(listener: GenerationCommitListener): () => void;
|
|
31
|
+
onGenerationCommit(listener: GenerationCommitListener<RuntimeGenerationState>): () => void;
|
|
31
32
|
get sourceOwnership(): SourceOwnershipIndex;
|
|
32
33
|
start(): Promise<RuntimeSnapshot>;
|
|
33
34
|
reload(target?: PluginId | string): Promise<RuntimeSnapshot>;
|
package/lib/root-runtime.js
CHANGED
|
@@ -14,6 +14,7 @@ import { NodePackageResolver } from './package-resolver.js';
|
|
|
14
14
|
import { PluginScopeAssembler, } from './plugin-scope-assembler.js';
|
|
15
15
|
import { ProjectGraphService, } from './project-graph.js';
|
|
16
16
|
import { HmrCoordinator } from './hmr-coordinator.js';
|
|
17
|
+
import { prepareRuntimeGeneration, } from './runtime-generation.js';
|
|
17
18
|
import { RootProcessRestartExecutor, } from './process-restart.js';
|
|
18
19
|
import { SlotGenerationPreparer } from './slot-generation-preparer.js';
|
|
19
20
|
import { capabilityDeltaFromSlots, capabilityDeltaIds, ConventionCapabilityDeltaResolver, filterCapabilityDelta, mergeCapabilityDeltas, } from './convention-capability-delta.js';
|
|
@@ -33,8 +34,6 @@ export class RootRuntime {
|
|
|
33
34
|
#configDocument;
|
|
34
35
|
#installResources;
|
|
35
36
|
#isolation;
|
|
36
|
-
#ownership = SourceOwnershipIndex.empty();
|
|
37
|
-
#model;
|
|
38
37
|
#configPatchTail = Promise.resolve();
|
|
39
38
|
#stopResult;
|
|
40
39
|
#controller;
|
|
@@ -51,7 +50,7 @@ export class RootRuntime {
|
|
|
51
50
|
this.#configDocument = structuredClone(options.config ?? {});
|
|
52
51
|
this.#installResources = options.installResources;
|
|
53
52
|
this.#isolation = options.isolation;
|
|
54
|
-
this.#controller = new RootController(emptyState(), options.onControlError);
|
|
53
|
+
this.#controller = new RootController(emptyState(), options.onControlError, Object.freeze({ ownership: SourceOwnershipIndex.empty() }));
|
|
55
54
|
}
|
|
56
55
|
get snapshot() {
|
|
57
56
|
return this.#controller.snapshot;
|
|
@@ -63,7 +62,7 @@ export class RootRuntime {
|
|
|
63
62
|
return this.#controller.onGenerationCommit(listener);
|
|
64
63
|
}
|
|
65
64
|
get sourceOwnership() {
|
|
66
|
-
return this.#ownership;
|
|
65
|
+
return this.#controller.committed.state.ownership;
|
|
67
66
|
}
|
|
68
67
|
async start() {
|
|
69
68
|
if (this.#configPort) {
|
|
@@ -71,21 +70,15 @@ export class RootRuntime {
|
|
|
71
70
|
this.#configSnapshot = snapshot;
|
|
72
71
|
this.#configDocument = structuredClone(snapshot.document);
|
|
73
72
|
}
|
|
74
|
-
let prepared;
|
|
75
73
|
const snapshot = await this.#controller.start(async (current, signal) => {
|
|
76
|
-
|
|
77
|
-
return prepared.generation;
|
|
74
|
+
return (await this.#prepare(current, signal)).generation;
|
|
78
75
|
});
|
|
79
|
-
this.#accept(requirePrepared(prepared));
|
|
80
76
|
return snapshot;
|
|
81
77
|
}
|
|
82
78
|
async reload(target = rootPluginId()) {
|
|
83
|
-
let prepared;
|
|
84
79
|
const snapshot = await this.#controller.reload(target, async (current, signal) => {
|
|
85
|
-
|
|
86
|
-
return prepared.generation;
|
|
80
|
+
return (await this.#prepare(current, signal)).generation;
|
|
87
81
|
});
|
|
88
|
-
this.#accept(requirePrepared(prepared));
|
|
89
82
|
return snapshot;
|
|
90
83
|
}
|
|
91
84
|
patchConfig(patches) {
|
|
@@ -98,7 +91,7 @@ export class RootRuntime {
|
|
|
98
91
|
return new HmrCoordinator({
|
|
99
92
|
...options,
|
|
100
93
|
modules: this.#modules,
|
|
101
|
-
ownership: () => this
|
|
94
|
+
ownership: () => this.sourceOwnership,
|
|
102
95
|
runtime: {
|
|
103
96
|
reload: async (plan) => {
|
|
104
97
|
const result = await this.#reloadPlan(plan);
|
|
@@ -125,9 +118,9 @@ export class RootRuntime {
|
|
|
125
118
|
return result;
|
|
126
119
|
}
|
|
127
120
|
async #reloadPlan(plan) {
|
|
128
|
-
let prepared;
|
|
129
121
|
let restart;
|
|
130
122
|
const snapshot = await this.#controller.reload(plan.subtrees[0] ?? plan.slots[0] ?? rootPluginId(), async (current, signal) => {
|
|
123
|
+
let prepared;
|
|
131
124
|
const resolved = await this.#resolveCapabilityDelta(current, plan);
|
|
132
125
|
const effective = resolved.plan;
|
|
133
126
|
if (this.#model && effective.manifestSources.length > 0) {
|
|
@@ -154,13 +147,10 @@ export class RootRuntime {
|
|
|
154
147
|
});
|
|
155
148
|
if (restart)
|
|
156
149
|
return restart;
|
|
157
|
-
if (prepared)
|
|
158
|
-
this.#accept(prepared);
|
|
159
150
|
return snapshot;
|
|
160
151
|
}
|
|
161
|
-
#
|
|
162
|
-
this.#
|
|
163
|
-
this.#model = prepared.model;
|
|
152
|
+
get #model() {
|
|
153
|
+
return this.#controller.committed.state.model;
|
|
164
154
|
}
|
|
165
155
|
async #resolveCapabilityDelta(current, plan) {
|
|
166
156
|
const known = capabilityDeltaFromSlots(current, plan.slots);
|
|
@@ -185,7 +175,7 @@ export class RootRuntime {
|
|
|
185
175
|
if (plan.subtrees.length === 0 || plan.subtrees.includes(rootPluginId()))
|
|
186
176
|
return false;
|
|
187
177
|
return plan.changed.every((source) => {
|
|
188
|
-
const records = this
|
|
178
|
+
const records = this.sourceOwnership.recordsFor(source);
|
|
189
179
|
return records.length > 0 && records.every((record) => record.role === 'plugin' || record.role === 'schema');
|
|
190
180
|
});
|
|
191
181
|
}
|
|
@@ -247,10 +237,10 @@ export class RootRuntime {
|
|
|
247
237
|
await this.#refreshConfigDocument();
|
|
248
238
|
const currentDocument = requireConfigDocument(this.#configDocument);
|
|
249
239
|
let plan;
|
|
250
|
-
let prepared;
|
|
251
240
|
let documentTransaction;
|
|
252
241
|
let committedDocument;
|
|
253
242
|
const snapshot = await this.#controller.reload(rootPluginId(), async (current, signal) => {
|
|
243
|
+
let prepared;
|
|
254
244
|
signal.throwIfAborted();
|
|
255
245
|
const resolver = await NodePackageResolver.create(this.#projectRoot);
|
|
256
246
|
const graph = await new ProjectGraphService(resolver).inspect(this.#projectRoot);
|
|
@@ -305,8 +295,6 @@ export class RootRuntime {
|
|
|
305
295
|
}
|
|
306
296
|
});
|
|
307
297
|
const completed = requireConfigPatchPlan(plan);
|
|
308
|
-
if (prepared)
|
|
309
|
-
this.#accept(prepared);
|
|
310
298
|
this.#configDocument = completed.candidate;
|
|
311
299
|
if (committedDocument)
|
|
312
300
|
this.#configSnapshot = committedDocument;
|
|
@@ -406,7 +394,7 @@ class GenerationAssembler {
|
|
|
406
394
|
this.environmentLayers = environmentLayers;
|
|
407
395
|
this.isolation = isolation;
|
|
408
396
|
this.#host = new NodeDiscoveryHost(modules);
|
|
409
|
-
this.#plugins = new PluginScopeAssembler(modules, configResolver, environment, primaryConfigDocument, installResources, environmentLayers, undefined, isolation);
|
|
397
|
+
this.#plugins = new PluginScopeAssembler(modules, configResolver, environment, primaryConfigDocument, generation, installResources, environmentLayers, undefined, isolation);
|
|
410
398
|
}
|
|
411
399
|
async prepare(signal) {
|
|
412
400
|
signal.throwIfAborted();
|
|
@@ -426,25 +414,21 @@ class GenerationAssembler {
|
|
|
426
414
|
const snapshot = createSnapshotView(this.generation, state);
|
|
427
415
|
const ownership = SourceOwnershipIndex.fromGeneration(this.graph, snapshot, this.#featureIdsByPackageRoot);
|
|
428
416
|
const assets = GenerationAssets.create(this.#plugins.createdScopeDisposers(), this.#projectionDisposers);
|
|
429
|
-
return {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
scopes: new Map(this.#plugins.scopes),
|
|
445
|
-
assets,
|
|
446
|
-
},
|
|
447
|
-
};
|
|
417
|
+
return prepareRuntimeGeneration({
|
|
418
|
+
snapshot: state,
|
|
419
|
+
dispose: () => assets.dispose(),
|
|
420
|
+
handoff: composeGenerationHandoffs(this.#plugins.generationHandoff(), projected.handoff),
|
|
421
|
+
}, ownership, {
|
|
422
|
+
graph: this.graph,
|
|
423
|
+
providers: new Map(this.#catalog.values().map((provider) => [provider.id, provider])),
|
|
424
|
+
rootsByFeature: new Map([...this.#rootsByFeature].map(([feature, roots]) => [
|
|
425
|
+
feature,
|
|
426
|
+
Object.freeze([...roots]),
|
|
427
|
+
])),
|
|
428
|
+
featureIdsByPackageRoot: new Map(this.#featureIdsByPackageRoot),
|
|
429
|
+
scopes: new Map(this.#plugins.scopes),
|
|
430
|
+
assets,
|
|
431
|
+
});
|
|
448
432
|
}
|
|
449
433
|
catch (error) {
|
|
450
434
|
await disposePreparedParts(this.#plugins.createdScopeDisposers().map(([, dispose]) => dispose), [...this.#projectionDisposers.values()], error);
|
|
@@ -507,11 +491,6 @@ function emptyState() {
|
|
|
507
491
|
projections: new Map(),
|
|
508
492
|
};
|
|
509
493
|
}
|
|
510
|
-
function requirePrepared(prepared) {
|
|
511
|
-
if (!prepared)
|
|
512
|
-
throw new Error('RootController committed without a prepared generation');
|
|
513
|
-
return prepared;
|
|
514
|
-
}
|
|
515
494
|
function isProcessPlan(value) {
|
|
516
495
|
return 'kind' in value && value.kind === 'process';
|
|
517
496
|
}
|
|
@@ -11,8 +11,12 @@ export interface RuntimeGenerationModel {
|
|
|
11
11
|
readonly scopes: ReadonlyMap<PluginId, Scope>;
|
|
12
12
|
readonly assets: GenerationAssets;
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
/** Sidecar state that must never be observed from a different generation. */
|
|
15
|
+
export interface RuntimeGenerationState {
|
|
16
16
|
readonly ownership: SourceOwnershipIndex;
|
|
17
|
-
readonly model
|
|
17
|
+
readonly model?: RuntimeGenerationModel;
|
|
18
|
+
}
|
|
19
|
+
export interface PreparedRuntimeGeneration {
|
|
20
|
+
readonly generation: PreparedGeneration<RuntimeGenerationState>;
|
|
18
21
|
}
|
|
22
|
+
export declare function prepareRuntimeGeneration(generation: PreparedGeneration, ownership: SourceOwnershipIndex, model: RuntimeGenerationModel): PreparedRuntimeGeneration;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type CapabilityId, type RuntimeSnapshot } from '@zhin.js/plugin-runtime';
|
|
2
2
|
import type { ModuleRuntime } from './module-runtime.js';
|
|
3
|
-
import type
|
|
3
|
+
import { type PreparedRuntimeGeneration, type RuntimeGenerationModel } from './runtime-generation.js';
|
|
4
4
|
import { type CapabilityDelta } from './convention-capability-delta.js';
|
|
5
5
|
export declare class SlotGenerationPreparer {
|
|
6
6
|
private readonly modules;
|
|
@@ -2,6 +2,7 @@ import { DisposeStack, createSnapshotView, } from '@zhin.js/plugin-runtime';
|
|
|
2
2
|
import { FeatureDiscovery } from '@zhin.js/feature-kit';
|
|
3
3
|
import { FeatureProjector, composeGenerationHandoffs } from './feature-projector.js';
|
|
4
4
|
import { NodeDiscoveryHost } from './node-discovery-host.js';
|
|
5
|
+
import { prepareRuntimeGeneration, } from './runtime-generation.js';
|
|
5
6
|
import { SourceOwnershipIndex } from './source-ownership.js';
|
|
6
7
|
import { capabilityDeltaFromSlots, } from './convention-capability-delta.js';
|
|
7
8
|
export class SlotGenerationPreparer {
|
|
@@ -48,15 +49,11 @@ export class SlotGenerationPreparer {
|
|
|
48
49
|
const snapshot = createSnapshotView(current.generation + 1, projected.state);
|
|
49
50
|
const ownership = SourceOwnershipIndex.fromGeneration(this.model.graph, snapshot, this.model.featureIdsByPackageRoot);
|
|
50
51
|
const assets = this.model.assets.replaceProjections(selectedByFeature.keys(), projected.disposers);
|
|
51
|
-
return {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
},
|
|
57
|
-
ownership,
|
|
58
|
-
model: { ...this.model, assets },
|
|
59
|
-
};
|
|
52
|
+
return prepareRuntimeGeneration({
|
|
53
|
+
snapshot: projected.state,
|
|
54
|
+
dispose: () => assets.dispose(),
|
|
55
|
+
handoff: composeGenerationHandoffs(projected.handoff),
|
|
56
|
+
}, ownership, { ...this.model, assets });
|
|
60
57
|
}
|
|
61
58
|
catch (error) {
|
|
62
59
|
await disposeProjections(projected.disposers.values(), error);
|
|
@@ -6,7 +6,7 @@ import type { IsolatedPluginRuntimePort } from './isolation.js';
|
|
|
6
6
|
import type { ModuleRuntime } from './module-runtime.js';
|
|
7
7
|
import { type PluginConfigResolver, type RootResourceInstaller } from './plugin-scope-assembler.js';
|
|
8
8
|
import type { ProjectGraph } from './project-graph.js';
|
|
9
|
-
import type
|
|
9
|
+
import { type PreparedRuntimeGeneration, type RuntimeGenerationModel } from './runtime-generation.js';
|
|
10
10
|
export declare class SubtreeTopologyChangedError extends Error {
|
|
11
11
|
constructor(message: string);
|
|
12
12
|
}
|
|
@@ -3,6 +3,7 @@ import { FeatureDiscovery } from '@zhin.js/feature-kit';
|
|
|
3
3
|
import { FeatureProjector, composeGenerationHandoffs } from './feature-projector.js';
|
|
4
4
|
import { NodeDiscoveryHost } from './node-discovery-host.js';
|
|
5
5
|
import { PluginScopeAssembler, } from './plugin-scope-assembler.js';
|
|
6
|
+
import { prepareRuntimeGeneration, } from './runtime-generation.js';
|
|
6
7
|
import { SourceOwnershipIndex } from './source-ownership.js';
|
|
7
8
|
import { addCapabilitySlot, featureSetupAliases, mergeSetupCapabilities, } from './setup-capabilities.js';
|
|
8
9
|
export class SubtreeTopologyChangedError extends Error {
|
|
@@ -37,7 +38,7 @@ export class SubtreeGenerationPreparer {
|
|
|
37
38
|
signal.throwIfAborted();
|
|
38
39
|
const nodes = indexGraph(this.graph);
|
|
39
40
|
assertCompatibleTopology(indexGraph(this.model.graph), nodes, roots);
|
|
40
|
-
const plugins = new PluginScopeAssembler(this.modules, this.configResolver, this.environment, this.primaryConfigDocument, this.installResources, this.environmentLayers, {
|
|
41
|
+
const plugins = new PluginScopeAssembler(this.modules, this.configResolver, this.environment, this.primaryConfigDocument, current.generation + 1, this.installResources, this.environmentLayers, {
|
|
41
42
|
scopes: this.model.scopes,
|
|
42
43
|
tree: current.tree,
|
|
43
44
|
config: current.config,
|
|
@@ -86,20 +87,16 @@ export class SubtreeGenerationPreparer {
|
|
|
86
87
|
// Local prepare still commits a complete immutable generation. The
|
|
87
88
|
// replacement map controls lifetime ownership, not snapshot granularity.
|
|
88
89
|
const assets = this.model.assets.replaceScopes([...nodes.keys()], replacements, projectionDisposers);
|
|
89
|
-
return {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
scopes: new Map(plugins.scopes),
|
|
100
|
-
assets,
|
|
101
|
-
},
|
|
102
|
-
};
|
|
90
|
+
return prepareRuntimeGeneration({
|
|
91
|
+
snapshot: projected.state,
|
|
92
|
+
dispose: () => assets.dispose(),
|
|
93
|
+
handoff: composeGenerationHandoffs(plugins.generationHandoff(), projected.handoff),
|
|
94
|
+
}, ownership, {
|
|
95
|
+
...this.model,
|
|
96
|
+
graph: this.graph,
|
|
97
|
+
scopes: new Map(plugins.scopes),
|
|
98
|
+
assets,
|
|
99
|
+
});
|
|
103
100
|
}
|
|
104
101
|
catch (error) {
|
|
105
102
|
await rollback(plugins.createdScopeDisposers().map(([, dispose]) => dispose), [...projectionDisposers.values()], error);
|
|
@@ -6,7 +6,7 @@ import type { IsolatedPluginRuntimePort } from './isolation.js';
|
|
|
6
6
|
import type { ModuleRuntime } from './module-runtime.js';
|
|
7
7
|
import { type PluginConfigResolver, type RootResourceInstaller } from './plugin-scope-assembler.js';
|
|
8
8
|
import type { ProjectGraph } from './project-graph.js';
|
|
9
|
-
import type
|
|
9
|
+
import { type PreparedRuntimeGeneration, type RuntimeGenerationModel } from './runtime-generation.js';
|
|
10
10
|
import { type CapabilityDelta } from './convention-capability-delta.js';
|
|
11
11
|
/** Runtime-local invalidation to commit alongside an ABI-safe manifest change. */
|
|
12
12
|
export interface TopologyRuntimeDelta {
|
|
@@ -5,6 +5,7 @@ import { FeatureCatalog, FeatureDiscovery, } from '@zhin.js/feature-kit';
|
|
|
5
5
|
import { FeatureProjector, composeGenerationHandoffs } from './feature-projector.js';
|
|
6
6
|
import { NodeDiscoveryHost } from './node-discovery-host.js';
|
|
7
7
|
import { PluginScopeAssembler, } from './plugin-scope-assembler.js';
|
|
8
|
+
import { prepareRuntimeGeneration, } from './runtime-generation.js';
|
|
8
9
|
import { SourceOwnershipIndex } from './source-ownership.js';
|
|
9
10
|
import { capabilityDeltaFromSlots, capabilityDeltaIds, } from './convention-capability-delta.js';
|
|
10
11
|
import { addCapabilitySlot, featureSetupAliases, mergeSetupCapabilities, } from './setup-capabilities.js';
|
|
@@ -46,7 +47,7 @@ export class TopologyGenerationPreparer {
|
|
|
46
47
|
if (!plan.changed && subtreeRoots.length === 0 && selected.size === 0)
|
|
47
48
|
return undefined;
|
|
48
49
|
const featureTopology = await this.#loadFeatureTopology(plan);
|
|
49
|
-
const plugins = new PluginScopeAssembler(this.modules, this.configResolver, this.environment, this.primaryConfigDocument, this.installResources, this.environmentLayers, {
|
|
50
|
+
const plugins = new PluginScopeAssembler(this.modules, this.configResolver, this.environment, this.primaryConfigDocument, current.generation + 1, this.installResources, this.environmentLayers, {
|
|
50
51
|
scopes: this.model.scopes,
|
|
51
52
|
tree: current.tree,
|
|
52
53
|
config: current.config,
|
|
@@ -91,22 +92,18 @@ export class TopologyGenerationPreparer {
|
|
|
91
92
|
const ownership = SourceOwnershipIndex.fromGeneration(this.graph, snapshot, featureTopology.featureIdsByPackageRoot);
|
|
92
93
|
const replacements = new Map(plugins.createdScopeDisposers());
|
|
93
94
|
const assets = this.model.assets.replaceScopes(graphOrder(this.graph), replacements, projectionDisposers);
|
|
94
|
-
return {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
scopes: new Map(plugins.scopes),
|
|
107
|
-
assets,
|
|
108
|
-
},
|
|
109
|
-
};
|
|
95
|
+
return prepareRuntimeGeneration({
|
|
96
|
+
snapshot: projected.state,
|
|
97
|
+
dispose: () => assets.dispose(),
|
|
98
|
+
handoff: composeGenerationHandoffs(plugins.generationHandoff(), projected.handoff),
|
|
99
|
+
}, ownership, {
|
|
100
|
+
graph: this.graph,
|
|
101
|
+
providers: featureTopology.providers,
|
|
102
|
+
rootsByFeature: featureTopology.rootsByFeature,
|
|
103
|
+
featureIdsByPackageRoot: featureTopology.featureIdsByPackageRoot,
|
|
104
|
+
scopes: new Map(plugins.scopes),
|
|
105
|
+
assets,
|
|
106
|
+
});
|
|
110
107
|
}
|
|
111
108
|
catch (error) {
|
|
112
109
|
await rollback(plugins.createdScopeDisposers().map(([, dispose]) => dispose), [...projectionDisposers.values()], error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhin.js/runtime",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "Static Plugin graph, generation transaction and HMR Root runtime for Zhin.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -18,22 +18,23 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"ajv": "8.18.0",
|
|
20
20
|
"semver": "7.8.5",
|
|
21
|
-
"@zhin.js/
|
|
22
|
-
"@zhin.js/
|
|
21
|
+
"@zhin.js/plugin-runtime": "1.1.8",
|
|
22
|
+
"@zhin.js/feature-kit": "1.0.13"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^26.1.2",
|
|
26
26
|
"typescript": "^6.0.3",
|
|
27
|
-
"@zhin.js/
|
|
28
|
-
"@zhin.js/
|
|
29
|
-
"@zhin.js/command": "1.0.
|
|
30
|
-
"@zhin.js/component": "1.0.
|
|
31
|
-
"@zhin.js/layout": "1.0.
|
|
32
|
-
"@zhin.js/mcp-feature": "1.0.
|
|
33
|
-
"@zhin.js/middleware": "1.0.
|
|
34
|
-
"@zhin.js/
|
|
35
|
-
"@zhin.js/skill": "1.0.
|
|
36
|
-
"@zhin.js/
|
|
27
|
+
"@zhin.js/agent-feature": "1.0.13",
|
|
28
|
+
"@zhin.js/adapter": "1.2.1",
|
|
29
|
+
"@zhin.js/command": "1.0.16",
|
|
30
|
+
"@zhin.js/component": "1.0.13",
|
|
31
|
+
"@zhin.js/layout": "1.0.13",
|
|
32
|
+
"@zhin.js/mcp-feature": "1.0.13",
|
|
33
|
+
"@zhin.js/middleware": "1.0.13",
|
|
34
|
+
"@zhin.js/prompt-section": "0.0.1",
|
|
35
|
+
"@zhin.js/skill": "1.0.13",
|
|
36
|
+
"@zhin.js/page": "1.0.13",
|
|
37
|
+
"@zhin.js/tool": "1.0.13"
|
|
37
38
|
},
|
|
38
39
|
"repository": {
|
|
39
40
|
"type": "git",
|