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.
Files changed (69) hide show
  1. package/.claude/SKILLS.md +7 -0
  2. package/.claude/skills/sdn-plugin-abi-compliance/SKILL.md +56 -0
  3. package/.claude/todo/001-js-host-startup-and-deno.md +85 -0
  4. package/LICENSE +21 -0
  5. package/README.md +223 -0
  6. package/bin/sdn-flow-host.js +169 -0
  7. package/docs/.nojekyll +0 -0
  8. package/docs/ARCHITECTURE.md +200 -0
  9. package/docs/HOST_CAPABILITY_MODEL.md +317 -0
  10. package/docs/PLUGIN_ARCHITECTURE.md +145 -0
  11. package/docs/PLUGIN_COMPATIBILITY.md +61 -0
  12. package/docs/PLUGIN_COMPLIANCE_CHECKS.md +82 -0
  13. package/docs/PLUGIN_MANIFEST.md +94 -0
  14. package/docs/css/style.css +465 -0
  15. package/docs/index.html +218 -0
  16. package/docs/js/app.mjs +751 -0
  17. package/docs/js/editor-panel.mjs +203 -0
  18. package/docs/js/flow-canvas.mjs +515 -0
  19. package/docs/js/flow-model.mjs +391 -0
  20. package/docs/js/workers/emception.worker.js +146 -0
  21. package/docs/js/workers/pyodide.worker.js +134 -0
  22. package/native/flow_source_generator.cpp +1958 -0
  23. package/package.json +67 -0
  24. package/schemas/FlowRuntimeAbi.fbs +91 -0
  25. package/src/auth/canonicalize.js +5 -0
  26. package/src/auth/index.js +11 -0
  27. package/src/auth/permissions.js +8 -0
  28. package/src/compiler/CppFlowSourceGenerator.js +475 -0
  29. package/src/compiler/EmceptionCompilerAdapter.js +244 -0
  30. package/src/compiler/SignedArtifactCatalog.js +152 -0
  31. package/src/compiler/index.js +8 -0
  32. package/src/compiler/nativeFlowSourceGeneratorTool.js +144 -0
  33. package/src/compliance/index.js +13 -0
  34. package/src/compliance/pluginCompliance.js +11 -0
  35. package/src/deploy/FlowDeploymentClient.js +532 -0
  36. package/src/deploy/index.js +8 -0
  37. package/src/designer/FlowDesignerSession.js +158 -0
  38. package/src/designer/index.js +2 -0
  39. package/src/designer/requirements.js +184 -0
  40. package/src/generated/runtimeAbiLayouts.js +544 -0
  41. package/src/host/appHost.js +105 -0
  42. package/src/host/autoHost.js +113 -0
  43. package/src/host/browserHostAdapters.js +108 -0
  44. package/src/host/compiledFlowRuntimeHost.js +703 -0
  45. package/src/host/constants.js +55 -0
  46. package/src/host/dependencyRuntime.js +227 -0
  47. package/src/host/descriptorAbi.js +351 -0
  48. package/src/host/fetchService.js +237 -0
  49. package/src/host/httpHostAdapters.js +280 -0
  50. package/src/host/index.js +91 -0
  51. package/src/host/installedFlowHost.js +885 -0
  52. package/src/host/invocationAbi.js +440 -0
  53. package/src/host/normalize.js +372 -0
  54. package/src/host/packageManagers.js +369 -0
  55. package/src/host/profile.js +134 -0
  56. package/src/host/runtimeAbi.js +106 -0
  57. package/src/host/workspace.js +895 -0
  58. package/src/index.js +8 -0
  59. package/src/runtime/FlowRuntime.js +273 -0
  60. package/src/runtime/MethodRegistry.js +295 -0
  61. package/src/runtime/constants.js +44 -0
  62. package/src/runtime/index.js +19 -0
  63. package/src/runtime/normalize.js +377 -0
  64. package/src/transport/index.js +7 -0
  65. package/src/transport/pki.js +7 -0
  66. package/src/utils/crypto.js +7 -0
  67. package/src/utils/encoding.js +65 -0
  68. package/src/utils/wasmCrypto.js +69 -0
  69. package/tools/run-plugin-compliance-check.mjs +153 -0
