@aionis/manifest 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/CHANGELOG.md +26 -0
- package/LICENSE +178 -0
- package/README.md +170 -0
- package/dist/ast/types.d.ts +34 -0
- package/dist/ast/types.js +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +122 -0
- package/dist/compile.d.ts +14 -0
- package/dist/compile.js +219 -0
- package/dist/contracts.d.ts +12610 -0
- package/dist/contracts.js +401 -0
- package/dist/diagnostics/types.d.ts +17 -0
- package/dist/diagnostics/types.js +1 -0
- package/dist/execute/localRuntime.d.ts +5 -0
- package/dist/execute/localRuntime.js +165 -0
- package/dist/execute/moduleRuntime.d.ts +25 -0
- package/dist/execute/moduleRuntime.js +352 -0
- package/dist/execute/types.d.ts +113 -0
- package/dist/execute/types.js +1 -0
- package/dist/execute-cli.d.ts +2 -0
- package/dist/execute-cli.js +112 -0
- package/dist/execute.d.ts +6 -0
- package/dist/execute.js +46 -0
- package/dist/graph/buildExecutionGraph.d.ts +8 -0
- package/dist/graph/buildExecutionGraph.js +69 -0
- package/dist/graph/types.d.ts +24 -0
- package/dist/graph/types.js +1 -0
- package/dist/handoff-store-cli.d.ts +2 -0
- package/dist/handoff-store-cli.js +139 -0
- package/dist/handoff-store.d.ts +103 -0
- package/dist/handoff-store.js +78 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +21 -0
- package/dist/ir/types.d.ts +92 -0
- package/dist/ir/types.js +1 -0
- package/dist/parser/parseAst.d.ts +3 -0
- package/dist/parser/parseAst.js +207 -0
- package/dist/parser/payload.d.ts +6 -0
- package/dist/parser/payload.js +211 -0
- package/dist/plan/buildExecutionPlan.d.ts +9 -0
- package/dist/plan/buildExecutionPlan.js +98 -0
- package/dist/plan/types.d.ts +44 -0
- package/dist/plan/types.js +1 -0
- package/dist/publish-cli.d.ts +2 -0
- package/dist/publish-cli.js +249 -0
- package/dist/publish.d.ts +148 -0
- package/dist/publish.js +190 -0
- package/dist/recover-cli.d.ts +2 -0
- package/dist/recover-cli.js +258 -0
- package/dist/recover.d.ts +489 -0
- package/dist/recover.js +290 -0
- package/dist/refs/resolveRefs.d.ts +13 -0
- package/dist/refs/resolveRefs.js +104 -0
- package/dist/registry/loadModuleRegistry.d.ts +28 -0
- package/dist/registry/loadModuleRegistry.js +97 -0
- package/dist/registry/types.d.ts +20 -0
- package/dist/registry/types.js +2 -0
- package/dist/resume-cli.d.ts +2 -0
- package/dist/resume-cli.js +379 -0
- package/dist/resume.d.ts +2076 -0
- package/dist/resume.js +452 -0
- package/dist/run-cli.d.ts +2 -0
- package/dist/run-cli.js +119 -0
- package/dist/run.d.ts +9 -0
- package/dist/run.js +43 -0
- package/dist/runtime-handoff-cli.d.ts +2 -0
- package/dist/runtime-handoff-cli.js +179 -0
- package/dist/runtime-handoff.d.ts +914 -0
- package/dist/runtime-handoff.js +383 -0
- package/dist/scanner/scanSource.d.ts +9 -0
- package/dist/scanner/scanSource.js +43 -0
- package/dist/schema/validateIrSchemas.d.ts +3 -0
- package/dist/schema/validateIrSchemas.js +64 -0
- package/dist/validate-module-cli.d.ts +2 -0
- package/dist/validate-module-cli.js +93 -0
- package/dist/validate-registry-cli.d.ts +2 -0
- package/dist/validate-registry-cli.js +82 -0
- package/dist/validate.d.ts +52 -0
- package/dist/validate.js +91 -0
- package/fixtures/duplicate-doc.aionis.md +19 -0
- package/fixtures/fenced-example.aionis.md +27 -0
- package/fixtures/malformed-payload.aionis.md +9 -0
- package/fixtures/npm-installed-module-registry.json +17 -0
- package/fixtures/standalone-module-registry.json +13 -0
- package/fixtures/standalone-runner-plan.json +51 -0
- package/fixtures/standalone-runner.aionis.md +29 -0
- package/fixtures/unresolved-ref.aionis.md +14 -0
- package/fixtures/valid-minimal.aionis.md +24 -0
- package/fixtures/valid-workflow.aionis.md +29 -0
- package/official-modules/copy-summary/CHANGELOG.md +6 -0
- package/official-modules/copy-summary/README.md +43 -0
- package/official-modules/copy-summary/index.mjs +41 -0
- package/official-modules/copy-summary/package.json +31 -0
- package/official-modules/json-transform/CHANGELOG.md +6 -0
- package/official-modules/json-transform/README.md +51 -0
- package/official-modules/json-transform/index.mjs +53 -0
- package/official-modules/json-transform/package.json +31 -0
- package/official-modules/research-claims/CHANGELOG.md +6 -0
- package/official-modules/research-claims/README.md +47 -0
- package/official-modules/research-claims/index.mjs +44 -0
- package/official-modules/research-claims/package.json +31 -0
- package/package.json +74 -0
package/dist/recover.js
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { AionisManifestHandoffStoreRequestSchema, } from "./handoff-store.js";
|
|
3
|
+
import { publishAionisManifestSource, publishHandoffStoreRequest, publishRuntimeHandoff, PublishInputKindSchema, PublishResultSchema, } from "./publish.js";
|
|
4
|
+
import { AionisManifestRuntimeHandoffSchema } from "./runtime-handoff.js";
|
|
5
|
+
export const AIONIS_MANIFEST_RECOVER_RESULT_VERSION = "aionis_manifest_recover_result_v1";
|
|
6
|
+
export const RecoverInputKindSchema = z.enum([
|
|
7
|
+
...PublishInputKindSchema.options,
|
|
8
|
+
"publish-result",
|
|
9
|
+
]);
|
|
10
|
+
export const HandoffRecoverRequestSchema = z.object({
|
|
11
|
+
tenant_id: z.string().min(1).optional(),
|
|
12
|
+
scope: z.string().min(1).optional(),
|
|
13
|
+
anchor: z.string().min(1),
|
|
14
|
+
repo_root: z.string().min(1).optional(),
|
|
15
|
+
file_path: z.string().min(1).optional(),
|
|
16
|
+
symbol: z.string().min(1).optional(),
|
|
17
|
+
handoff_kind: z.enum(["patch_handoff", "review_handoff", "task_handoff"]).optional(),
|
|
18
|
+
memory_lane: z.enum(["private", "shared"]).optional(),
|
|
19
|
+
limit: z.number().int().positive().optional(),
|
|
20
|
+
});
|
|
21
|
+
export const HandoffRecoverPayloadSchema = z.object({
|
|
22
|
+
tenant_id: z.string().min(1),
|
|
23
|
+
scope: z.string().min(1),
|
|
24
|
+
handoff_kind: z.string().min(1),
|
|
25
|
+
anchor: z.string().min(1),
|
|
26
|
+
matched_nodes: z.number().int().nonnegative(),
|
|
27
|
+
handoff: z.record(z.unknown()),
|
|
28
|
+
prompt_safe_handoff: z.record(z.unknown()).optional(),
|
|
29
|
+
execution_ready_handoff: z.record(z.unknown()).optional(),
|
|
30
|
+
execution_result_summary: z.record(z.unknown()).nullable().optional(),
|
|
31
|
+
execution_artifacts: z.array(z.record(z.unknown())).optional(),
|
|
32
|
+
execution_evidence: z.array(z.record(z.unknown())).optional(),
|
|
33
|
+
execution_state_v1: z.record(z.unknown()).optional(),
|
|
34
|
+
execution_packet_v1: z.record(z.unknown()).optional(),
|
|
35
|
+
control_profile_v1: z.record(z.unknown()).optional(),
|
|
36
|
+
execution_transitions_v1: z.array(z.record(z.unknown())).optional(),
|
|
37
|
+
});
|
|
38
|
+
export const RecoverResultSchema = z.object({
|
|
39
|
+
recover_result_version: z.literal(AIONIS_MANIFEST_RECOVER_RESULT_VERSION),
|
|
40
|
+
recovered_at: z.string().min(1),
|
|
41
|
+
base_url: z.string().min(1),
|
|
42
|
+
input_kind: RecoverInputKindSchema,
|
|
43
|
+
source_doc_id: z.string().min(1).nullable(),
|
|
44
|
+
source_doc_version: z.string().min(1).nullable(),
|
|
45
|
+
publish_result: PublishResultSchema.nullable(),
|
|
46
|
+
recover_request: HandoffRecoverRequestSchema,
|
|
47
|
+
recover_response: z.object({
|
|
48
|
+
status: z.number().int().nonnegative(),
|
|
49
|
+
request_id: z.string().nullable(),
|
|
50
|
+
data: HandoffRecoverPayloadSchema,
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
function joinUrl(baseUrl, path) {
|
|
54
|
+
return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
55
|
+
}
|
|
56
|
+
function withTimeout(timeoutMs) {
|
|
57
|
+
const controller = new AbortController();
|
|
58
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
59
|
+
return {
|
|
60
|
+
signal: controller.signal,
|
|
61
|
+
cancel: () => clearTimeout(timer),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async function parseResponseBody(res) {
|
|
65
|
+
const contentType = (res.headers.get("content-type") ?? "").toLowerCase();
|
|
66
|
+
if (contentType.includes("application/json"))
|
|
67
|
+
return res.json();
|
|
68
|
+
const text = await res.text();
|
|
69
|
+
return text.length > 0 ? text : null;
|
|
70
|
+
}
|
|
71
|
+
function buildHeaders(args) {
|
|
72
|
+
const headers = {
|
|
73
|
+
"content-type": "application/json",
|
|
74
|
+
};
|
|
75
|
+
if (args.apiKey)
|
|
76
|
+
headers["x-api-key"] = args.apiKey;
|
|
77
|
+
if (args.adminToken)
|
|
78
|
+
headers["x-admin-token"] = args.adminToken;
|
|
79
|
+
if (args.authBearer) {
|
|
80
|
+
headers.authorization = args.authBearer.toLowerCase().startsWith("bearer ")
|
|
81
|
+
? args.authBearer
|
|
82
|
+
: `Bearer ${args.authBearer}`;
|
|
83
|
+
}
|
|
84
|
+
if (args.requestId)
|
|
85
|
+
headers["x-request-id"] = args.requestId;
|
|
86
|
+
return headers;
|
|
87
|
+
}
|
|
88
|
+
function extractRequestId(headers) {
|
|
89
|
+
return headers.get("x-request-id") ?? headers.get("request-id") ?? null;
|
|
90
|
+
}
|
|
91
|
+
function firstString(...values) {
|
|
92
|
+
for (const value of values) {
|
|
93
|
+
if (typeof value === "string" && value.trim().length > 0)
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
function firstMemoryLane(...values) {
|
|
99
|
+
for (const value of values) {
|
|
100
|
+
if (value === "private" || value === "shared")
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
function maybeRecord(value) {
|
|
106
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
107
|
+
}
|
|
108
|
+
function buildRecoverRequestFromPublishResult(publishResult, overrides) {
|
|
109
|
+
const storeRequest = maybeRecord(publishResult.handoff_store_request);
|
|
110
|
+
return HandoffRecoverRequestSchema.parse({
|
|
111
|
+
tenant_id: overrides.tenantId ?? firstString(storeRequest?.tenant_id),
|
|
112
|
+
scope: overrides.scope ?? firstString(publishResult.response.scope, publishResult.request.scope, storeRequest?.scope),
|
|
113
|
+
anchor: firstString(publishResult.response.handoff_anchor, publishResult.request.anchor, storeRequest?.anchor),
|
|
114
|
+
repo_root: firstString(overrides.repoRoot, storeRequest?.repo_root),
|
|
115
|
+
file_path: firstString(overrides.filePath, storeRequest?.file_path),
|
|
116
|
+
symbol: firstString(overrides.symbol, storeRequest?.symbol),
|
|
117
|
+
handoff_kind: overrides.handoffKind ??
|
|
118
|
+
firstString(publishResult.response.handoff_kind, publishResult.request.handoff_kind, storeRequest?.handoff_kind),
|
|
119
|
+
memory_lane: firstMemoryLane(overrides.memoryLane, publishResult.request.memory_lane, storeRequest?.memory_lane),
|
|
120
|
+
limit: overrides.limit,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
export async function recoverPublishedAionisManifest(args) {
|
|
124
|
+
const publishResult = PublishResultSchema.parse(args.publishResult);
|
|
125
|
+
const request = buildRecoverRequestFromPublishResult(publishResult, args);
|
|
126
|
+
const timeoutMs = args.timeoutMs ?? 10_000;
|
|
127
|
+
const timeout = withTimeout(timeoutMs);
|
|
128
|
+
const url = joinUrl(args.baseUrl, "/v1/handoff/recover");
|
|
129
|
+
try {
|
|
130
|
+
const res = await fetch(url, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: buildHeaders({
|
|
133
|
+
apiKey: args.apiKey,
|
|
134
|
+
authBearer: args.authBearer,
|
|
135
|
+
adminToken: args.adminToken,
|
|
136
|
+
requestId: args.requestId,
|
|
137
|
+
}),
|
|
138
|
+
body: JSON.stringify(request),
|
|
139
|
+
signal: timeout.signal,
|
|
140
|
+
});
|
|
141
|
+
const body = await parseResponseBody(res);
|
|
142
|
+
if (!res.ok) {
|
|
143
|
+
const message = body && typeof body === "object" && "message" in body && typeof body.message === "string"
|
|
144
|
+
? String(body.message)
|
|
145
|
+
: `handoff recover failed with status ${res.status}`;
|
|
146
|
+
throw new Error(message);
|
|
147
|
+
}
|
|
148
|
+
return RecoverResultSchema.parse({
|
|
149
|
+
recover_result_version: AIONIS_MANIFEST_RECOVER_RESULT_VERSION,
|
|
150
|
+
recovered_at: args.recoveredAt ?? new Date().toISOString(),
|
|
151
|
+
base_url: args.baseUrl,
|
|
152
|
+
input_kind: args.inputKind ?? "publish-result",
|
|
153
|
+
source_doc_id: publishResult.source_doc_id,
|
|
154
|
+
source_doc_version: publishResult.source_doc_version,
|
|
155
|
+
publish_result: publishResult,
|
|
156
|
+
recover_request: request,
|
|
157
|
+
recover_response: {
|
|
158
|
+
status: res.status,
|
|
159
|
+
request_id: extractRequestId(res.headers),
|
|
160
|
+
data: HandoffRecoverPayloadSchema.parse(body),
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
timeout.cancel();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export async function recoverAionisManifestSource(args) {
|
|
169
|
+
const publishResult = await publishAionisManifestSource({
|
|
170
|
+
source: args.source,
|
|
171
|
+
inputPath: args.inputPath,
|
|
172
|
+
baseUrl: args.baseUrl,
|
|
173
|
+
scope: args.scope,
|
|
174
|
+
tenantId: args.tenantId,
|
|
175
|
+
actor: args.actor,
|
|
176
|
+
memoryLane: args.memoryLane,
|
|
177
|
+
title: args.title,
|
|
178
|
+
tags: args.tags,
|
|
179
|
+
repoRoot: args.repoRoot,
|
|
180
|
+
filePath: args.filePath,
|
|
181
|
+
symbol: args.symbol,
|
|
182
|
+
currentStage: args.currentStage,
|
|
183
|
+
activeRole: args.activeRole,
|
|
184
|
+
allowCompileErrors: args.allowCompileErrors,
|
|
185
|
+
timeoutMs: args.timeoutMs,
|
|
186
|
+
apiKey: args.apiKey,
|
|
187
|
+
authBearer: args.authBearer,
|
|
188
|
+
adminToken: args.adminToken,
|
|
189
|
+
requestId: args.requestId,
|
|
190
|
+
publishedAt: args.recoveredAt,
|
|
191
|
+
});
|
|
192
|
+
return recoverPublishedAionisManifest({
|
|
193
|
+
publishResult,
|
|
194
|
+
inputKind: "source",
|
|
195
|
+
baseUrl: args.baseUrl,
|
|
196
|
+
tenantId: args.tenantId,
|
|
197
|
+
scope: args.scope,
|
|
198
|
+
memoryLane: args.memoryLane,
|
|
199
|
+
repoRoot: args.repoRoot,
|
|
200
|
+
filePath: args.filePath,
|
|
201
|
+
symbol: args.symbol,
|
|
202
|
+
handoffKind: args.handoffKind,
|
|
203
|
+
limit: args.limit,
|
|
204
|
+
timeoutMs: args.timeoutMs,
|
|
205
|
+
apiKey: args.apiKey,
|
|
206
|
+
authBearer: args.authBearer,
|
|
207
|
+
adminToken: args.adminToken,
|
|
208
|
+
requestId: args.requestId,
|
|
209
|
+
recoveredAt: args.recoveredAt,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
export async function recoverRuntimeHandoff(args) {
|
|
213
|
+
const runtimeHandoff = AionisManifestRuntimeHandoffSchema.parse(args.runtimeHandoff);
|
|
214
|
+
const publishResult = await publishRuntimeHandoff({
|
|
215
|
+
runtimeHandoff,
|
|
216
|
+
baseUrl: args.baseUrl,
|
|
217
|
+
scope: args.scope,
|
|
218
|
+
tenantId: args.tenantId,
|
|
219
|
+
actor: args.actor,
|
|
220
|
+
memoryLane: args.memoryLane,
|
|
221
|
+
title: args.title,
|
|
222
|
+
tags: args.tags,
|
|
223
|
+
repoRoot: args.repoRoot,
|
|
224
|
+
filePath: args.filePath,
|
|
225
|
+
symbol: args.symbol,
|
|
226
|
+
currentStage: args.currentStage,
|
|
227
|
+
activeRole: args.activeRole,
|
|
228
|
+
allowCompileErrors: args.allowCompileErrors,
|
|
229
|
+
timeoutMs: args.timeoutMs,
|
|
230
|
+
apiKey: args.apiKey,
|
|
231
|
+
authBearer: args.authBearer,
|
|
232
|
+
adminToken: args.adminToken,
|
|
233
|
+
requestId: args.requestId,
|
|
234
|
+
publishedAt: args.recoveredAt,
|
|
235
|
+
});
|
|
236
|
+
return recoverPublishedAionisManifest({
|
|
237
|
+
publishResult,
|
|
238
|
+
inputKind: "runtime-handoff",
|
|
239
|
+
baseUrl: args.baseUrl,
|
|
240
|
+
tenantId: args.tenantId,
|
|
241
|
+
scope: args.scope,
|
|
242
|
+
memoryLane: args.memoryLane,
|
|
243
|
+
repoRoot: args.repoRoot,
|
|
244
|
+
filePath: args.filePath,
|
|
245
|
+
symbol: args.symbol,
|
|
246
|
+
handoffKind: args.handoffKind,
|
|
247
|
+
limit: args.limit,
|
|
248
|
+
timeoutMs: args.timeoutMs,
|
|
249
|
+
apiKey: args.apiKey,
|
|
250
|
+
authBearer: args.authBearer,
|
|
251
|
+
adminToken: args.adminToken,
|
|
252
|
+
requestId: args.requestId,
|
|
253
|
+
recoveredAt: args.recoveredAt,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
export async function recoverHandoffStoreRequest(args) {
|
|
257
|
+
const handoffStoreRequest = AionisManifestHandoffStoreRequestSchema.parse(args.handoffStoreRequest);
|
|
258
|
+
const publishResult = await publishHandoffStoreRequest({
|
|
259
|
+
baseUrl: args.baseUrl,
|
|
260
|
+
handoffStoreRequest,
|
|
261
|
+
inputKind: "handoff-store-request",
|
|
262
|
+
sourceDocId: null,
|
|
263
|
+
sourceDocVersion: null,
|
|
264
|
+
timeoutMs: args.timeoutMs,
|
|
265
|
+
apiKey: args.apiKey,
|
|
266
|
+
authBearer: args.authBearer,
|
|
267
|
+
adminToken: args.adminToken,
|
|
268
|
+
requestId: args.requestId,
|
|
269
|
+
publishedAt: args.recoveredAt,
|
|
270
|
+
});
|
|
271
|
+
return recoverPublishedAionisManifest({
|
|
272
|
+
publishResult,
|
|
273
|
+
inputKind: "handoff-store-request",
|
|
274
|
+
baseUrl: args.baseUrl,
|
|
275
|
+
tenantId: args.tenantId,
|
|
276
|
+
scope: args.scope,
|
|
277
|
+
memoryLane: args.memoryLane,
|
|
278
|
+
repoRoot: args.repoRoot,
|
|
279
|
+
filePath: args.filePath,
|
|
280
|
+
symbol: args.symbol,
|
|
281
|
+
handoffKind: args.handoffKind,
|
|
282
|
+
limit: args.limit,
|
|
283
|
+
timeoutMs: args.timeoutMs,
|
|
284
|
+
apiKey: args.apiKey,
|
|
285
|
+
authBearer: args.authBearer,
|
|
286
|
+
adminToken: args.adminToken,
|
|
287
|
+
requestId: args.requestId,
|
|
288
|
+
recoveredAt: args.recoveredAt,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Diagnostic } from "../diagnostics/types.js";
|
|
2
|
+
import type { AionisManifestIR } from "../ir/types.js";
|
|
3
|
+
export interface RefBinding {
|
|
4
|
+
raw: string;
|
|
5
|
+
scope: string;
|
|
6
|
+
path: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface RefResolutionResult {
|
|
9
|
+
ir: AionisManifestIR;
|
|
10
|
+
bindings: RefBinding[];
|
|
11
|
+
diagnostics: Diagnostic[];
|
|
12
|
+
}
|
|
13
|
+
export declare function resolveRefs(ir: AionisManifestIR): RefResolutionResult;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const VALID_SCOPES = new Set(["doc", "ctx", "run", "out", "mem"]);
|
|
2
|
+
function parseRef(ref) {
|
|
3
|
+
const match = /^([A-Za-z][A-Za-z0-9_-]*)(?:\.([A-Za-z0-9_.-]+))?$/.exec(ref.trim());
|
|
4
|
+
if (!match)
|
|
5
|
+
return null;
|
|
6
|
+
return {
|
|
7
|
+
raw: ref,
|
|
8
|
+
scope: match[1],
|
|
9
|
+
path: match[2] ? match[2].split(".") : [],
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function isObject(value) {
|
|
13
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
function getPathValue(source, path) {
|
|
16
|
+
let current = source;
|
|
17
|
+
for (const segment of path) {
|
|
18
|
+
if (!isObject(current) || !(segment in current))
|
|
19
|
+
return undefined;
|
|
20
|
+
current = current[segment];
|
|
21
|
+
}
|
|
22
|
+
return current;
|
|
23
|
+
}
|
|
24
|
+
function buildMergedContext(context) {
|
|
25
|
+
const merged = {};
|
|
26
|
+
for (const entry of context) {
|
|
27
|
+
Object.assign(merged, entry.data);
|
|
28
|
+
}
|
|
29
|
+
return merged;
|
|
30
|
+
}
|
|
31
|
+
function validateBinding(ir, binding, execution) {
|
|
32
|
+
if (!VALID_SCOPES.has(binding.scope)) {
|
|
33
|
+
return {
|
|
34
|
+
severity: "error",
|
|
35
|
+
code: "UNRESOLVED_REF",
|
|
36
|
+
message: `Unknown ref scope '${binding.scope}' in '${binding.raw}'.`,
|
|
37
|
+
loc: execution.loc ?? ir.doc?.loc ?? {
|
|
38
|
+
start: { line: 1, column: 1, offset: 0 },
|
|
39
|
+
end: { line: 1, column: 1, offset: 0 },
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (binding.scope === "ctx") {
|
|
44
|
+
const merged = buildMergedContext(ir.context);
|
|
45
|
+
const value = binding.path.length === 0 ? merged : getPathValue(merged, binding.path);
|
|
46
|
+
if (value === undefined) {
|
|
47
|
+
return {
|
|
48
|
+
severity: "error",
|
|
49
|
+
code: "UNRESOLVED_REF",
|
|
50
|
+
message: `Context ref '${binding.raw}' could not be resolved.`,
|
|
51
|
+
loc: execution.loc ?? ir.doc?.loc ?? {
|
|
52
|
+
start: { line: 1, column: 1, offset: 0 },
|
|
53
|
+
end: { line: 1, column: 1, offset: 0 },
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (binding.scope === "doc") {
|
|
59
|
+
const docValue = ir.doc?.data;
|
|
60
|
+
const value = binding.path.length === 0 ? docValue : getPathValue(docValue, binding.path);
|
|
61
|
+
if (value === undefined) {
|
|
62
|
+
return {
|
|
63
|
+
severity: "error",
|
|
64
|
+
code: "UNRESOLVED_REF",
|
|
65
|
+
message: `Document ref '${binding.raw}' could not be resolved.`,
|
|
66
|
+
loc: execution.loc ?? ir.doc?.loc ?? {
|
|
67
|
+
start: { line: 1, column: 1, offset: 0 },
|
|
68
|
+
end: { line: 1, column: 1, offset: 0 },
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
export function resolveRefs(ir) {
|
|
76
|
+
const bindings = [];
|
|
77
|
+
const diagnostics = [];
|
|
78
|
+
for (const execution of ir.executions) {
|
|
79
|
+
if (!execution.input_ref)
|
|
80
|
+
continue;
|
|
81
|
+
const binding = parseRef(execution.input_ref);
|
|
82
|
+
if (!binding) {
|
|
83
|
+
diagnostics.push({
|
|
84
|
+
severity: "error",
|
|
85
|
+
code: "UNRESOLVED_REF",
|
|
86
|
+
message: `Invalid ref syntax '${execution.input_ref}'.`,
|
|
87
|
+
loc: execution.loc ?? ir.doc?.loc ?? {
|
|
88
|
+
start: { line: 1, column: 1, offset: 0 },
|
|
89
|
+
end: { line: 1, column: 1, offset: 0 },
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
bindings.push(binding);
|
|
95
|
+
const diagnostic = validateBinding(ir, binding, execution);
|
|
96
|
+
if (diagnostic)
|
|
97
|
+
diagnostics.push(diagnostic);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
ir,
|
|
101
|
+
bindings,
|
|
102
|
+
diagnostics,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ModuleRegistryExecutionRuntime, StaticModuleRegistry } from "../execute/moduleRuntime.js";
|
|
2
|
+
import type { ExecutionModuleDefinition } from "../execute/types.js";
|
|
3
|
+
import type { AnyModuleRegistryFileV1, ModuleRegistryFileEntryV1, NpmModuleRegistryFileEntryV1 } from "./types.js";
|
|
4
|
+
export declare function loadModuleDefinitionFromFileEntry(args: {
|
|
5
|
+
registryDir: string;
|
|
6
|
+
entry: Pick<ModuleRegistryFileEntryV1, "entry"> & {
|
|
7
|
+
module?: string;
|
|
8
|
+
};
|
|
9
|
+
}): Promise<{
|
|
10
|
+
definition: ExecutionModuleDefinition;
|
|
11
|
+
resolvedEntryPath: string;
|
|
12
|
+
}>;
|
|
13
|
+
export declare function loadModuleDefinitionFromNpmEntry(args: {
|
|
14
|
+
registryDir: string;
|
|
15
|
+
entry: Pick<NpmModuleRegistryFileEntryV1, "package" | "export"> & {
|
|
16
|
+
module?: string;
|
|
17
|
+
};
|
|
18
|
+
}): Promise<{
|
|
19
|
+
definition: ExecutionModuleDefinition;
|
|
20
|
+
resolvedEntryPath: string;
|
|
21
|
+
specifier: string;
|
|
22
|
+
}>;
|
|
23
|
+
export declare function loadModuleRegistryFile(registryPath: string): Promise<AnyModuleRegistryFileV1>;
|
|
24
|
+
export declare function loadModuleRegistry(registryPath: string): Promise<StaticModuleRegistry>;
|
|
25
|
+
export declare function createModuleRegistryRuntimeFromFile(args: {
|
|
26
|
+
registryPath: string;
|
|
27
|
+
runtimeId?: string;
|
|
28
|
+
}): Promise<ModuleRegistryExecutionRuntime>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { AnyModuleRegistryFileSchema, validateExecutionModuleManifest } from "../contracts.js";
|
|
6
|
+
import { ModuleRegistryExecutionRuntime, StaticModuleRegistry } from "../execute/moduleRuntime.js";
|
|
7
|
+
import { AIONIS_MANIFEST_MODULE_REGISTRY_VERSION } from "./types.js";
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function asManifest(value, entryPath) {
|
|
12
|
+
if (!isRecord(value)) {
|
|
13
|
+
throw new Error(`Registry module '${entryPath}' does not export a valid manifest object.`);
|
|
14
|
+
}
|
|
15
|
+
validateExecutionModuleManifest(value);
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
function asHandler(value, entryPath) {
|
|
19
|
+
if (typeof value !== "function") {
|
|
20
|
+
throw new Error(`Registry module '${entryPath}' does not export a valid handler function.`);
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
function asDefinition(loaded, declaredModule, entryPath) {
|
|
25
|
+
if (!isRecord(loaded)) {
|
|
26
|
+
throw new Error(`Registry entry '${entryPath}' did not load a module object.`);
|
|
27
|
+
}
|
|
28
|
+
const manifest = asManifest(loaded.manifest, entryPath);
|
|
29
|
+
const handler = asHandler(loaded.handler, entryPath);
|
|
30
|
+
if (declaredModule && manifest.module !== declaredModule) {
|
|
31
|
+
throw new Error(`Registry entry '${entryPath}' declares module '${declaredModule}' but exports manifest '${manifest.module}'.`);
|
|
32
|
+
}
|
|
33
|
+
return { manifest, handler };
|
|
34
|
+
}
|
|
35
|
+
function toNpmSpecifier(packageName, exportPath) {
|
|
36
|
+
if (!exportPath || exportPath === ".")
|
|
37
|
+
return packageName;
|
|
38
|
+
if (!exportPath.startsWith("./")) {
|
|
39
|
+
throw new Error(`NPM registry export '${exportPath}' must be '.' or start with './'.`);
|
|
40
|
+
}
|
|
41
|
+
return `${packageName}/${exportPath.slice(2)}`;
|
|
42
|
+
}
|
|
43
|
+
export async function loadModuleDefinitionFromFileEntry(args) {
|
|
44
|
+
const resolvedEntryPath = path.resolve(args.registryDir, args.entry.entry);
|
|
45
|
+
const loaded = await import(pathToFileURL(resolvedEntryPath).href);
|
|
46
|
+
return {
|
|
47
|
+
definition: asDefinition(loaded, args.entry.module, resolvedEntryPath),
|
|
48
|
+
resolvedEntryPath,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export async function loadModuleDefinitionFromNpmEntry(args) {
|
|
52
|
+
const requireFromRegistry = createRequire(path.join(args.registryDir, "__aionis_manifest_registry__.cjs"));
|
|
53
|
+
const specifier = toNpmSpecifier(args.entry.package, args.entry.export);
|
|
54
|
+
const resolvedEntryPath = requireFromRegistry.resolve(specifier);
|
|
55
|
+
const loaded = await import(pathToFileURL(resolvedEntryPath).href);
|
|
56
|
+
return {
|
|
57
|
+
definition: asDefinition(loaded, args.entry.module, resolvedEntryPath),
|
|
58
|
+
resolvedEntryPath,
|
|
59
|
+
specifier,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async function loadDefinitionsFromFileRegistry(registryPath, registry) {
|
|
63
|
+
const registryDir = path.dirname(registryPath);
|
|
64
|
+
return Promise.all(registry.modules.map(async (entry) => {
|
|
65
|
+
const loaded = await loadModuleDefinitionFromFileEntry({ registryDir, entry });
|
|
66
|
+
return loaded.definition;
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
async function loadDefinitionsFromNpmRegistry(registryPath, registry) {
|
|
70
|
+
const registryDir = path.dirname(registryPath);
|
|
71
|
+
return Promise.all(registry.modules.map(async (entry) => {
|
|
72
|
+
const loaded = await loadModuleDefinitionFromNpmEntry({ registryDir, entry });
|
|
73
|
+
return loaded.definition;
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
export async function loadModuleRegistryFile(registryPath) {
|
|
77
|
+
const raw = await readFile(registryPath, "utf8");
|
|
78
|
+
return AnyModuleRegistryFileSchema.parse(JSON.parse(raw));
|
|
79
|
+
}
|
|
80
|
+
export async function loadModuleRegistry(registryPath) {
|
|
81
|
+
const registry = await loadModuleRegistryFile(registryPath);
|
|
82
|
+
const definitions = registry.version === AIONIS_MANIFEST_MODULE_REGISTRY_VERSION
|
|
83
|
+
? await loadDefinitionsFromFileRegistry(registryPath, registry)
|
|
84
|
+
: await loadDefinitionsFromNpmRegistry(registryPath, registry);
|
|
85
|
+
return new StaticModuleRegistry(definitions);
|
|
86
|
+
}
|
|
87
|
+
export async function createModuleRegistryRuntimeFromFile(args) {
|
|
88
|
+
const registryFile = await loadModuleRegistryFile(args.registryPath);
|
|
89
|
+
const registry = await loadModuleRegistry(args.registryPath);
|
|
90
|
+
return new ModuleRegistryExecutionRuntime({
|
|
91
|
+
runtime_id: args.runtimeId ??
|
|
92
|
+
(registryFile.version === AIONIS_MANIFEST_MODULE_REGISTRY_VERSION
|
|
93
|
+
? "standalone_file_registry_v1"
|
|
94
|
+
: "standalone_npm_registry_v1"),
|
|
95
|
+
registry,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare const AIONIS_MANIFEST_MODULE_REGISTRY_VERSION: "aionis_manifest_module_registry_v1";
|
|
2
|
+
export declare const AIONIS_MANIFEST_NPM_MODULE_REGISTRY_VERSION: "aionis_manifest_npm_module_registry_v1";
|
|
3
|
+
export interface ModuleRegistryFileEntryV1 {
|
|
4
|
+
module: string;
|
|
5
|
+
entry: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ModuleRegistryFileV1 {
|
|
8
|
+
version: typeof AIONIS_MANIFEST_MODULE_REGISTRY_VERSION;
|
|
9
|
+
modules: ModuleRegistryFileEntryV1[];
|
|
10
|
+
}
|
|
11
|
+
export interface NpmModuleRegistryFileEntryV1 {
|
|
12
|
+
module: string;
|
|
13
|
+
package: string;
|
|
14
|
+
export?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface NpmModuleRegistryFileV1 {
|
|
17
|
+
version: typeof AIONIS_MANIFEST_NPM_MODULE_REGISTRY_VERSION;
|
|
18
|
+
modules: NpmModuleRegistryFileEntryV1[];
|
|
19
|
+
}
|
|
20
|
+
export type AnyModuleRegistryFileV1 = ModuleRegistryFileV1 | NpmModuleRegistryFileV1;
|