sdn-flow 0.2.0
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/.claude/SKILLS.md +7 -0
- package/.claude/skills/sdn-plugin-abi-compliance/SKILL.md +56 -0
- package/.claude/todo/001-js-host-startup-and-deno.md +85 -0
- package/LICENSE +21 -0
- package/README.md +223 -0
- package/bin/sdn-flow-host.js +169 -0
- package/docs/.nojekyll +0 -0
- package/docs/ARCHITECTURE.md +200 -0
- package/docs/HOST_CAPABILITY_MODEL.md +317 -0
- package/docs/PLUGIN_ARCHITECTURE.md +145 -0
- package/docs/PLUGIN_COMPATIBILITY.md +61 -0
- package/docs/PLUGIN_COMPLIANCE_CHECKS.md +82 -0
- package/docs/PLUGIN_MANIFEST.md +94 -0
- package/docs/css/style.css +465 -0
- package/docs/index.html +218 -0
- package/docs/js/app.mjs +751 -0
- package/docs/js/editor-panel.mjs +203 -0
- package/docs/js/flow-canvas.mjs +515 -0
- package/docs/js/flow-model.mjs +391 -0
- package/docs/js/workers/emception.worker.js +146 -0
- package/docs/js/workers/pyodide.worker.js +134 -0
- package/native/flow_source_generator.cpp +1958 -0
- package/package.json +67 -0
- package/schemas/FlowRuntimeAbi.fbs +91 -0
- package/src/auth/canonicalize.js +5 -0
- package/src/auth/index.js +11 -0
- package/src/auth/permissions.js +8 -0
- package/src/compiler/CppFlowSourceGenerator.js +475 -0
- package/src/compiler/EmceptionCompilerAdapter.js +244 -0
- package/src/compiler/SignedArtifactCatalog.js +152 -0
- package/src/compiler/index.js +8 -0
- package/src/compiler/nativeFlowSourceGeneratorTool.js +144 -0
- package/src/compliance/index.js +13 -0
- package/src/compliance/pluginCompliance.js +11 -0
- package/src/deploy/FlowDeploymentClient.js +532 -0
- package/src/deploy/index.js +8 -0
- package/src/designer/FlowDesignerSession.js +158 -0
- package/src/designer/index.js +2 -0
- package/src/designer/requirements.js +184 -0
- package/src/generated/runtimeAbiLayouts.js +544 -0
- package/src/host/appHost.js +105 -0
- package/src/host/autoHost.js +113 -0
- package/src/host/browserHostAdapters.js +108 -0
- package/src/host/compiledFlowRuntimeHost.js +703 -0
- package/src/host/constants.js +55 -0
- package/src/host/dependencyRuntime.js +227 -0
- package/src/host/descriptorAbi.js +351 -0
- package/src/host/fetchService.js +237 -0
- package/src/host/httpHostAdapters.js +280 -0
- package/src/host/index.js +91 -0
- package/src/host/installedFlowHost.js +885 -0
- package/src/host/invocationAbi.js +440 -0
- package/src/host/normalize.js +372 -0
- package/src/host/packageManagers.js +369 -0
- package/src/host/profile.js +134 -0
- package/src/host/runtimeAbi.js +106 -0
- package/src/host/workspace.js +895 -0
- package/src/index.js +8 -0
- package/src/runtime/FlowRuntime.js +273 -0
- package/src/runtime/MethodRegistry.js +295 -0
- package/src/runtime/constants.js +44 -0
- package/src/runtime/index.js +19 -0
- package/src/runtime/normalize.js +377 -0
- package/src/transport/index.js +7 -0
- package/src/transport/pki.js +7 -0
- package/src/utils/crypto.js +7 -0
- package/src/utils/encoding.js +65 -0
- package/src/utils/wasmCrypto.js +69 -0
- package/tools/run-plugin-compliance-check.mjs +153 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
function normalizeString(value, fallback = null) {
|
|
5
|
+
if (typeof value !== "string") {
|
|
6
|
+
return fallback;
|
|
7
|
+
}
|
|
8
|
+
const normalized = value.trim();
|
|
9
|
+
return normalized.length > 0 ? normalized : fallback;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function isObject(value) {
|
|
13
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function normalizeCommandArgs(args) {
|
|
17
|
+
if (!Array.isArray(args)) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
return args
|
|
21
|
+
.map((value) => {
|
|
22
|
+
if (value === undefined || value === null) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return String(value);
|
|
26
|
+
})
|
|
27
|
+
.filter((value) => value !== null);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function normalizeCommandSpec(commandSpec, defaults = {}) {
|
|
31
|
+
const input =
|
|
32
|
+
typeof commandSpec === "string"
|
|
33
|
+
? { command: commandSpec }
|
|
34
|
+
: isObject(commandSpec)
|
|
35
|
+
? commandSpec
|
|
36
|
+
: null;
|
|
37
|
+
const command = normalizeString(input?.command ?? input?.cmd, null);
|
|
38
|
+
if (!command) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
"Command-backed package-manager operations require a command string.",
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
command,
|
|
45
|
+
args: normalizeCommandArgs(input.args),
|
|
46
|
+
cwd: path.resolve(
|
|
47
|
+
normalizeString(input.cwd ?? defaults.cwd, null) ?? process.cwd(),
|
|
48
|
+
),
|
|
49
|
+
env: isObject(input.env)
|
|
50
|
+
? {
|
|
51
|
+
...(isObject(defaults.env) ? defaults.env : process.env),
|
|
52
|
+
...input.env,
|
|
53
|
+
}
|
|
54
|
+
: defaults.env,
|
|
55
|
+
input:
|
|
56
|
+
typeof input.input === "string" || input.input instanceof Uint8Array
|
|
57
|
+
? input.input
|
|
58
|
+
: null,
|
|
59
|
+
allowedExitCodes: Array.isArray(input.allowedExitCodes)
|
|
60
|
+
? input.allowedExitCodes
|
|
61
|
+
: Array.isArray(defaults.allowedExitCodes)
|
|
62
|
+
? defaults.allowedExitCodes
|
|
63
|
+
: [0],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function createCommandError(commandResult) {
|
|
68
|
+
const commandLine = [commandResult.command, ...commandResult.args].join(" ");
|
|
69
|
+
const stderr =
|
|
70
|
+
normalizeString(commandResult.stderr, null) ?? "Command exited without stderr.";
|
|
71
|
+
return new Error(
|
|
72
|
+
`Package-manager command failed (${commandResult.exitCode}): ${commandLine}\n${stderr}`,
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function formatPackageInstallSpecifier(packageReference = {}) {
|
|
77
|
+
const sourceRef = normalizeString(packageReference.sourceRef, null);
|
|
78
|
+
if (sourceRef) {
|
|
79
|
+
return sourceRef;
|
|
80
|
+
}
|
|
81
|
+
const packageId = normalizeString(packageReference.packageId, null);
|
|
82
|
+
if (!packageId) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
"Package references require packageId or sourceRef for installation.",
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const version = normalizeString(packageReference.version, null);
|
|
88
|
+
return version ? `${packageId}@${version}` : packageId;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function formatPackageUpdateSpecifier(packageReference = {}) {
|
|
92
|
+
const sourceRef = normalizeString(packageReference.sourceRef, null);
|
|
93
|
+
if (sourceRef) {
|
|
94
|
+
return sourceRef;
|
|
95
|
+
}
|
|
96
|
+
const packageId = normalizeString(packageReference.packageId, null);
|
|
97
|
+
if (!packageId) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
"Package references require packageId or sourceRef for updates.",
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
return packageId;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function resolvePackageReferenceCwd(packageReference, workspace, options = {}) {
|
|
106
|
+
return path.resolve(
|
|
107
|
+
normalizeString(
|
|
108
|
+
options.cwd ??
|
|
109
|
+
options.baseDirectory ??
|
|
110
|
+
workspace?.baseDirectory ??
|
|
111
|
+
process.cwd(),
|
|
112
|
+
process.cwd(),
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function resolveNodeModulesInstallPath(installRoot, packageId) {
|
|
118
|
+
const normalizedPackageId = normalizeString(packageId, null);
|
|
119
|
+
if (!normalizedPackageId) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
return path.resolve(installRoot, "node_modules", ...normalizedPackageId.split("/"));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function createNodeCommandRunner(defaultOptions = {}) {
|
|
126
|
+
return async function runCommand(commandSpec, context = {}) {
|
|
127
|
+
const normalizedCommand = normalizeCommandSpec(commandSpec, defaultOptions);
|
|
128
|
+
const {
|
|
129
|
+
command,
|
|
130
|
+
args,
|
|
131
|
+
cwd,
|
|
132
|
+
env,
|
|
133
|
+
input,
|
|
134
|
+
allowedExitCodes,
|
|
135
|
+
} = normalizedCommand;
|
|
136
|
+
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
const child = spawn(command, args, {
|
|
139
|
+
cwd,
|
|
140
|
+
env,
|
|
141
|
+
stdio: "pipe",
|
|
142
|
+
});
|
|
143
|
+
let stdout = "";
|
|
144
|
+
let stderr = "";
|
|
145
|
+
|
|
146
|
+
child.stdout.on("data", (chunk) => {
|
|
147
|
+
stdout += chunk.toString();
|
|
148
|
+
});
|
|
149
|
+
child.stderr.on("data", (chunk) => {
|
|
150
|
+
stderr += chunk.toString();
|
|
151
|
+
});
|
|
152
|
+
child.on("error", reject);
|
|
153
|
+
child.on("close", (exitCode) => {
|
|
154
|
+
const commandResult = {
|
|
155
|
+
phase: context.phase ?? null,
|
|
156
|
+
command,
|
|
157
|
+
args,
|
|
158
|
+
cwd,
|
|
159
|
+
exitCode: Number.isInteger(exitCode) ? exitCode : -1,
|
|
160
|
+
stdout,
|
|
161
|
+
stderr,
|
|
162
|
+
};
|
|
163
|
+
if (!allowedExitCodes.includes(commandResult.exitCode)) {
|
|
164
|
+
reject(createCommandError(commandResult));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
resolve(commandResult);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (input instanceof Uint8Array || typeof input === "string") {
|
|
171
|
+
child.stdin.write(input);
|
|
172
|
+
}
|
|
173
|
+
child.stdin.end();
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function createCommandPackageManager(options = {}) {
|
|
179
|
+
const runCommand =
|
|
180
|
+
typeof options.runCommand === "function"
|
|
181
|
+
? options.runCommand
|
|
182
|
+
: createNodeCommandRunner(options.runnerOptions);
|
|
183
|
+
const installCommand =
|
|
184
|
+
typeof options.installCommand === "function"
|
|
185
|
+
? options.installCommand
|
|
186
|
+
: null;
|
|
187
|
+
const updateCommand =
|
|
188
|
+
typeof options.updateCommand === "function"
|
|
189
|
+
? options.updateCommand
|
|
190
|
+
: null;
|
|
191
|
+
const removeCommand =
|
|
192
|
+
typeof options.removeCommand === "function"
|
|
193
|
+
? options.removeCommand
|
|
194
|
+
: null;
|
|
195
|
+
const resolveInstallRecord =
|
|
196
|
+
typeof options.resolveInstallRecord === "function"
|
|
197
|
+
? options.resolveInstallRecord
|
|
198
|
+
: null;
|
|
199
|
+
|
|
200
|
+
if (!installCommand || !updateCommand || !resolveInstallRecord) {
|
|
201
|
+
throw new Error(
|
|
202
|
+
"Command-backed package managers require installCommand, updateCommand, and resolveInstallRecord callbacks.",
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function executePhase(phase, packageReference, workspace, operationOptions) {
|
|
207
|
+
const commandFactory =
|
|
208
|
+
phase === "install"
|
|
209
|
+
? installCommand
|
|
210
|
+
: phase === "update"
|
|
211
|
+
? updateCommand
|
|
212
|
+
: removeCommand;
|
|
213
|
+
if (typeof commandFactory !== "function") {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
const commandSpec = commandFactory(
|
|
217
|
+
packageReference,
|
|
218
|
+
workspace,
|
|
219
|
+
operationOptions,
|
|
220
|
+
);
|
|
221
|
+
return runCommand(commandSpec, {
|
|
222
|
+
phase,
|
|
223
|
+
packageReference,
|
|
224
|
+
workspace,
|
|
225
|
+
options: operationOptions,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
async install(packageReference, workspace, operationOptions = {}) {
|
|
231
|
+
const commandResult = await executePhase(
|
|
232
|
+
"install",
|
|
233
|
+
packageReference,
|
|
234
|
+
workspace,
|
|
235
|
+
operationOptions,
|
|
236
|
+
);
|
|
237
|
+
return resolveInstallRecord({
|
|
238
|
+
phase: "install",
|
|
239
|
+
packageReference,
|
|
240
|
+
workspace,
|
|
241
|
+
commandResult,
|
|
242
|
+
options: operationOptions,
|
|
243
|
+
});
|
|
244
|
+
},
|
|
245
|
+
async update(packageReference, workspace, operationOptions = {}) {
|
|
246
|
+
const commandResult = await executePhase(
|
|
247
|
+
"update",
|
|
248
|
+
packageReference,
|
|
249
|
+
workspace,
|
|
250
|
+
operationOptions,
|
|
251
|
+
);
|
|
252
|
+
return resolveInstallRecord({
|
|
253
|
+
phase: "update",
|
|
254
|
+
packageReference,
|
|
255
|
+
workspace,
|
|
256
|
+
commandResult,
|
|
257
|
+
options: operationOptions,
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
async remove(packageReference, workspace, operationOptions = {}) {
|
|
261
|
+
return executePhase(
|
|
262
|
+
"remove",
|
|
263
|
+
packageReference,
|
|
264
|
+
workspace,
|
|
265
|
+
operationOptions,
|
|
266
|
+
);
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function createNpmPackageManager(options = {}) {
|
|
272
|
+
const packageManagerCommand = normalizeString(
|
|
273
|
+
options.packageManagerCommand ?? options.command,
|
|
274
|
+
"npm",
|
|
275
|
+
);
|
|
276
|
+
const resolveCwd =
|
|
277
|
+
typeof options.resolveCwd === "function"
|
|
278
|
+
? options.resolveCwd
|
|
279
|
+
: (packageReference, workspace) =>
|
|
280
|
+
resolvePackageReferenceCwd(packageReference, workspace, options);
|
|
281
|
+
const resolvePluginPackage =
|
|
282
|
+
typeof options.resolvePluginPackage === "function"
|
|
283
|
+
? options.resolvePluginPackage
|
|
284
|
+
: null;
|
|
285
|
+
|
|
286
|
+
return createCommandPackageManager({
|
|
287
|
+
...options,
|
|
288
|
+
installCommand(packageReference, workspace, operationOptions = {}) {
|
|
289
|
+
const cwd = resolveCwd(packageReference, workspace, operationOptions);
|
|
290
|
+
return {
|
|
291
|
+
command: packageManagerCommand,
|
|
292
|
+
args: [
|
|
293
|
+
"install",
|
|
294
|
+
"--no-save",
|
|
295
|
+
formatPackageInstallSpecifier(packageReference),
|
|
296
|
+
],
|
|
297
|
+
cwd,
|
|
298
|
+
};
|
|
299
|
+
},
|
|
300
|
+
updateCommand(packageReference, workspace, operationOptions = {}) {
|
|
301
|
+
const cwd = resolveCwd(packageReference, workspace, operationOptions);
|
|
302
|
+
return {
|
|
303
|
+
command: packageManagerCommand,
|
|
304
|
+
args: [
|
|
305
|
+
"install",
|
|
306
|
+
"--no-save",
|
|
307
|
+
formatPackageUpdateSpecifier(packageReference),
|
|
308
|
+
],
|
|
309
|
+
cwd,
|
|
310
|
+
};
|
|
311
|
+
},
|
|
312
|
+
removeCommand(packageReference, workspace, operationOptions = {}) {
|
|
313
|
+
const cwd = resolveCwd(packageReference, workspace, operationOptions);
|
|
314
|
+
const packageId = normalizeString(packageReference.packageId, null);
|
|
315
|
+
if (!packageId) {
|
|
316
|
+
throw new Error(
|
|
317
|
+
"npm package-manager removal requires packageId on the workspace package reference.",
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
command: packageManagerCommand,
|
|
322
|
+
args: ["uninstall", "--no-save", packageId],
|
|
323
|
+
cwd,
|
|
324
|
+
};
|
|
325
|
+
},
|
|
326
|
+
async resolveInstallRecord({
|
|
327
|
+
packageReference,
|
|
328
|
+
workspace,
|
|
329
|
+
commandResult,
|
|
330
|
+
options: operationOptions = {},
|
|
331
|
+
}) {
|
|
332
|
+
const cwd = resolveCwd(packageReference, workspace, operationOptions);
|
|
333
|
+
const explicitSourceRef = normalizeString(packageReference.sourceRef, null);
|
|
334
|
+
const installPath =
|
|
335
|
+
normalizeString(packageReference.installPath, null) ??
|
|
336
|
+
resolveNodeModulesInstallPath(cwd, packageReference.packageId);
|
|
337
|
+
const pluginPackage = resolvePluginPackage
|
|
338
|
+
? await resolvePluginPackage(
|
|
339
|
+
packageReference,
|
|
340
|
+
workspace,
|
|
341
|
+
commandResult,
|
|
342
|
+
operationOptions,
|
|
343
|
+
)
|
|
344
|
+
: undefined;
|
|
345
|
+
return {
|
|
346
|
+
packageId: normalizeString(packageReference.packageId, null),
|
|
347
|
+
pluginId: normalizeString(packageReference.pluginId, null),
|
|
348
|
+
version: normalizeString(packageReference.version, null),
|
|
349
|
+
sourceType:
|
|
350
|
+
normalizeString(packageReference.sourceType, null) ?? "npm",
|
|
351
|
+
sourceRef: explicitSourceRef,
|
|
352
|
+
installPath,
|
|
353
|
+
metadata: {
|
|
354
|
+
packageManager: packageManagerCommand,
|
|
355
|
+
requestedSpecifier:
|
|
356
|
+
explicitSourceRef ?? formatPackageInstallSpecifier(packageReference),
|
|
357
|
+
...(isObject(packageReference.metadata) ? packageReference.metadata : {}),
|
|
358
|
+
},
|
|
359
|
+
...(pluginPackage ? { pluginPackage } : {}),
|
|
360
|
+
};
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export default {
|
|
366
|
+
createCommandPackageManager,
|
|
367
|
+
createNodeCommandRunner,
|
|
368
|
+
createNpmPackageManager,
|
|
369
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { RecommendedCapabilityIds } from "../compliance/index.js";
|
|
2
|
+
import { HostedRuntimeAdapter, HostedRuntimeEngine } from "./constants.js";
|
|
3
|
+
|
|
4
|
+
function normalizeString(value, fallback = null) {
|
|
5
|
+
if (typeof value !== "string") {
|
|
6
|
+
return fallback;
|
|
7
|
+
}
|
|
8
|
+
const normalized = value.trim().toLowerCase();
|
|
9
|
+
return normalized.length > 0 ? normalized : fallback;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function normalizeStringArray(values) {
|
|
13
|
+
if (!Array.isArray(values)) {
|
|
14
|
+
return [];
|
|
15
|
+
}
|
|
16
|
+
return values
|
|
17
|
+
.map((value) => normalizeString(value, null))
|
|
18
|
+
.filter((value) => value !== null);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const AllKnownCapabilities = Object.freeze(
|
|
22
|
+
Array.from(new Set(RecommendedCapabilityIds)).sort(),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const BrowserCapabilityProfile = Object.freeze([
|
|
26
|
+
"clock",
|
|
27
|
+
"random",
|
|
28
|
+
"logging",
|
|
29
|
+
"timers",
|
|
30
|
+
"schedule_cron",
|
|
31
|
+
"http",
|
|
32
|
+
"websocket",
|
|
33
|
+
"pubsub",
|
|
34
|
+
"protocol_dial",
|
|
35
|
+
"storage_query",
|
|
36
|
+
"context_read",
|
|
37
|
+
"context_write",
|
|
38
|
+
"crypto_hash",
|
|
39
|
+
"crypto_sign",
|
|
40
|
+
"crypto_verify",
|
|
41
|
+
"crypto_encrypt",
|
|
42
|
+
"crypto_decrypt",
|
|
43
|
+
"crypto_key_agreement",
|
|
44
|
+
"crypto_kdf",
|
|
45
|
+
"scene_access",
|
|
46
|
+
"entity_access",
|
|
47
|
+
"render_hooks",
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
const WasiCapabilityProfile = Object.freeze([
|
|
51
|
+
"clock",
|
|
52
|
+
"random",
|
|
53
|
+
"pipe",
|
|
54
|
+
"filesystem",
|
|
55
|
+
"storage_query",
|
|
56
|
+
"context_read",
|
|
57
|
+
"crypto_hash",
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
const EngineCapabilityProfiles = Object.freeze({
|
|
61
|
+
[HostedRuntimeEngine.NODE]: AllKnownCapabilities,
|
|
62
|
+
[HostedRuntimeEngine.DENO]: AllKnownCapabilities,
|
|
63
|
+
[HostedRuntimeEngine.BUN]: AllKnownCapabilities,
|
|
64
|
+
[HostedRuntimeEngine.BROWSER]: BrowserCapabilityProfile,
|
|
65
|
+
[HostedRuntimeEngine.WASI]: WasiCapabilityProfile,
|
|
66
|
+
[HostedRuntimeEngine.GO]: AllKnownCapabilities,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export function normalizeHostedRuntimeEngine(value, fallback = null) {
|
|
70
|
+
const normalized = normalizeString(value, null);
|
|
71
|
+
if (!normalized) {
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
|
74
|
+
return Object.values(HostedRuntimeEngine).includes(normalized)
|
|
75
|
+
? normalized
|
|
76
|
+
: fallback;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function listHostedRuntimeCapabilities({ adapter = null, engine = null } = {}) {
|
|
80
|
+
const normalizedAdapter = normalizeString(adapter, null);
|
|
81
|
+
const normalizedEngine = normalizeHostedRuntimeEngine(engine, null);
|
|
82
|
+
|
|
83
|
+
if (normalizedAdapter === HostedRuntimeAdapter.GO_SDN) {
|
|
84
|
+
return [...AllKnownCapabilities];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
normalizedAdapter === HostedRuntimeAdapter.SDN_JS ||
|
|
89
|
+
normalizedEngine !== null
|
|
90
|
+
) {
|
|
91
|
+
return [
|
|
92
|
+
...(
|
|
93
|
+
EngineCapabilityProfiles[
|
|
94
|
+
normalizedEngine ?? HostedRuntimeEngine.NODE
|
|
95
|
+
] ?? AllKnownCapabilities
|
|
96
|
+
),
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return [...AllKnownCapabilities];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function evaluateHostedCapabilitySupport({
|
|
104
|
+
adapter = null,
|
|
105
|
+
engine = null,
|
|
106
|
+
requiredCapabilities = [],
|
|
107
|
+
} = {}) {
|
|
108
|
+
const supportedCapabilities = listHostedRuntimeCapabilities({
|
|
109
|
+
adapter,
|
|
110
|
+
engine,
|
|
111
|
+
});
|
|
112
|
+
const supportedSet = new Set(supportedCapabilities);
|
|
113
|
+
const normalizedRequired = Array.from(
|
|
114
|
+
new Set(normalizeStringArray(requiredCapabilities)),
|
|
115
|
+
).sort();
|
|
116
|
+
const unsupportedCapabilities = normalizedRequired.filter(
|
|
117
|
+
(capability) => !supportedSet.has(capability),
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
adapter: normalizeString(adapter, null),
|
|
122
|
+
engine: normalizeHostedRuntimeEngine(engine, null),
|
|
123
|
+
supportedCapabilities,
|
|
124
|
+
requiredCapabilities: normalizedRequired,
|
|
125
|
+
unsupportedCapabilities,
|
|
126
|
+
ok: unsupportedCapabilities.length === 0,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export default {
|
|
131
|
+
evaluateHostedCapabilitySupport,
|
|
132
|
+
listHostedRuntimeCapabilities,
|
|
133
|
+
normalizeHostedRuntimeEngine,
|
|
134
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { normalizeCompiledArtifact } from "../deploy/FlowDeploymentClient.js";
|
|
2
|
+
|
|
3
|
+
export const DefaultRequiredRuntimeExportRoles = Object.freeze([
|
|
4
|
+
"descriptorSymbol",
|
|
5
|
+
"resetStateSymbol",
|
|
6
|
+
"enqueueTriggerSymbol",
|
|
7
|
+
"enqueueEdgeSymbol",
|
|
8
|
+
"readyNodeSymbol",
|
|
9
|
+
"beginInvocationSymbol",
|
|
10
|
+
"completeInvocationSymbol",
|
|
11
|
+
"applyInvocationResultSymbol",
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
function getWasmExports(target = null) {
|
|
15
|
+
if (target && typeof target === "object") {
|
|
16
|
+
if (
|
|
17
|
+
target.instance?.exports &&
|
|
18
|
+
typeof target.instance.exports === "object"
|
|
19
|
+
) {
|
|
20
|
+
return target.instance.exports;
|
|
21
|
+
}
|
|
22
|
+
if (target.exports && typeof target.exports === "object") {
|
|
23
|
+
return target.exports;
|
|
24
|
+
}
|
|
25
|
+
return target;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveRuntimeExport({ role, symbol, wasmExports, requiredRoles }) {
|
|
31
|
+
const required = requiredRoles.has(role);
|
|
32
|
+
if (!symbol) {
|
|
33
|
+
if (required) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Compiled runtime ABI is missing the symbol name for ${role}.`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const candidateSymbols = [symbol, `_${symbol}`];
|
|
41
|
+
const resolvedSymbol = candidateSymbols.find(
|
|
42
|
+
(candidate) => candidate in wasmExports,
|
|
43
|
+
);
|
|
44
|
+
if (!resolvedSymbol) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`Compiled runtime ABI export "${symbol}" for ${role} is not present on the wasm instance.`,
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
role,
|
|
51
|
+
symbol,
|
|
52
|
+
resolvedSymbol,
|
|
53
|
+
value: wasmExports[resolvedSymbol],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function bindCompiledRuntimeAbi({
|
|
58
|
+
artifact,
|
|
59
|
+
instance = null,
|
|
60
|
+
wasmExports = null,
|
|
61
|
+
requiredRoles = DefaultRequiredRuntimeExportRoles,
|
|
62
|
+
} = {}) {
|
|
63
|
+
const normalizedArtifact = await normalizeCompiledArtifact(artifact);
|
|
64
|
+
const resolvedWasmExports = getWasmExports(wasmExports ?? instance);
|
|
65
|
+
|
|
66
|
+
if (!resolvedWasmExports || typeof resolvedWasmExports !== "object") {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"bindCompiledRuntimeAbi requires a WebAssembly instance or exports object.",
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const requiredRoleSet = new Set(
|
|
73
|
+
Array.isArray(requiredRoles)
|
|
74
|
+
? requiredRoles
|
|
75
|
+
: DefaultRequiredRuntimeExportRoles,
|
|
76
|
+
);
|
|
77
|
+
const resolvedByRole = {};
|
|
78
|
+
const resolvedBySymbol = {};
|
|
79
|
+
|
|
80
|
+
for (const [role, symbol] of Object.entries(
|
|
81
|
+
normalizedArtifact.runtimeExports,
|
|
82
|
+
)) {
|
|
83
|
+
const binding = resolveRuntimeExport({
|
|
84
|
+
role,
|
|
85
|
+
symbol,
|
|
86
|
+
wasmExports: resolvedWasmExports,
|
|
87
|
+
requiredRoles: requiredRoleSet,
|
|
88
|
+
});
|
|
89
|
+
resolvedByRole[role] = binding?.value ?? null;
|
|
90
|
+
if (binding) {
|
|
91
|
+
resolvedBySymbol[binding.symbol] = binding.value;
|
|
92
|
+
resolvedBySymbol[binding.resolvedSymbol] = binding.value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
artifact: normalizedArtifact,
|
|
98
|
+
runtimeExports: normalizedArtifact.runtimeExports,
|
|
99
|
+
requiredRoles: Array.from(requiredRoleSet),
|
|
100
|
+
wasmExports: resolvedWasmExports,
|
|
101
|
+
resolvedByRole,
|
|
102
|
+
resolvedBySymbol,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default bindCompiledRuntimeAbi;
|