@@ -0,0 +1,440 @@
1
+ import {
2
+ bindCompiledRuntimeAbi,
3
+ DefaultRequiredRuntimeExportRoles,
4
+ } from "./runtimeAbi.js";
5
+ import {
6
+ FlowFrameDescriptorLayout,
7
+ FlowInvocationDescriptorLayout,
8
+ } from "../generated/runtimeAbiLayouts.js";
9
+
10
+ export { FlowFrameDescriptorLayout, FlowInvocationDescriptorLayout };
11
+
12
+ export const DefaultRequiredInvocationExportRoles = Object.freeze([
13
+ ...DefaultRequiredRuntimeExportRoles,
14
+ "mallocSymbol",
15
+ "freeSymbol",
16
+ "ingressFrameDescriptorsSymbol",
17
+ "ingressFrameDescriptorCountSymbol",
18
+ "currentInvocationDescriptorSymbol",
19
+ "prepareInvocationDescriptorSymbol",
20
+ "enqueueTriggerFrameSymbol",
21
+ "enqueueEdgeFrameSymbol",
22
+ "applyInvocationResultSymbol",
23
+ ]);
24
+
25
+ const INVALID_INDEX = 0xffffffff;
26
+
27
+ function resolveMemory(bound, explicitMemory = null) {
28
+ const memory =
29
+ explicitMemory ??
30
+ bound?.wasmExports?.memory ??
31
+ bound?.artifact?.wasmMemory ??
32
+ null;
33
+ if (!memory || !(memory.buffer instanceof ArrayBuffer)) {
34
+ throw new Error(
35
+ "Compiled invocation ABI requires a WebAssembly.Memory export.",
36
+ );
37
+ }
38
+ return memory;
39
+ }
40
+
41
+ function readCString(memory, pointer) {
42
+ if (!pointer) {
43
+ return null;
44
+ }
45
+ const bytes = new Uint8Array(memory.buffer);
46
+ let end = pointer >>> 0;
47
+ while (end < bytes.length && bytes[end] !== 0) {
48
+ end += 1;
49
+ }
50
+ return new TextDecoder().decode(bytes.subarray(pointer >>> 0, end));
51
+ }
52
+
53
+ function writeCString(memory, pointer, value) {
54
+ const bytes = new Uint8Array(memory.buffer);
55
+ const encoded = new TextEncoder().encode(`${String(value ?? "")}\0`);
56
+ bytes.set(encoded, pointer >>> 0);
57
+ }
58
+
59
+ function cloneBytes(memory, offset, size) {
60
+ const base = Number(offset) >>> 0;
61
+ const length = Number(size) >>> 0;
62
+ if (length === 0) {
63
+ return new Uint8Array();
64
+ }
65
+ const bytes = new Uint8Array(memory.buffer, base, length);
66
+ return new Uint8Array(bytes);
67
+ }
68
+
69
+ function readFrameDescriptor(memory, pointer) {
70
+ if (!pointer) {
71
+ return null;
72
+ }
73
+ const view = new DataView(memory.buffer);
74
+ const base = pointer >>> 0;
75
+ return {
76
+ ingressIndex: view.getUint32(
77
+ base + FlowFrameDescriptorLayout.fields.ingressIndex.offset,
78
+ true,
79
+ ),
80
+ typeDescriptorIndex: view.getUint32(
81
+ base + FlowFrameDescriptorLayout.fields.typeDescriptorIndex.offset,
82
+ true,
83
+ ),
84
+ portIdPointer: view.getUint32(
85
+ base + FlowFrameDescriptorLayout.fields.portIdPointer.offset,
86
+ true,
87
+ ),
88
+ portId: readCString(
89
+ memory,
90
+ view.getUint32(base + FlowFrameDescriptorLayout.fields.portIdPointer.offset, true),
91
+ ),
92
+ alignment: view.getUint32(
93
+ base + FlowFrameDescriptorLayout.fields.alignment.offset,
94
+ true,
95
+ ),
96
+ offset: view.getUint32(
97
+ base + FlowFrameDescriptorLayout.fields.offset.offset,
98
+ true,
99
+ ),
100
+ size: view.getUint32(
101
+ base + FlowFrameDescriptorLayout.fields.size.offset,
102
+ true,
103
+ ),
104
+ streamId: view.getUint32(
105
+ base + FlowFrameDescriptorLayout.fields.streamId.offset,
106
+ true,
107
+ ),
108
+ sequence: view.getUint32(
109
+ base + FlowFrameDescriptorLayout.fields.sequence.offset,
110
+ true,
111
+ ),
112
+ traceToken: Number(
113
+ view.getBigUint64(
114
+ base + FlowFrameDescriptorLayout.fields.traceToken.offset,
115
+ true,
116
+ ),
117
+ ),
118
+ endOfStream:
119
+ view.getUint8(base + FlowFrameDescriptorLayout.fields.endOfStream.offset) !==
120
+ 0,
121
+ occupied:
122
+ view.getUint8(base + FlowFrameDescriptorLayout.fields.occupied.offset) !== 0,
123
+ };
124
+ }
125
+
126
+ function allocateCString(bound, memory, value) {
127
+ if (value === null || value === undefined) {
128
+ return 0;
129
+ }
130
+ const encoded = new TextEncoder().encode(`${String(value)}\0`);
131
+ const pointer = Number(bound.resolvedByRole.mallocSymbol(encoded.length)) >>> 0;
132
+ writeCString(memory, pointer, value);
133
+ return pointer;
134
+ }
135
+
136
+ function writeFrameDescriptor(memory, pointer, frame = {}, portIdPointer = 0) {
137
+ const view = new DataView(memory.buffer);
138
+ const base = pointer >>> 0;
139
+ view.setUint32(
140
+ base + FlowFrameDescriptorLayout.fields.ingressIndex.offset,
141
+ Number(frame.ingressIndex ?? frame.ingress_index ?? INVALID_INDEX) >>> 0,
142
+ true,
143
+ );
144
+ view.setUint32(
145
+ base + FlowFrameDescriptorLayout.fields.typeDescriptorIndex.offset,
146
+ Number(
147
+ frame.typeDescriptorIndex ?? frame.type_descriptor_index ?? INVALID_INDEX,
148
+ ) >>> 0,
149
+ true,
150
+ );
151
+ view.setUint32(
152
+ base + FlowFrameDescriptorLayout.fields.portIdPointer.offset,
153
+ Number(portIdPointer) >>> 0,
154
+ true,
155
+ );
156
+ view.setUint32(
157
+ base + FlowFrameDescriptorLayout.fields.alignment.offset,
158
+ Number(frame.alignment ?? 8) >>> 0,
159
+ true,
160
+ );
161
+ view.setUint32(
162
+ base + FlowFrameDescriptorLayout.fields.offset.offset,
163
+ Number(frame.offset ?? 0) >>> 0,
164
+ true,
165
+ );
166
+ view.setUint32(
167
+ base + FlowFrameDescriptorLayout.fields.size.offset,
168
+ Number(frame.size ?? 0) >>> 0,
169
+ true,
170
+ );
171
+ view.setUint32(
172
+ base + FlowFrameDescriptorLayout.fields.streamId.offset,
173
+ Number(frame.streamId ?? frame.stream_id ?? 0) >>> 0,
174
+ true,
175
+ );
176
+ view.setUint32(
177
+ base + FlowFrameDescriptorLayout.fields.sequence.offset,
178
+ Number(frame.sequence ?? 0) >>> 0,
179
+ true,
180
+ );
181
+ view.setBigUint64(
182
+ base + FlowFrameDescriptorLayout.fields.traceToken.offset,
183
+ BigInt(Number(frame.traceToken ?? frame.trace_token ?? 0)),
184
+ true,
185
+ );
186
+ view.setUint8(
187
+ base + FlowFrameDescriptorLayout.fields.endOfStream.offset,
188
+ (frame.endOfStream ?? frame.end_of_stream) ? 1 : 0,
189
+ );
190
+ view.setUint8(base + FlowFrameDescriptorLayout.fields.occupied.offset, 1);
191
+ }
192
+
193
+ function readInvocationDescriptor(memory, pointer) {
194
+ if (!pointer) {
195
+ return null;
196
+ }
197
+ const view = new DataView(memory.buffer);
198
+ const base = pointer >>> 0;
199
+ const frameCount = view.getUint32(
200
+ base + FlowInvocationDescriptorLayout.fields.frameCount.offset,
201
+ true,
202
+ );
203
+ const framesPointer = view.getUint32(
204
+ base + FlowInvocationDescriptorLayout.fields.framesPointer.offset,
205
+ true,
206
+ );
207
+ const frames = [];
208
+ for (let index = 0; index < frameCount; index += 1) {
209
+ frames.push(
210
+ readFrameDescriptor(
211
+ memory,
212
+ framesPointer + index * FlowFrameDescriptorLayout.size,
213
+ ),
214
+ );
215
+ }
216
+ return {
217
+ nodeIndex: view.getUint32(
218
+ base + FlowInvocationDescriptorLayout.fields.nodeIndex.offset,
219
+ true,
220
+ ),
221
+ dispatchDescriptorIndex: view.getUint32(
222
+ base + FlowInvocationDescriptorLayout.fields.dispatchDescriptorIndex.offset,
223
+ true,
224
+ ),
225
+ pluginIdPointer: view.getUint32(
226
+ base + FlowInvocationDescriptorLayout.fields.pluginIdPointer.offset,
227
+ true,
228
+ ),
229
+ methodIdPointer: view.getUint32(
230
+ base + FlowInvocationDescriptorLayout.fields.methodIdPointer.offset,
231
+ true,
232
+ ),
233
+ framesPointer,
234
+ frameCount,
235
+ pluginId: readCString(
236
+ memory,
237
+ view.getUint32(
238
+ base + FlowInvocationDescriptorLayout.fields.pluginIdPointer.offset,
239
+ true,
240
+ ),
241
+ ),
242
+ methodId: readCString(
243
+ memory,
244
+ view.getUint32(
245
+ base + FlowInvocationDescriptorLayout.fields.methodIdPointer.offset,
246
+ true,
247
+ ),
248
+ ),
249
+ frames,
250
+ };
251
+ }
252
+
253
+ function withAllocatedFrame(bound, memory, frame, invoke) {
254
+ const malloc = bound.resolvedByRole.mallocSymbol;
255
+ const free = bound.resolvedByRole.freeSymbol;
256
+ const pointer = Number(malloc(FlowFrameDescriptorLayout.size)) >>> 0;
257
+ const portIdPointer = allocateCString(
258
+ bound,
259
+ memory,
260
+ frame.portId ?? frame.port_id ?? null,
261
+ );
262
+ try {
263
+ writeFrameDescriptor(memory, pointer, frame, portIdPointer);
264
+ return invoke(pointer);
265
+ } finally {
266
+ if (portIdPointer) {
267
+ free(portIdPointer);
268
+ }
269
+ free(pointer);
270
+ }
271
+ }
272
+
273
+ function withAllocatedFrames(bound, memory, frames = [], invoke) {
274
+ const normalizedFrames = Array.isArray(frames) ? frames : [];
275
+ if (normalizedFrames.length === 0) {
276
+ return invoke(0, 0);
277
+ }
278
+ const malloc = bound.resolvedByRole.mallocSymbol;
279
+ const free = bound.resolvedByRole.freeSymbol;
280
+ const descriptorBytes = FlowFrameDescriptorLayout.size * normalizedFrames.length;
281
+ const descriptorsPointer = Number(malloc(descriptorBytes)) >>> 0;
282
+ const portIdPointers = [];
283
+ const payloadPointers = [];
284
+ try {
285
+ normalizedFrames.forEach((frame, index) => {
286
+ const portIdPointer = allocateCString(
287
+ bound,
288
+ memory,
289
+ frame.portId ?? frame.port_id ?? null,
290
+ );
291
+ portIdPointers.push(portIdPointer);
292
+ const payload =
293
+ frame.bytes ??
294
+ frame.data ??
295
+ frame.payloadBytes ??
296
+ frame.payload_bytes ??
297
+ null;
298
+ let payloadPointer = Number(frame.offset ?? 0) >>> 0;
299
+ let payloadSize = Number(frame.size ?? 0) >>> 0;
300
+ if (payload instanceof Uint8Array || ArrayBuffer.isView(payload) || payload instanceof ArrayBuffer) {
301
+ const payloadBytes = payload instanceof Uint8Array
302
+ ? payload
303
+ : payload instanceof ArrayBuffer
304
+ ? new Uint8Array(payload)
305
+ : new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength);
306
+ payloadPointer = Number(malloc(payloadBytes.byteLength || 1)) >>> 0;
307
+ payloadSize = payloadBytes.byteLength;
308
+ new Uint8Array(memory.buffer).set(payloadBytes, payloadPointer);
309
+ payloadPointers.push(payloadPointer);
310
+ }
311
+ writeFrameDescriptor(
312
+ memory,
313
+ descriptorsPointer + index * FlowFrameDescriptorLayout.size,
314
+ {
315
+ ...frame,
316
+ offset: payloadPointer,
317
+ size: payloadSize,
318
+ },
319
+ portIdPointer,
320
+ );
321
+ });
322
+ return invoke(descriptorsPointer, normalizedFrames.length);
323
+ } finally {
324
+ portIdPointers.forEach((pointer) => {
325
+ if (pointer) {
326
+ free(pointer);
327
+ }
328
+ });
329
+ // Descriptor memory is only needed for the duration of the host call.
330
+ free(descriptorsPointer);
331
+ // Payload memory is retained by the compiled runtime until reset; caller is
332
+ // expected to release tracked allocations through the bound host helpers.
333
+ payloadPointers.forEach((pointer) => {
334
+ if (pointer) {
335
+ bound.retainedArenaAllocations.add(pointer);
336
+ }
337
+ });
338
+ }
339
+ }
340
+
341
+ export async function bindCompiledInvocationAbi({
342
+ artifact,
343
+ instance = null,
344
+ wasmExports = null,
345
+ memory = null,
346
+ requiredRoles = DefaultRequiredInvocationExportRoles,
347
+ } = {}) {
348
+ const bound = await bindCompiledRuntimeAbi({
349
+ artifact,
350
+ instance,
351
+ wasmExports,
352
+ requiredRoles,
353
+ });
354
+ const resolvedMemory = resolveMemory(bound, memory);
355
+ bound.retainedArenaAllocations = new Set();
356
+
357
+ return {
358
+ ...bound,
359
+ memory: resolvedMemory,
360
+ retainedArenaAllocations: bound.retainedArenaAllocations,
361
+ releaseRetainedArenaAllocations() {
362
+ const free = bound.resolvedByRole.freeSymbol;
363
+ for (const pointer of this.retainedArenaAllocations) {
364
+ free(pointer);
365
+ }
366
+ this.retainedArenaAllocations.clear();
367
+ },
368
+ readFrameBytes(frame) {
369
+ if (!frame) {
370
+ return new Uint8Array();
371
+ }
372
+ return cloneBytes(
373
+ resolvedMemory,
374
+ frame.offset ?? frame.offset_bytes ?? 0,
375
+ frame.size ?? 0,
376
+ );
377
+ },
378
+ readCurrentInvocation() {
379
+ const pointer =
380
+ Number(bound.resolvedByRole.currentInvocationDescriptorSymbol()) >>> 0;
381
+ return readInvocationDescriptor(resolvedMemory, pointer);
382
+ },
383
+ readIngressFrameDescriptors() {
384
+ const pointer =
385
+ Number(bound.resolvedByRole.ingressFrameDescriptorsSymbol()) >>> 0;
386
+ const count = Number(
387
+ bound.resolvedByRole.ingressFrameDescriptorCountSymbol(),
388
+ );
389
+ const frames = [];
390
+ for (let index = 0; index < count; index += 1) {
391
+ frames.push(
392
+ readFrameDescriptor(
393
+ resolvedMemory,
394
+ pointer + index * FlowFrameDescriptorLayout.size,
395
+ ),
396
+ );
397
+ }
398
+ return frames;
399
+ },
400
+ prepareNodeInvocationDescriptor(nodeIndex, frameBudget = 1) {
401
+ bound.resolvedByRole.prepareInvocationDescriptorSymbol(
402
+ nodeIndex,
403
+ frameBudget,
404
+ );
405
+ return this.readCurrentInvocation();
406
+ },
407
+ enqueueTriggerFrame(triggerIndex, frame) {
408
+ return withAllocatedFrame(bound, resolvedMemory, frame, (pointer) =>
409
+ bound.resolvedByRole.enqueueTriggerFrameSymbol(triggerIndex, pointer),
410
+ );
411
+ },
412
+ enqueueEdgeFrame(edgeIndex, frame) {
413
+ return withAllocatedFrame(bound, resolvedMemory, frame, (pointer) =>
414
+ bound.resolvedByRole.enqueueEdgeFrameSymbol(edgeIndex, pointer),
415
+ );
416
+ },
417
+ applyNodeInvocationResult(nodeIndex, result = {}) {
418
+ return withAllocatedFrames(
419
+ bound,
420
+ resolvedMemory,
421
+ result.outputs ?? [],
422
+ (pointer, count) =>
423
+ bound.resolvedByRole.applyInvocationResultSymbol(
424
+ nodeIndex,
425
+ Number(result.statusCode ?? result.status_code ?? 0) >>> 0,
426
+ Number(result.backlogRemaining ?? result.backlog_remaining ?? 0) >>> 0,
427
+ Boolean(result.yielded ?? false),
428
+ pointer,
429
+ count,
430
+ ),
431
+ );
432
+ },
433
+ resetRuntimeState() {
434
+ bound.resolvedByRole.resetStateSymbol();
435
+ this.releaseRetainedArenaAllocations();
436
+ },
437
+ };
438
+ }
439
+
440
+ export default bindCompiledInvocationAbi;