@workflow/world-testing 4.1.0-beta.56 → 4.1.0-beta.58

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,27 +1,6 @@
1
1
  {
2
2
  "version": "1.0.0",
3
3
  "steps": {
4
- "workflows/addition.ts": {
5
- "add": {
6
- "stepId": "step//./workflows/addition//add"
7
- }
8
- },
9
- "workflow/dist/internal/builtins.js": {
10
- "__builtin_response_array_buffer": {
11
- "stepId": "__builtin_response_array_buffer"
12
- },
13
- "__builtin_response_json": {
14
- "stepId": "__builtin_response_json"
15
- },
16
- "__builtin_response_text": {
17
- "stepId": "__builtin_response_text"
18
- }
19
- },
20
- "workflow/dist/stdlib.js": {
21
- "fetch": {
22
- "stepId": "step//workflow@4.1.0-beta.55//fetch"
23
- }
24
- },
25
4
  "workflows/hooks.ts": {
26
5
  "writeEvent": {
27
6
  "stepId": "step//./workflows/hooks//writeEvent"
@@ -32,6 +11,11 @@
32
11
  "stepId": "step//./workflows/noop//noop"
33
12
  }
34
13
  },
14
+ "workflows/addition.ts": {
15
+ "add": {
16
+ "stepId": "step//./workflows/addition//add"
17
+ }
18
+ },
35
19
  "workflows/null-byte.ts": {
36
20
  "nullByteStep": {
37
21
  "stepId": "step//./workflows/null-byte//nullByteStep"
@@ -44,18 +28,25 @@
44
28
  "stepThatThrowsRetryableError": {
45
29
  "stepId": "step//./workflows/retriable-and-fatal//stepThatThrowsRetryableError"
46
30
  }
31
+ },
32
+ "workflow/dist/internal/builtins.js": {
33
+ "__builtin_response_array_buffer": {
34
+ "stepId": "__builtin_response_array_buffer"
35
+ },
36
+ "__builtin_response_json": {
37
+ "stepId": "__builtin_response_json"
38
+ },
39
+ "__builtin_response_text": {
40
+ "stepId": "__builtin_response_text"
41
+ }
42
+ },
43
+ "workflow/dist/stdlib.js": {
44
+ "fetch": {
45
+ "stepId": "step//workflow@4.1.0-beta.57//fetch"
46
+ }
47
47
  }
48
48
  },
49
49
  "workflows": {
50
- "workflows/addition.ts": {
51
- "addition": {
52
- "workflowId": "workflow//./workflows/addition//addition",
53
- "graph": {
54
- "nodes": [],
55
- "edges": []
56
- }
57
- }
58
- },
59
50
  "workflows/hooks.ts": {
60
51
  "collectWithHook": {
61
52
  "workflowId": "workflow//./workflows/hooks//collectWithHook",
@@ -74,6 +65,15 @@
74
65
  }
75
66
  }
76
67
  },
68
+ "workflows/addition.ts": {
69
+ "addition": {
70
+ "workflowId": "workflow//./workflows/addition//addition",
71
+ "graph": {
72
+ "nodes": [],
73
+ "edges": []
74
+ }
75
+ }
76
+ },
77
77
  "workflows/null-byte.ts": {
78
78
  "nullByteWorkflow": {
79
79
  "workflowId": "workflow//./workflows/null-byte//nullByteWorkflow",
@@ -25191,7 +25191,7 @@ async function fetch2(...args) {
25191
25191
  return globalThis.fetch(...args);
25192
25192
  }
25193
25193
  __name(fetch2, "fetch");
25194
- registerStepFunction("step//workflow@4.1.0-beta.55//fetch", fetch2);
25194
+ registerStepFunction("step//workflow@4.1.0-beta.57//fetch", fetch2);
25195
25195
  // workflows/addition.ts
25196
25196
  async function add(num, num2) {
25197
25197
  return num + num2;
@@ -43345,7 +43345,7 @@ async function withWindowsRetry(fn, maxRetries = 5) {
43345
43345
  const isRetryable = attempt < maxRetries && retryableErrors.includes(error45.code);
43346
43346
  if (!isRetryable)
43347
43347
  throw error45;
43348
- const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * baseDelayMs;
43348
+ const delay = baseDelayMs * 2 ** attempt + Math.random() * baseDelayMs;
43349
43349
  await new Promise((resolve2) => setTimeout(resolve2, delay));
43350
43350
  }
43351
43351
  }
@@ -43458,9 +43458,13 @@ async function deleteJSON(filePath) {
43458
43458
  }
43459
43459
  __name(deleteJSON, "deleteJSON");
43460
43460
  async function listJSONFiles(dirPath) {
43461
+ return listFilesByExtension(dirPath, ".json");
43462
+ }
43463
+ __name(listJSONFiles, "listJSONFiles");
43464
+ async function listFilesByExtension(dirPath, extension) {
43461
43465
  try {
43462
43466
  const files = await import_node_fs.promises.readdir(dirPath);
43463
- return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(".json", ""));
43467
+ return files.filter((f) => f.endsWith(extension)).map((f) => f.slice(0, -extension.length));
43464
43468
  }
43465
43469
  catch (error45) {
43466
43470
  if (error45.code === "ENOENT")
@@ -43468,7 +43472,7 @@ async function listJSONFiles(dirPath) {
43468
43472
  throw error45;
43469
43473
  }
43470
43474
  }
43471
- __name(listJSONFiles, "listJSONFiles");
43475
+ __name(listFilesByExtension, "listFilesByExtension");
43472
43476
  function parseCursor(cursor) {
43473
43477
  if (!cursor)
43474
43478
  return null;
@@ -44435,7 +44439,7 @@ function createStreamer(basedir) {
44435
44439
  chunk: chunkBuffer,
44436
44440
  eof: false
44437
44441
  });
44438
- const chunkPath = import_node_path8.default.join(basedir, "streams", "chunks", `${name}-${chunkId}.json`);
44442
+ const chunkPath = import_node_path8.default.join(basedir, "streams", "chunks", `${name}-${chunkId}.bin`);
44439
44443
  await write(chunkPath, serialized);
44440
44444
  const chunkData = Uint8Array.from(chunkBuffer);
44441
44445
  streamEmitter.emit(`chunk:${name}`, {
@@ -44457,7 +44461,7 @@ function createStreamer(basedir) {
44457
44461
  chunk: chunkBuffer,
44458
44462
  eof: false
44459
44463
  });
44460
- const chunkPath = import_node_path8.default.join(basedir, "streams", "chunks", `${name}-${chunkId}.json`);
44464
+ const chunkPath = import_node_path8.default.join(basedir, "streams", "chunks", `${name}-${chunkId}.bin`);
44461
44465
  await write(chunkPath, serialized);
44462
44466
  return {
44463
44467
  chunkId,
@@ -44477,7 +44481,7 @@ function createStreamer(basedir) {
44477
44481
  const chunkId = `chnk_${monotonicUlid2()}`;
44478
44482
  const runId = await _runId;
44479
44483
  await registerStreamForRun(runId, name);
44480
- const chunkPath = import_node_path8.default.join(basedir, "streams", "chunks", `${name}-${chunkId}.json`);
44484
+ const chunkPath = import_node_path8.default.join(basedir, "streams", "chunks", `${name}-${chunkId}.bin`);
44481
44485
  await write(chunkPath, serializeChunk({
44482
44486
  chunk: Buffer.from([]),
44483
44487
  eof: true
@@ -44532,8 +44536,18 @@ function createStreamer(basedir) {
44532
44536
  removeListeners = closeListener;
44533
44537
  streamEmitter.on(`chunk:${name}`, chunkListener);
44534
44538
  streamEmitter.on(`close:${name}`, closeListener);
44535
- const files = await listJSONFiles(chunksDir);
44536
- const chunkFiles = files.filter((file2) => file2.startsWith(`${name}-`)).sort();
44539
+ const [binFiles, jsonFiles] = await Promise.all([
44540
+ listFilesByExtension(chunksDir, ".bin"),
44541
+ listFilesByExtension(chunksDir, ".json")
44542
+ ]);
44543
+ const fileExtMap = /* @__PURE__ */ new Map();
44544
+ for (const f of jsonFiles)
44545
+ fileExtMap.set(f, ".json");
44546
+ for (const f of binFiles)
44547
+ fileExtMap.set(f, ".bin");
44548
+ const chunkFiles = [
44549
+ ...fileExtMap.keys()
44550
+ ].filter((file2) => file2.startsWith(`${name}-`)).sort();
44537
44551
  let isComplete = false;
44538
44552
  for (let i = startIndex; i < chunkFiles.length; i++) {
44539
44553
  const file2 = chunkFiles[i];
@@ -44541,7 +44555,8 @@ function createStreamer(basedir) {
44541
44555
  if (deliveredChunkIds.has(chunkId)) {
44542
44556
  continue;
44543
44557
  }
44544
- const chunk = deserializeChunk(await readBuffer(import_node_path8.default.join(chunksDir, `${file2}.json`)));
44558
+ const ext = fileExtMap.get(file2) ?? ".bin";
44559
+ const chunk = deserializeChunk(await readBuffer(import_node_path8.default.join(chunksDir, `${file2}${ext}`)));
44545
44560
  if (chunk?.eof === true) {
44546
44561
  isComplete = true;
44547
44562
  break;
@@ -48331,14 +48346,19 @@ function getStreamType(stream) {
48331
48346
  }
48332
48347
  }
48333
48348
  __name(getStreamType, "getStreamType");
48349
+ var FRAME_HEADER_SIZE = 4;
48334
48350
  function getSerializeStream(reducers) {
48335
48351
  const encoder = new TextEncoder();
48336
48352
  const stream = new TransformStream({
48337
48353
  transform(chunk, controller) {
48338
48354
  try {
48339
48355
  const serialized = stringify(chunk, reducers);
48340
- controller.enqueue(encoder.encode(`${serialized}
48341
- `));
48356
+ const payload = encoder.encode(serialized);
48357
+ const prefixed = encodeWithFormatPrefix(SerializationFormat.DEVALUE_V1, payload);
48358
+ const frame = new Uint8Array(FRAME_HEADER_SIZE + prefixed.length);
48359
+ new DataView(frame.buffer).setUint32(0, prefixed.length, false);
48360
+ frame.set(prefixed, FRAME_HEADER_SIZE);
48361
+ controller.enqueue(frame);
48342
48362
  }
48343
48363
  catch (error45) {
48344
48364
  controller.error(new WorkflowRuntimeError(formatSerializationError("stream chunk", error45), {
@@ -48353,28 +48373,56 @@ function getSerializeStream(reducers) {
48353
48373
  __name(getSerializeStream, "getSerializeStream");
48354
48374
  function getDeserializeStream(revivers) {
48355
48375
  const decoder2 = new TextDecoder();
48356
- let buffer = "";
48376
+ let buffer = new Uint8Array(0);
48377
+ function appendToBuffer(data) {
48378
+ const newBuffer = new Uint8Array(buffer.length + data.length);
48379
+ newBuffer.set(buffer, 0);
48380
+ newBuffer.set(data, buffer.length);
48381
+ buffer = newBuffer;
48382
+ }
48383
+ __name(appendToBuffer, "appendToBuffer");
48384
+ function processFrames(controller) {
48385
+ while (buffer.length >= FRAME_HEADER_SIZE) {
48386
+ const frameLength = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength).getUint32(0, false);
48387
+ if (buffer.length < FRAME_HEADER_SIZE + frameLength) {
48388
+ break;
48389
+ }
48390
+ const frameData = buffer.slice(FRAME_HEADER_SIZE, FRAME_HEADER_SIZE + frameLength);
48391
+ buffer = buffer.slice(FRAME_HEADER_SIZE + frameLength);
48392
+ const { format, payload } = decodeFormatPrefix(frameData);
48393
+ if (format === SerializationFormat.DEVALUE_V1) {
48394
+ const text = decoder2.decode(payload);
48395
+ controller.enqueue(parse3(text, revivers));
48396
+ }
48397
+ }
48398
+ }
48399
+ __name(processFrames, "processFrames");
48357
48400
  const stream = new TransformStream({
48358
48401
  transform(chunk, controller) {
48359
- buffer += decoder2.decode(chunk, {
48360
- stream: true
48361
- });
48362
- while (true) {
48363
- const newlineIndex = buffer.indexOf("\n");
48364
- if (newlineIndex === -1)
48365
- break;
48366
- const line = buffer.slice(0, newlineIndex);
48367
- buffer = buffer.slice(newlineIndex + 1);
48402
+ if (buffer.length === 0 && chunk.length >= FRAME_HEADER_SIZE) {
48403
+ const possibleLength = new DataView(chunk.buffer, chunk.byteOffset, chunk.byteLength).getUint32(0, false);
48404
+ if (possibleLength > 0 && possibleLength < 1e8) {
48405
+ appendToBuffer(chunk);
48406
+ processFrames(controller);
48407
+ return;
48408
+ }
48409
+ }
48410
+ else if (buffer.length > 0) {
48411
+ appendToBuffer(chunk);
48412
+ processFrames(controller);
48413
+ return;
48414
+ }
48415
+ const text = decoder2.decode(chunk);
48416
+ const lines = text.split("\n");
48417
+ for (const line of lines) {
48368
48418
  if (line.length > 0) {
48369
- const obj = parse3(line, revivers);
48370
- controller.enqueue(obj);
48419
+ controller.enqueue(parse3(line, revivers));
48371
48420
  }
48372
48421
  }
48373
48422
  },
48374
48423
  flush(controller) {
48375
- if (buffer && buffer.length > 0) {
48376
- const obj = parse3(buffer, revivers);
48377
- controller.enqueue(obj);
48424
+ if (buffer.length > 0) {
48425
+ processFrames(controller);
48378
48426
  }
48379
48427
  }
48380
48428
  });
@@ -48919,10 +48967,10 @@ async function waitedUntil(fn) {
48919
48967
  }
48920
48968
  __name(waitedUntil, "waitedUntil");
48921
48969
  // ../core/dist/runtime/helpers.js
48922
- var SAFE_WORKFLOW_NAME_PATTERN = /^[a-zA-Z0-9_\-./]+$/;
48970
+ var SAFE_WORKFLOW_NAME_PATTERN = /^[a-zA-Z0-9_\-./@]+$/;
48923
48971
  function getWorkflowQueueName(workflowName) {
48924
48972
  if (!SAFE_WORKFLOW_NAME_PATTERN.test(workflowName)) {
48925
- throw new Error(`Invalid workflow name "${workflowName}": must only contain alphanumeric characters, underscores, hyphens, dots, or forward slashes`);
48973
+ throw new Error(`Invalid workflow name "${workflowName}": must only contain alphanumeric characters, underscores, hyphens, dots, forward slashes, or at signs`);
48926
48974
  }
48927
48975
  return `__wkf_workflow_${workflowName}`;
48928
48976
  }
@@ -49018,6 +49066,34 @@ function getQueueOverhead(message) {
49018
49066
  }
49019
49067
  }
49020
49068
  __name(getQueueOverhead, "getQueueOverhead");
49069
+ async function withServerErrorRetry(fn) {
49070
+ const delays = [
49071
+ 500,
49072
+ 1e3,
49073
+ 2e3
49074
+ ];
49075
+ for (let attempt = 0; attempt <= delays.length; attempt++) {
49076
+ try {
49077
+ return await fn();
49078
+ }
49079
+ catch (err) {
49080
+ if (WorkflowAPIError.is(err) && err.status !== void 0 && err.status >= 500 && attempt < delays.length) {
49081
+ runtimeLogger.warn("Server error (5xx) from workflow-server, retrying in-process", {
49082
+ status: err.status,
49083
+ attempt: attempt + 1,
49084
+ maxRetries: delays.length,
49085
+ nextDelayMs: delays[attempt],
49086
+ url: err.url
49087
+ });
49088
+ await new Promise((resolve2) => setTimeout(resolve2, delays[attempt]));
49089
+ continue;
49090
+ }
49091
+ throw err;
49092
+ }
49093
+ }
49094
+ throw new Error("withServerErrorRetry: unreachable");
49095
+ }
49096
+ __name(withServerErrorRetry, "withServerErrorRetry");
49021
49097
  // ../core/dist/runtime/resume-hook.js
49022
49098
  async function getHookByToken2(token) {
49023
49099
  const world = getWorld();
@@ -49344,11 +49420,11 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49344
49420
  });
49345
49421
  let step;
49346
49422
  try {
49347
- const startResult = await world.events.create(workflowRunId, {
49423
+ const startResult = await withServerErrorRetry(() => world.events.create(workflowRunId, {
49348
49424
  eventType: "step_started",
49349
49425
  specVersion: SPEC_VERSION_CURRENT,
49350
49426
  correlationId: stepId
49351
- });
49427
+ }));
49352
49428
  if (!startResult.step) {
49353
49429
  throw new WorkflowRuntimeError(`step_started event for "${stepId}" did not return step entity`);
49354
49430
  }
@@ -49386,7 +49462,7 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49386
49462
  "step.name": stepName,
49387
49463
  "step.id": stepId
49388
49464
  });
49389
- await queueMessage(world, `__wkf_workflow_${workflowName}`, {
49465
+ await queueMessage(world, getWorkflowQueueName(workflowName), {
49390
49466
  runId: workflowRunId,
49391
49467
  traceCarrier: await serializeTraceCarrier(),
49392
49468
  requestedAt: /* @__PURE__ */ new Date()
@@ -49518,21 +49594,21 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49518
49594
  throw err;
49519
49595
  }));
49520
49596
  const [, traceCarrier] = await Promise.all([
49521
- world.events.create(workflowRunId, {
49597
+ withServerErrorRetry(() => world.events.create(workflowRunId, {
49522
49598
  eventType: "step_completed",
49523
49599
  specVersion: SPEC_VERSION_CURRENT,
49524
49600
  correlationId: stepId,
49525
49601
  eventData: {
49526
49602
  result
49527
49603
  }
49528
- }),
49604
+ })),
49529
49605
  serializeTraceCarrier()
49530
49606
  ]);
49531
49607
  span?.setAttributes({
49532
49608
  ...StepStatus("completed"),
49533
49609
  ...StepResultType(typeof result)
49534
49610
  });
49535
- await queueMessage(world, `__wkf_workflow_${workflowName}`, {
49611
+ await queueMessage(world, getWorkflowQueueName(workflowName), {
49536
49612
  runId: workflowRunId,
49537
49613
  traceCarrier,
49538
49614
  requestedAt: /* @__PURE__ */ new Date()
@@ -49564,6 +49640,16 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49564
49640
  });
49565
49641
  return;
49566
49642
  }
49643
+ if (err.status !== void 0 && err.status >= 500) {
49644
+ runtimeLogger.warn("Persistent server error (5xx) during step, deferring to queue retry", {
49645
+ status: err.status,
49646
+ workflowRunId,
49647
+ stepId,
49648
+ error: err.message,
49649
+ url: err.url
49650
+ });
49651
+ throw err;
49652
+ }
49567
49653
  }
49568
49654
  if (isFatal) {
49569
49655
  stepLogger.error("Encountered FatalError while executing step, bubbling up to parent workflow", {
@@ -49571,7 +49657,7 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49571
49657
  stepName,
49572
49658
  errorStack: normalizedStack
49573
49659
  });
49574
- await world.events.create(workflowRunId, {
49660
+ await withServerErrorRetry(() => world.events.create(workflowRunId, {
49575
49661
  eventType: "step_failed",
49576
49662
  specVersion: SPEC_VERSION_CURRENT,
49577
49663
  correlationId: stepId,
@@ -49579,7 +49665,7 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49579
49665
  error: normalizedError.message,
49580
49666
  stack: normalizedStack
49581
49667
  }
49582
- });
49668
+ }));
49583
49669
  span?.setAttributes({
49584
49670
  ...StepStatus("failed"),
49585
49671
  ...StepFatalError(true)
@@ -49602,7 +49688,7 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49602
49688
  errorStack: normalizedStack
49603
49689
  });
49604
49690
  const errorMessage = `Step "${stepName}" failed after ${maxRetries2} ${pluralize("retry", "retries", maxRetries2)}: ${normalizedError.message}`;
49605
- await world.events.create(workflowRunId, {
49691
+ await withServerErrorRetry(() => world.events.create(workflowRunId, {
49606
49692
  eventType: "step_failed",
49607
49693
  specVersion: SPEC_VERSION_CURRENT,
49608
49694
  correlationId: stepId,
@@ -49610,7 +49696,7 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49610
49696
  error: errorMessage,
49611
49697
  stack: normalizedStack
49612
49698
  }
49613
- });
49699
+ }));
49614
49700
  span?.setAttributes({
49615
49701
  ...StepStatus("failed"),
49616
49702
  ...StepRetryExhausted(true)
@@ -49633,7 +49719,7 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49633
49719
  errorStack: normalizedStack
49634
49720
  });
49635
49721
  }
49636
- await world.events.create(workflowRunId, {
49722
+ await withServerErrorRetry(() => world.events.create(workflowRunId, {
49637
49723
  eventType: "step_retrying",
49638
49724
  specVersion: SPEC_VERSION_CURRENT,
49639
49725
  correlationId: stepId,
@@ -49644,7 +49730,7 @@ var stepHandler = getWorldHandlers().createQueueHandler("__wkf_step_", async (me
49644
49730
  retryAfter: err.retryAfter
49645
49731
  }
49646
49732
  }
49647
- });
49733
+ }));
49648
49734
  const timeoutSeconds = Math.max(1, RetryableError.is(err) ? Math.ceil((+err.retryAfter.getTime() - Date.now()) / 1e3) : 1);
49649
49735
  span?.setAttributes({
49650
49736
  ...StepRetryTimeoutSeconds(timeoutSeconds),