@oomol-lab/open-flow 0.1.0-beta.25 → 0.1.0-beta.26

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.
@@ -1,6 +1,8 @@
1
1
  import type { CodeModule, ConnectorCapability, JsonValue } from '../browser/flow-change.js';
2
2
  export { currentEngineContract, nodejsEngineContract, findEngineContract, type EngineContract } from './engine-contract.js';
3
3
  export type RuntimeModule = Pick<CodeModule, 'imports' | 'source'>;
4
+ /** Snapshot a Task result before JSON transport can discard or coerce invalid values. */
5
+ export declare function snapshotRuntimeResult(value: unknown): JsonValue | undefined;
4
6
  export declare const nodejsRuntimeConformanceCases: readonly RuntimeConformanceCase[];
5
7
  export interface RuntimeProgram {
6
8
  readonly engineContract: string;
@@ -19,7 +19,7 @@ function t(n, r) {
19
19
  }
20
20
  //#endregion
21
21
  //#region src/execution/common/engineContract.ts
22
- var n = "open-flow-engine/v3", r = "open-flow-engine/v3/nodejs-compat-v1", i = {
22
+ var n = "open-flow-engine/v4", r = "open-flow-engine/v4/nodejs-compat-v1", i = {
23
23
  platformExports: /* @__PURE__ */ new Set(["engineContract", "identity"]),
24
24
  platformModule: "open-flow:platform",
25
25
  platformSource: `export const engineContract = ${JSON.stringify(n)}
@@ -58,15 +58,26 @@ export function identity(value) { return value }
58
58
  `
59
59
  };
60
60
  function o(e) {
61
- if (e == "open-flow-engine/v3") return i;
62
- if (e == "open-flow-engine/v3/nodejs-compat-v1") return a;
61
+ if (e == "open-flow-engine/v4") return i;
62
+ if (e == "open-flow-engine/v4/nodejs-compat-v1") return a;
63
63
  }
64
64
  //#endregion
65
65
  //#region src/execution/common/runtime.ts
66
- var s = [{
66
+ function s(e) {
67
+ let t = /* @__PURE__ */ new Set(), n = (e, r) => {
68
+ if (r > 64) throw Error("Task result exceeds the maximum JSON depth.");
69
+ if (e === null || typeof e == "string" || typeof e == "boolean" || typeof e == "number" && Number.isFinite(e)) return e;
70
+ if (typeof e != "object" || t.has(e) || !Array.isArray(e) && Object.getPrototypeOf(e) !== Object.prototype && Object.getPrototypeOf(e) !== null || Object.getOwnPropertySymbols(e).length > 0) throw Error("Task result must contain only JSON values.");
71
+ t.add(e);
72
+ let i = Array.isArray(e) ? Array.from(e, (e) => n(e, r + 1)) : Object.fromEntries(Object.entries(e).filter(([, e]) => r != 0 || e !== void 0).map(([e, t]) => [e, n(t, r + 1)]));
73
+ return t.delete(e), i;
74
+ };
75
+ return e === void 0 ? void 0 : n(e, 0);
76
+ }
77
+ var c = [{
67
78
  name: "supports Node builtin imports and asynchronous virtual file operations",
68
79
  async verify(e) {
69
- l(await e.invoke({
80
+ u(await e.invoke({
70
81
  capability: async () => ({
71
82
  body: null,
72
83
  status: 403
@@ -74,7 +85,7 @@ var s = [{
74
85
  input: null,
75
86
  invocationId: "nodejs-files",
76
87
  program: {
77
- ...c(e, "import assert from 'node:assert/strict';\nimport { Buffer } from 'node:buffer'; import path from 'node:path'; import fs from 'node:fs/promises';\nexport default async () => {\n const file = path.join('/tmp', 'conformance.txt');\n await fs.writeFile(file, Buffer.from('你好'));\n const text = await fs.readFile(file, 'utf8');\n assert.equal(text, '你好');\n await fs.unlink(file);\n return { text, bytes: Buffer.byteLength(text) };\n}"),
88
+ ...l(e, "import assert from 'node:assert/strict';\nimport { Buffer } from 'node:buffer'; import path from 'node:path'; import fs from 'node:fs/promises';\nexport default async () => {\n const file = path.join('/tmp', 'conformance.txt');\n await fs.writeFile(file, Buffer.from('你好'));\n const text = await fs.readFile(file, 'utf8');\n assert.equal(text, '你好');\n await fs.unlink(file);\n return { text, bytes: Buffer.byteLength(text) };\n}"),
78
89
  engineContract: r
79
90
  }
80
91
  }), {
@@ -85,7 +96,7 @@ var s = [{
85
96
  }, {
86
97
  name: "preserves abort reasons and cancels Promise timers",
87
98
  async verify(e) {
88
- l(await e.invoke({
99
+ u(await e.invoke({
89
100
  capability: async () => ({
90
101
  body: null,
91
102
  status: 403
@@ -93,7 +104,7 @@ var s = [{
93
104
  input: null,
94
105
  invocationId: "nodejs-abort",
95
106
  program: {
96
- ...c(e, "import { setTimeout } from 'node:timers/promises';\nexport default async () => {\n const controller = new AbortController(); const reason = { message: 'stop' };\n const combined = AbortSignal.any([controller.signal]);\n const pending = setTimeout(60000, 'late', { signal: combined });\n controller.abort(reason); controller.abort('ignored');\n let thrown; try { combined.throwIfAborted(); } catch (error) { thrown = error; }\n try { await pending; return false; } catch (error) {\n return { sameReason: combined.reason === reason && thrown === reason, name: error.name };\n }\n}"),
107
+ ...l(e, "import { setTimeout } from 'node:timers/promises';\nexport default async () => {\n const controller = new AbortController(); const reason = { message: 'stop' };\n const combined = AbortSignal.any([controller.signal]);\n const pending = setTimeout(60000, 'late', { signal: combined });\n controller.abort(reason); controller.abort('ignored');\n let thrown; try { combined.throwIfAborted(); } catch (error) { thrown = error; }\n try { await pending; return false; } catch (error) {\n return { sameReason: combined.reason === reason && thrown === reason, name: error.name };\n }\n}"),
97
108
  engineContract: r
98
109
  }
99
110
  }), {
@@ -102,7 +113,7 @@ var s = [{
102
113
  }, "Node cancellation");
103
114
  }
104
115
  }];
105
- function c(e, t, r = {}) {
116
+ function l(e, t, r = {}) {
106
117
  return {
107
118
  engineContract: n,
108
119
  engineDigest: e.engineDigest,
@@ -116,10 +127,10 @@ function c(e, t, r = {}) {
116
127
  }
117
128
  };
118
129
  }
119
- function l(e, n, r) {
130
+ function u(e, n, r) {
120
131
  if (!t(e, n)) throw Error(`${r}: expected ${JSON.stringify(n)}, received ${JSON.stringify(e)}.`);
121
132
  }
122
- async function u(e, t, n) {
133
+ async function d(e, t, n) {
123
134
  try {
124
135
  await e;
125
136
  } catch (e) {
@@ -128,7 +139,60 @@ async function u(e, t, n) {
128
139
  }
129
140
  throw Error(`${n}: operation unexpectedly succeeded.`);
130
141
  }
131
- var d = [
142
+ var f = [
143
+ {
144
+ name: "rejects Task results that JSON transport would discard or coerce",
145
+ async verify(e) {
146
+ for (let t of [
147
+ "{ value: () => 42 }",
148
+ "{ value: Symbol(\"value\") }",
149
+ "{ value: NaN }",
150
+ "{ value: Infinity }",
151
+ "{ value: 1n }",
152
+ "{ value: { nested: undefined } }",
153
+ "{ value: [undefined] }",
154
+ "{ value: Array(1) }",
155
+ "{ value: new Date() }",
156
+ "{ value: { toJSON() { return \"hidden\" } } }",
157
+ "{ [Symbol(\"key\")]: 1 }",
158
+ "(() => { const value = {}; value.self = value; return { value } })()"
159
+ ]) await d(e.invoke({
160
+ capability: async () => ({
161
+ body: null,
162
+ status: 200
163
+ }),
164
+ input: null,
165
+ invocationId: "invalid-task-result",
166
+ program: l(e, `export default () => (${t})`)
167
+ }), /Task result must contain only JSON values/, `Invalid Task result: ${t}`);
168
+ }
169
+ },
170
+ {
171
+ name: "snapshots JSON results while preserving missing ports and shared values",
172
+ async verify(e) {
173
+ let t = await e.invoke({
174
+ capability: async () => ({
175
+ body: null,
176
+ status: 200
177
+ }),
178
+ input: null,
179
+ invocationId: "json-task-result",
180
+ program: l(e, "export default () => {\n const shared = { text: 'hello', values: [1, true, null] };\n return { missing: undefined, nullable: null, first: shared, second: shared };\n }")
181
+ }), n = {
182
+ text: "hello",
183
+ values: [
184
+ 1,
185
+ !0,
186
+ null
187
+ ]
188
+ };
189
+ u(t, {
190
+ nullable: null,
191
+ first: n,
192
+ second: n
193
+ }, "JSON Task result");
194
+ }
195
+ },
132
196
  {
133
197
  name: "preserves Action error codes and fixed per-call Connection selection",
134
198
  async verify(e) {
@@ -140,10 +204,10 @@ var d = [
140
204
  alias: "office"
141
205
  }, { connectionId: "home" }]
142
206
  }];
143
- l(await e.invoke({
207
+ u(await e.invoke({
144
208
  capabilities: t,
145
209
  capability: async ({ payload: e }) => {
146
- let n = p(t, e);
210
+ let n = m(t, e);
147
211
  if (n.input.fail) throw Object.assign(/* @__PURE__ */ Error("Provider unavailable."), { code: "connector.unavailable" });
148
212
  return {
149
213
  body: n.connectionId ?? null,
@@ -152,7 +216,7 @@ var d = [
152
216
  },
153
217
  input: null,
154
218
  invocationId: "action-accounts",
155
- program: c(e, "export default async (_, context) => {\n const values = await Promise.all([\n context.actions.example.echo({}, { connectionAlias: 'office' }),\n context.actions['example.echo']({}, { connectionId: 'home' }),\n ])\n try { await context.actions.example.echo({ fail: true }, { connectionId: 'work' }) }\n catch (error) { values.push(error.code) }\n return values\n }")
219
+ program: l(e, "export default async (_, context) => {\n const values = await Promise.all([\n context.actions.example.echo({}, { connectionAlias: 'office' }),\n context.actions['example.echo']({}, { connectionId: 'home' }),\n ])\n try { await context.actions.example.echo({ fail: true }, { connectionId: 'work' }) }\n catch (error) { values.push(error.code) }\n return values\n }")
156
220
  }), [
157
221
  "work",
158
222
  "home",
@@ -164,7 +228,7 @@ var d = [
164
228
  name: "shares Action entries and assigns independent concurrent call identities",
165
229
  async verify(e) {
166
230
  let t = [];
167
- l(await e.invoke({
231
+ u(await e.invoke({
168
232
  capabilities: [{
169
233
  kind: "connector",
170
234
  action: "example.echo",
@@ -176,7 +240,7 @@ var d = [
176
240
  }),
177
241
  input: null,
178
242
  invocationId: "action-calls",
179
- program: c(e, "export default async (_input, context) => {\n const call = context.actions.example.echo\n return { same: call === context.actions['example.echo'], values: await Promise.all([call({ value: 1 }), call({ value: 2 }), call({ value: 3 })]) }\n }")
243
+ program: l(e, "export default async (_input, context) => {\n const call = context.actions.example.echo\n return { same: call === context.actions['example.echo'], values: await Promise.all([call({ value: 1 }), call({ value: 2 }), call({ value: 3 })]) }\n }")
180
244
  }), {
181
245
  same: !0,
182
246
  values: [
@@ -187,7 +251,7 @@ var d = [
187
251
  action: "example.echo",
188
252
  input: { value: e }
189
253
  }))
190
- }, "Action results"), l(new Set(t.map((e) => e.callId)).size, 3, "Independent Action identities"), l(t.map((e) => e.invocationId), [
254
+ }, "Action results"), u(new Set(t.map((e) => e.callId)).size, 3, "Independent Action identities"), u(t.map((e) => e.invocationId), [
191
255
  "action-calls",
192
256
  "action-calls",
193
257
  "action-calls"
@@ -197,13 +261,13 @@ var d = [
197
261
  {
198
262
  name: "loads the fixed ESM closure and platform module",
199
263
  async verify(e) {
200
- l(await e.invoke({
264
+ u(await e.invoke({
201
265
  capability: async () => {
202
266
  throw Error("Capability must not be called.");
203
267
  },
204
268
  input: { value: 6 },
205
269
  invocationId: "module-closure",
206
- program: c(e, "import { double } from './helper.mjs'\nimport { engineContract, identity } from 'open-flow:platform'\nexport default (input) => ({ engineContract, value: identity(double(input.value)) })", { helper: {
270
+ program: l(e, "import { double } from './helper.mjs'\nimport { engineContract, identity } from 'open-flow:platform'\nexport default (input) => ({ engineContract, value: identity(double(input.value)) })", { helper: {
207
271
  imports: [],
208
272
  source: "export const double = (value) => value * 2"
209
273
  } })
@@ -216,14 +280,14 @@ var d = [
216
280
  {
217
281
  name: "rejects modules outside the fixed closure",
218
282
  async verify(e) {
219
- await u(e.invoke({
283
+ await d(e.invoke({
220
284
  capability: async () => ({
221
285
  body: null,
222
286
  status: 200
223
287
  }),
224
288
  input: null,
225
289
  invocationId: "closed-linker",
226
- program: c(e, "import fs from 'node:fs'\nexport default () => fs.readFileSync('/etc/passwd', 'utf8')")
290
+ program: l(e, "import fs from 'node:fs'\nexport default () => fs.readFileSync('/etc/passwd', 'utf8')")
227
291
  }), /invalid|unsupported|cannot import|not part of/i, "Closed linker");
228
292
  }
229
293
  },
@@ -231,7 +295,7 @@ var d = [
231
295
  name: "copies asynchronous Capability requests and responses",
232
296
  async verify(e) {
233
297
  let t = [];
234
- l(await e.invoke({
298
+ u(await e.invoke({
235
299
  capability: async (e) => (t.push(e), {
236
300
  body: {
237
301
  accepted: !0,
@@ -241,7 +305,7 @@ var d = [
241
305
  }),
242
306
  input: { issue: 42 },
243
307
  invocationId: "async-capability",
244
- program: c(e, "export default async (input, capability) => capability.artifact.put({ action: 'read', input })")
308
+ program: l(e, "export default async (input, capability) => capability.artifact.put({ action: 'read', input })")
245
309
  }), {
246
310
  body: {
247
311
  accepted: !0,
@@ -251,7 +315,7 @@ var d = [
251
315
  }
252
316
  },
253
317
  status: 200
254
- }, "Capability result"), l(t.map(({ invocationId: e, kind: t, payload: n }) => ({
318
+ }, "Capability result"), u(t.map(({ invocationId: e, kind: t, payload: n }) => ({
255
319
  invocationId: e,
256
320
  kind: t,
257
321
  payload: n
@@ -268,13 +332,13 @@ var d = [
268
332
  {
269
333
  name: "returns final outputs without exposing intermediate output capability",
270
334
  async verify(e) {
271
- l(await e.invoke({
335
+ u(await e.invoke({
272
336
  capability: async () => {
273
337
  throw Error("Capability must not be called.");
274
338
  },
275
339
  input: null,
276
340
  invocationId: "final-outputs",
277
- program: c(e, "export default (_input, context) => ({ available: typeof context.outputs, value: 1 })")
341
+ program: l(e, "export default (_input, context) => ({ available: typeof context.outputs, value: 1 })")
278
342
  }), {
279
343
  available: "undefined",
280
344
  value: 1
@@ -297,14 +361,14 @@ var d = [
297
361
  }, { once: !0 }))),
298
362
  input: null,
299
363
  invocationId: "canceled-capability",
300
- program: c(e, "export default async (_input, context) => context.actions.example.wait({})"),
364
+ program: l(e, "export default async (_input, context) => context.actions.example.wait({})"),
301
365
  signal: t.signal
302
366
  });
303
- await i, t.abort(/* @__PURE__ */ Error("Invocation canceled.")), await u(a, /cancel|abort|disposed/i, "Canceled invocation"), l(n, !0, "Capability cancellation");
367
+ await i, t.abort(/* @__PURE__ */ Error("Invocation canceled.")), await d(a, /cancel|abort|disposed/i, "Canceled invocation"), u(n, !0, "Capability cancellation");
304
368
  }
305
369
  }
306
370
  ];
307
- function f(e, t) {
371
+ function p(e, t) {
308
372
  let n = (e, t) => {
309
373
  if (!(e === null || typeof e == "string" || typeof e == "boolean" || typeof e == "number" && Number.isFinite(e))) {
310
374
  if (typeof e != "object" || t.has(e) || !Array.isArray(e) && Object.getPrototypeOf(e) !== Object.prototype && Object.getPrototypeOf(e) !== null || Object.getOwnPropertySymbols(e).length > 0) throw Object.assign(/* @__PURE__ */ Error("Action parameters must contain only JSON values."), { code: "capability.invalid" });
@@ -326,7 +390,7 @@ function f(e, t) {
326
390
  for (let e of i.values()) Object.freeze(e);
327
391
  return Object.freeze(r);
328
392
  }
329
- function p(e, t) {
393
+ function m(e, t) {
330
394
  let n = Object.assign(/* @__PURE__ */ Error("The Action request is invalid."), { code: "capability.invalid" }), r = Object.assign(/* @__PURE__ */ Error("The Action or Connection is not declared for this Task."), { code: "capability.denied" });
331
395
  if (typeof t != "object" || !t || Array.isArray(t)) throw n;
332
396
  let i = t;
@@ -356,4 +420,4 @@ function p(e, t) {
356
420
  };
357
421
  }
358
422
  //#endregion
359
- export { f as createActions, n as currentEngineContract, o as findEngineContract, r as nodejsEngineContract, s as nodejsRuntimeConformanceCases, p as resolveAction, d as runtimeConformanceCases };
423
+ export { p as createActions, n as currentEngineContract, o as findEngineContract, r as nodejsEngineContract, c as nodejsRuntimeConformanceCases, m as resolveAction, f as runtimeConformanceCases, s as snapshotRuntimeResult };