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,372 @@
1
+ import {
2
+ HostedRuntimeAdapter,
3
+ HostedRuntimeAuthority,
4
+ HostedRuntimeBindingDirection,
5
+ HostedRuntimeKind,
6
+ HostedRuntimeStartupPhase,
7
+ HostedRuntimeTransport,
8
+ } from "./constants.js";
9
+ import { evaluateHostedCapabilitySupport, normalizeHostedRuntimeEngine } from "./profile.js";
10
+
11
+ const STARTUP_PHASE_ORDER = Object.freeze({
12
+ [HostedRuntimeStartupPhase.BOOTSTRAP]: 0,
13
+ [HostedRuntimeStartupPhase.EARLY]: 1,
14
+ [HostedRuntimeStartupPhase.SESSION]: 2,
15
+ [HostedRuntimeStartupPhase.ON_DEMAND]: 3,
16
+ });
17
+
18
+ function normalizeString(value, fallback = null) {
19
+ if (typeof value !== "string") {
20
+ return fallback;
21
+ }
22
+ const normalized = value.trim();
23
+ return normalized.length > 0 ? normalized : fallback;
24
+ }
25
+
26
+ function normalizeStringArray(values) {
27
+ if (!Array.isArray(values)) {
28
+ return [];
29
+ }
30
+ const normalized = values
31
+ .map((value) => normalizeString(value, null))
32
+ .filter((value) => value !== null);
33
+ return Array.from(new Set(normalized));
34
+ }
35
+
36
+ function normalizeEnum(value, allowedValues, fallback) {
37
+ const normalized = normalizeString(value, null);
38
+ if (!normalized) {
39
+ return fallback;
40
+ }
41
+ return Object.values(allowedValues).includes(normalized)
42
+ ? normalized
43
+ : fallback;
44
+ }
45
+
46
+ function normalizeBoolean(value, fallback = false) {
47
+ if (typeof value === "boolean") {
48
+ return value;
49
+ }
50
+ return fallback;
51
+ }
52
+
53
+ function defaultRuntimeExecution(kind) {
54
+ return "compiled-wasm";
55
+ }
56
+
57
+ export function normalizeHostedBinding(binding = {}) {
58
+ const direction = normalizeEnum(
59
+ binding.direction,
60
+ HostedRuntimeBindingDirection,
61
+ HostedRuntimeBindingDirection.DIAL,
62
+ );
63
+ const transport = normalizeEnum(
64
+ binding.transport,
65
+ HostedRuntimeTransport,
66
+ HostedRuntimeTransport.SAME_APP,
67
+ );
68
+ const bindingId =
69
+ normalizeString(binding.bindingId ?? binding.binding_id, null) ??
70
+ [
71
+ direction,
72
+ transport,
73
+ normalizeString(binding.protocolId ?? binding.protocol_id, null),
74
+ normalizeString(
75
+ binding.targetRuntimeId ?? binding.target_runtime_id,
76
+ null,
77
+ ),
78
+ normalizeString(binding.url, null),
79
+ ]
80
+ .filter(Boolean)
81
+ .join(":");
82
+
83
+ return {
84
+ bindingId: bindingId || `${direction}:${transport}`,
85
+ direction,
86
+ transport,
87
+ protocolId: normalizeString(
88
+ binding.protocolId ?? binding.protocol_id,
89
+ null,
90
+ ),
91
+ targetRuntimeId: normalizeString(
92
+ binding.targetRuntimeId ?? binding.target_runtime_id,
93
+ null,
94
+ ),
95
+ audience: normalizeString(binding.audience, null),
96
+ peerId: normalizeString(binding.peerId ?? binding.peer_id, null),
97
+ url: normalizeString(binding.url, null),
98
+ required: normalizeBoolean(binding.required, true),
99
+ description: normalizeString(binding.description, null),
100
+ };
101
+ }
102
+
103
+ export function normalizeHostedRuntime(runtime = {}) {
104
+ const defaultEngine = normalizeHostedRuntimeEngine(runtime.defaultEngine, null);
105
+ const kind = normalizeEnum(
106
+ runtime.kind,
107
+ HostedRuntimeKind,
108
+ HostedRuntimeKind.FLOW,
109
+ );
110
+ const pluginId = normalizeString(runtime.pluginId ?? runtime.plugin_id, null);
111
+ const programId = normalizeString(
112
+ runtime.programId ?? runtime.program_id,
113
+ null,
114
+ );
115
+ const runtimeId = normalizeString(
116
+ runtime.runtimeId ??
117
+ runtime.runtime_id ??
118
+ runtime.serviceId ??
119
+ programId ??
120
+ pluginId,
121
+ null,
122
+ );
123
+
124
+ if (!runtimeId) {
125
+ throw new Error(
126
+ "Hosted runtime requires runtimeId, programId, or pluginId.",
127
+ );
128
+ }
129
+
130
+ const startupPhase = normalizeEnum(
131
+ runtime.startupPhase ?? runtime.startup_phase,
132
+ HostedRuntimeStartupPhase,
133
+ HostedRuntimeStartupPhase.ON_DEMAND,
134
+ );
135
+ const autoStart = normalizeBoolean(
136
+ runtime.autoStart ?? runtime.auto_start,
137
+ startupPhase !== HostedRuntimeStartupPhase.ON_DEMAND,
138
+ );
139
+
140
+ return {
141
+ runtimeId,
142
+ kind,
143
+ pluginId,
144
+ programId,
145
+ description: normalizeString(runtime.description, null),
146
+ execution: normalizeString(
147
+ runtime.execution ?? runtime.executionMode ?? runtime.execution_mode,
148
+ defaultRuntimeExecution(kind),
149
+ ),
150
+ authority: normalizeEnum(
151
+ runtime.authority,
152
+ HostedRuntimeAuthority,
153
+ HostedRuntimeAuthority.LOCAL,
154
+ ),
155
+ adapter: normalizeEnum(
156
+ runtime.adapter ?? runtime.hostAdapter ?? runtime.host_adapter,
157
+ HostedRuntimeAdapter,
158
+ null,
159
+ ),
160
+ engine:
161
+ normalizeHostedRuntimeEngine(
162
+ runtime.engine ?? runtime.runtimeEngine ?? runtime.runtime_engine,
163
+ null,
164
+ ) ?? defaultEngine,
165
+ startupPhase,
166
+ autoStart,
167
+ dependsOn: normalizeStringArray(runtime.dependsOn ?? runtime.depends_on),
168
+ requiredCapabilities: normalizeStringArray(
169
+ runtime.requiredCapabilities ?? runtime.required_capabilities,
170
+ ),
171
+ bindings: Array.isArray(runtime.bindings)
172
+ ? runtime.bindings.map((binding) => normalizeHostedBinding(binding))
173
+ : [],
174
+ };
175
+ }
176
+
177
+ export function normalizeHostedRuntimePlan(plan = {}) {
178
+ const planEngine = normalizeHostedRuntimeEngine(
179
+ plan.engine ?? plan.runtimeEngine ?? plan.runtime_engine,
180
+ null,
181
+ );
182
+ const runtimes = Array.isArray(plan.runtimes)
183
+ ? plan.runtimes.map((runtime) =>
184
+ normalizeHostedRuntime({
185
+ ...runtime,
186
+ defaultEngine: planEngine,
187
+ }),
188
+ )
189
+ : [];
190
+ return {
191
+ hostId: normalizeString(plan.hostId ?? plan.host_id, "host"),
192
+ hostKind: normalizeString(plan.hostKind ?? plan.host_kind, "host"),
193
+ description: normalizeString(plan.description, null),
194
+ adapter: normalizeEnum(
195
+ plan.adapter ?? plan.hostAdapter ?? plan.host_adapter,
196
+ HostedRuntimeAdapter,
197
+ null,
198
+ ),
199
+ engine: planEngine,
200
+ disconnectedCapable: normalizeBoolean(
201
+ plan.disconnectedCapable ?? plan.disconnected_capable,
202
+ false,
203
+ ),
204
+ runtimes,
205
+ };
206
+ }
207
+
208
+ function compareRuntimePriority(left, right) {
209
+ const leftPhaseOrder = STARTUP_PHASE_ORDER[left.startupPhase] ?? 99;
210
+ const rightPhaseOrder = STARTUP_PHASE_ORDER[right.startupPhase] ?? 99;
211
+ if (leftPhaseOrder !== rightPhaseOrder) {
212
+ return leftPhaseOrder - rightPhaseOrder;
213
+ }
214
+ if (left.autoStart !== right.autoStart) {
215
+ return left.autoStart ? -1 : 1;
216
+ }
217
+ return left.runtimeId.localeCompare(right.runtimeId);
218
+ }
219
+
220
+ function sortReadyRuntimes(runtimes) {
221
+ return runtimes.sort(compareRuntimePriority);
222
+ }
223
+
224
+ function buildStartupOrder(runtimes) {
225
+ const runtimeMap = new Map(
226
+ runtimes.map((runtime) => [runtime.runtimeId, runtime]),
227
+ );
228
+ const dependents = new Map();
229
+ const inDegree = new Map();
230
+
231
+ for (const runtime of runtimes) {
232
+ inDegree.set(runtime.runtimeId, 0);
233
+ dependents.set(runtime.runtimeId, []);
234
+ }
235
+
236
+ for (const runtime of runtimes) {
237
+ for (const dependencyId of runtime.dependsOn) {
238
+ if (!runtimeMap.has(dependencyId)) {
239
+ continue;
240
+ }
241
+ dependents.get(dependencyId).push(runtime.runtimeId);
242
+ inDegree.set(runtime.runtimeId, inDegree.get(runtime.runtimeId) + 1);
243
+ }
244
+ }
245
+
246
+ const ready = sortReadyRuntimes(
247
+ runtimes.filter((runtime) => inDegree.get(runtime.runtimeId) === 0),
248
+ );
249
+ const ordered = [];
250
+
251
+ while (ready.length > 0) {
252
+ const runtime = ready.shift();
253
+ ordered.push(runtime);
254
+ for (const dependentId of dependents.get(runtime.runtimeId)) {
255
+ const nextDegree = inDegree.get(dependentId) - 1;
256
+ inDegree.set(dependentId, nextDegree);
257
+ if (nextDegree === 0) {
258
+ ready.push(runtimeMap.get(dependentId));
259
+ sortReadyRuntimes(ready);
260
+ }
261
+ }
262
+ }
263
+
264
+ if (ordered.length !== runtimes.length) {
265
+ throw new Error("Hosted runtime plan contains a dependency cycle.");
266
+ }
267
+
268
+ return ordered;
269
+ }
270
+
271
+ function bindingSupportsDisconnectedOperation(binding) {
272
+ return (
273
+ binding.transport !== HostedRuntimeTransport.HTTP && binding.url === null
274
+ );
275
+ }
276
+
277
+ export function summarizeHostedRuntimePlan(planInput = {}) {
278
+ const plan = normalizeHostedRuntimePlan(planInput);
279
+ const startupOrder = buildStartupOrder(plan.runtimes);
280
+ const bindings = [];
281
+ const adapters = new Set();
282
+ const transports = new Set();
283
+
284
+ if (plan.adapter) {
285
+ adapters.add(plan.adapter);
286
+ }
287
+
288
+ for (const runtime of plan.runtimes) {
289
+ if (runtime.adapter) {
290
+ adapters.add(runtime.adapter);
291
+ }
292
+ for (const binding of runtime.bindings) {
293
+ bindings.push({
294
+ ownerRuntimeId: runtime.runtimeId,
295
+ startupPhase: runtime.startupPhase,
296
+ ...binding,
297
+ });
298
+ transports.add(binding.transport);
299
+ }
300
+ }
301
+
302
+ const disconnectedCapable =
303
+ plan.disconnectedCapable ||
304
+ bindings.every((binding) =>
305
+ binding.required ? bindingSupportsDisconnectedOperation(binding) : true,
306
+ );
307
+
308
+ return {
309
+ hostId: plan.hostId,
310
+ hostKind: plan.hostKind,
311
+ adapter: plan.adapter,
312
+ engine: plan.engine,
313
+ adapters: Array.from(adapters).sort(),
314
+ transports: Array.from(transports).sort(),
315
+ disconnectedCapable,
316
+ startupOrder: startupOrder.map((runtime) => ({
317
+ runtimeId: runtime.runtimeId,
318
+ kind: runtime.kind,
319
+ programId: runtime.programId,
320
+ pluginId: runtime.pluginId,
321
+ startupPhase: runtime.startupPhase,
322
+ autoStart: runtime.autoStart,
323
+ authority: runtime.authority,
324
+ adapter: runtime.adapter ?? plan.adapter,
325
+ engine: runtime.engine ?? plan.engine,
326
+ dependsOn: runtime.dependsOn,
327
+ })),
328
+ earlyStartRuntimes: startupOrder
329
+ .filter(
330
+ (runtime) =>
331
+ runtime.autoStart &&
332
+ (runtime.startupPhase === HostedRuntimeStartupPhase.BOOTSTRAP ||
333
+ runtime.startupPhase === HostedRuntimeStartupPhase.EARLY),
334
+ )
335
+ .map((runtime) => runtime.runtimeId),
336
+ localServices: startupOrder
337
+ .filter(
338
+ (runtime) =>
339
+ runtime.authority === HostedRuntimeAuthority.LOCAL &&
340
+ runtime.kind === HostedRuntimeKind.SERVICE,
341
+ )
342
+ .map((runtime) => ({
343
+ runtimeId: runtime.runtimeId,
344
+ startupPhase: runtime.startupPhase,
345
+ adapter: runtime.adapter ?? plan.adapter,
346
+ engine: runtime.engine ?? plan.engine,
347
+ })),
348
+ runtimeCompatibility: startupOrder.map((runtime) =>
349
+ ({
350
+ runtimeId: runtime.runtimeId,
351
+ adapter: runtime.adapter ?? plan.adapter,
352
+ engine: runtime.engine ?? plan.engine,
353
+ ...evaluateHostedCapabilitySupport({
354
+ adapter: runtime.adapter ?? plan.adapter,
355
+ engine: runtime.engine ?? plan.engine,
356
+ requiredCapabilities: runtime.requiredCapabilities,
357
+ }),
358
+ })),
359
+ bindings: bindings.sort((left, right) =>
360
+ `${left.ownerRuntimeId}:${left.direction}:${left.transport}:${left.protocolId ?? ""}`.localeCompare(
361
+ `${right.ownerRuntimeId}:${right.direction}:${right.transport}:${right.protocolId ?? ""}`,
362
+ ),
363
+ ),
364
+ };
365
+ }
366
+
367
+ export default {
368
+ normalizeHostedBinding,
369
+ normalizeHostedRuntime,
370
+ normalizeHostedRuntimePlan,
371
+ summarizeHostedRuntimePlan,
372
+ };