@workflow/world-testing 4.1.16 → 4.1.18

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.
@@ -50439,7 +50439,7 @@ init_dist();
50439
50439
  init_node();
50440
50440
 
50441
50441
  // ../core/dist/version.js
50442
- var version2 = "4.8.1";
50442
+ var version2 = "4.8.3";
50443
50443
 
50444
50444
  // ../core/dist/runtime/world.js
50445
50445
  var import_node_module = require("node:module");
@@ -64035,30 +64035,74 @@ function getStepFunction(stepId) {
64035
64035
  return void 0;
64036
64036
  }
64037
64037
  __name(getStepFunction, "getStepFunction");
64038
- async function awaitEarlierDeliveries(ctx, eventIndex, deferBehindKinds) {
64038
+ var DEFER_BEHIND = {
64039
+ hook: ["wait", "step"],
64040
+ wait: ["hook", "step"],
64041
+ step: ["wait", "hook", "step"]
64042
+ };
64043
+ function resolvesOnItsOwn(barriers, index, entry, memo) {
64044
+ const cached2 = memo.get(index);
64045
+ if (cached2 !== void 0) {
64046
+ return cached2;
64047
+ }
64048
+ const result = computeResolvesOnItsOwn(barriers, index, entry, memo);
64049
+ memo.set(index, result);
64050
+ return result;
64051
+ }
64052
+ __name(resolvesOnItsOwn, "resolvesOnItsOwn");
64053
+ function computeResolvesOnItsOwn(barriers, index, entry, memo) {
64054
+ if (!entry.armed) {
64055
+ return false;
64056
+ }
64057
+ if (entry.kind === "step") {
64058
+ return true;
64059
+ }
64060
+ const deferBehind = DEFER_BEHIND[entry.kind];
64061
+ for (const [otherIndex, other] of barriers) {
64062
+ if (otherIndex < index && deferBehind.includes(other.kind) && !resolvesOnItsOwn(barriers, otherIndex, other, memo)) {
64063
+ return false;
64064
+ }
64065
+ }
64066
+ return true;
64067
+ }
64068
+ __name(computeResolvesOnItsOwn, "computeResolvesOnItsOwn");
64069
+ async function awaitEarlierDeliveries(ctx, eventIndex, kind) {
64039
64070
  if (eventIndex === void 0 || !ctx.pendingDeliveryBarriers || ctx.pendingDeliveryBarriers.size === 0) {
64040
64071
  return;
64041
64072
  }
64073
+ const barriers = ctx.pendingDeliveryBarriers;
64074
+ const deferBehind = DEFER_BEHIND[kind];
64042
64075
  const earlier = [];
64043
- for (const [index, entry] of ctx.pendingDeliveryBarriers) {
64044
- if (index < eventIndex && deferBehindKinds.includes(entry.kind)) {
64045
- earlier.push(entry.delivered);
64076
+ const selfResolving = /* @__PURE__ */ new Map();
64077
+ for (const [index, entry] of barriers) {
64078
+ if (index >= eventIndex || !deferBehind.includes(entry.kind)) {
64079
+ continue;
64046
64080
  }
64081
+ if (kind === "step" && !resolvesOnItsOwn(barriers, index, entry, selfResolving)) {
64082
+ continue;
64083
+ }
64084
+ earlier.push(entry.delivered);
64047
64085
  }
64048
64086
  if (earlier.length > 0) {
64049
64087
  await Promise.all(earlier);
64088
+ await new Promise((resolve3) => setTimeout(resolve3, 0));
64050
64089
  }
64051
64090
  }
64052
64091
  __name(awaitEarlierDeliveries, "awaitEarlierDeliveries");
64053
- function registerDeliveryBarrier(ctx, eventIndex, kind) {
64092
+ function registerDeliveryBarrier(ctx, eventIndex, kind, options = {}) {
64054
64093
  const barriers = ctx.pendingDeliveryBarriers;
64055
64094
  if (!barriers || eventIndex === void 0) {
64056
64095
  return { markDelivered: /* @__PURE__ */ __name(() => {
64057
- }, "markDelivered") };
64096
+ }, "markDelivered"), arm: /* @__PURE__ */ __name(() => {
64097
+ }, "arm") };
64058
64098
  }
64059
64099
  let done = false;
64060
64100
  const { promise: promise2, resolve: resolve3 } = withResolvers();
64061
- const entry = { kind, delivered: promise2 };
64101
+ const entry = {
64102
+ kind,
64103
+ delivered: promise2,
64104
+ armed: options.armed ?? true
64105
+ };
64062
64106
  barriers.set(eventIndex, entry);
64063
64107
  const finish = /* @__PURE__ */ __name(() => {
64064
64108
  if (done) {
@@ -64071,12 +64115,35 @@ function registerDeliveryBarrier(ctx, eventIndex, kind) {
64071
64115
  resolve3();
64072
64116
  }, "finish");
64073
64117
  scheduleWhenIdle(ctx, finish);
64074
- return { markDelivered: finish };
64118
+ return {
64119
+ markDelivered: finish,
64120
+ arm: /* @__PURE__ */ __name(() => {
64121
+ entry.armed = true;
64122
+ }, "arm")
64123
+ };
64075
64124
  }
64076
64125
  __name(registerDeliveryBarrier, "registerDeliveryBarrier");
64126
+ function hasParkedCommittedDelivery(ctx) {
64127
+ const barriers = ctx.pendingDeliveryBarriers;
64128
+ if (!barriers || barriers.size === 0) {
64129
+ return false;
64130
+ }
64131
+ const selfResolving = /* @__PURE__ */ new Map();
64132
+ for (const [index, entry] of barriers) {
64133
+ if (resolvesOnItsOwn(barriers, index, entry, selfResolving)) {
64134
+ return true;
64135
+ }
64136
+ }
64137
+ return false;
64138
+ }
64139
+ __name(hasParkedCommittedDelivery, "hasParkedCommittedDelivery");
64140
+ function isDeliveryIdle(ctx) {
64141
+ return ctx.pendingDeliveries === 0 && !hasParkedCommittedDelivery(ctx);
64142
+ }
64143
+ __name(isDeliveryIdle, "isDeliveryIdle");
64077
64144
  function scheduleWhenIdle(ctx, fn) {
64078
64145
  const check2 = /* @__PURE__ */ __name(() => {
64079
- if (ctx.pendingDeliveries > 0) {
64146
+ if (!isDeliveryIdle(ctx)) {
64080
64147
  ctx.promiseQueue.then(() => {
64081
64148
  setTimeout(check2, 0);
64082
64149
  });
@@ -66167,6 +66234,7 @@ __name(customRandom, "customRandom");
66167
66234
  init_node();
66168
66235
 
66169
66236
  // ../core/dist/events-consumer.js
66237
+ var DEFERRED_CHECK_DELAY_MS = 100;
66170
66238
  var EventConsumerResult;
66171
66239
  (function(EventConsumerResult2) {
66172
66240
  EventConsumerResult2[EventConsumerResult2["Consumed"] = 0] = "Consumed";
@@ -66183,6 +66251,7 @@ var EventsConsumer = class {
66183
66251
  onConsumedEvent;
66184
66252
  onUnconsumedEvent;
66185
66253
  getPromiseQueue;
66254
+ isDeliveryIdle;
66186
66255
  pendingUnconsumedCheck = null;
66187
66256
  pendingUnconsumedTimeout = null;
66188
66257
  unconsumedCheckVersion = 0;
@@ -66192,6 +66261,7 @@ var EventsConsumer = class {
66192
66261
  this.onConsumedEvent = options.onConsumedEvent;
66193
66262
  this.onUnconsumedEvent = options.onUnconsumedEvent;
66194
66263
  this.getPromiseQueue = options.getPromiseQueue;
66264
+ this.isDeliveryIdle = options.isDeliveryIdle ?? (() => true);
66195
66265
  }
66196
66266
  /**
66197
66267
  * Registers a callback function to be called after an event has been consumed
@@ -66275,16 +66345,58 @@ var EventsConsumer = class {
66275
66345
  // resumes -> the next hook payload starts hydrating).
66276
66346
  () => new Promise((resolve3) => setTimeout(resolve3, 0))
66277
66347
  ).then(() => this.getPromiseQueue()).then(() => {
66278
- this.pendingUnconsumedTimeout = setTimeout(() => {
66279
- this.pendingUnconsumedTimeout = null;
66280
- if (this.unconsumedCheckVersion === checkVersion) {
66281
- this.pendingUnconsumedCheck = null;
66282
- this.onUnconsumedEvent(currentEvent);
66283
- }
66284
- }, 100);
66348
+ this.whenDeliveryIdle(checkVersion, () => {
66349
+ this.pendingUnconsumedTimeout = setTimeout(() => {
66350
+ this.pendingUnconsumedTimeout = null;
66351
+ if (this.unconsumedCheckVersion === checkVersion) {
66352
+ this.pendingUnconsumedCheck = null;
66353
+ this.onUnconsumedEvent(currentEvent);
66354
+ }
66355
+ }, DEFERRED_CHECK_DELAY_MS);
66356
+ });
66285
66357
  });
66286
66358
  }
66287
66359
  }
66360
+ /**
66361
+ * Run `fn` once no data delivery is in flight, polling the way
66362
+ * `scheduleWhenIdle` does: let the promise queue drain, re-check a timer
66363
+ * tick later, repeat.
66364
+ *
66365
+ * Without this the check is a bet that every delivery the walk is running
66366
+ * ahead of lands inside a fixed window. Consumption is synchronous while the
66367
+ * resolution it triggers is not: a step result hydrates in the host, resolves
66368
+ * from a detached continuation behind `awaitEarlierDeliveries`, and only then
66369
+ * does VM code run far enough to subscribe the next consumer. Replaying a
66370
+ * batch of N parallel step results leaves N-1 of them on that detached path
66371
+ * with the queue already drained, so the walk sits on the ordered event the
66372
+ * VM is about to draw and the window is the only thing standing between a
66373
+ * healthy run and `ReplayDivergenceError`. On a backend whose deliveries take
66374
+ * longer than the window, that bet loses.
66375
+ *
66376
+ * Termination is `hasParkedCommittedDelivery`'s: it counts only deliveries
66377
+ * that resolve on their own, so nothing here can gate its own retirement. A
66378
+ * genuinely orphaned event has no delivery to wait on and reaches `fn` on the
66379
+ * first poll.
66380
+ */
66381
+ whenDeliveryIdle(checkVersion, fn) {
66382
+ const poll = /* @__PURE__ */ __name(() => {
66383
+ this.pendingUnconsumedTimeout = null;
66384
+ if (this.unconsumedCheckVersion !== checkVersion) {
66385
+ return;
66386
+ }
66387
+ if (this.isDeliveryIdle()) {
66388
+ fn();
66389
+ return;
66390
+ }
66391
+ this.getPromiseQueue().then(() => {
66392
+ if (this.unconsumedCheckVersion !== checkVersion) {
66393
+ return;
66394
+ }
66395
+ this.pendingUnconsumedTimeout = setTimeout(poll, 0);
66396
+ });
66397
+ }, "poll");
66398
+ poll();
66399
+ }
66288
66400
  };
66289
66401
 
66290
66402
  // ../core/dist/runtime/get-port-lazy.js
@@ -66411,6 +66523,9 @@ function createUseStep(ctx) {
66411
66523
  }
66412
66524
  if (event.eventType === "step_failed") {
66413
66525
  ctx.invocationsQueue.delete(event.correlationId);
66526
+ const eventIndex = ctx.eventsConsumer.eventIndex;
66527
+ const barrier = registerDeliveryBarrier(ctx, eventIndex, "step");
66528
+ const earlierDelivered = awaitEarlierDeliveries(ctx, eventIndex, "step");
66414
66529
  ctx.promiseQueue = ctx.promiseQueue.then(() => {
66415
66530
  const errorData = event.eventData.error;
66416
66531
  const isErrorObject = typeof errorData === "object" && errorData !== null;
@@ -66420,7 +66535,10 @@ function createUseStep(ctx) {
66420
66535
  if (errorStack) {
66421
66536
  error48.stack = errorStack;
66422
66537
  }
66423
- reject(error48);
66538
+ void earlierDelivered.then(() => {
66539
+ barrier.markDelivered();
66540
+ reject(error48);
66541
+ });
66424
66542
  });
66425
66543
  return EventConsumerResult.Finished;
66426
66544
  }
@@ -66428,16 +66546,28 @@ function createUseStep(ctx) {
66428
66546
  ctx.invocationsQueue.delete(event.correlationId);
66429
66547
  const completedEventId = event.eventId;
66430
66548
  const serializedResult = event.eventData.result;
66549
+ const eventIndex = ctx.eventsConsumer.eventIndex;
66550
+ const barrier = registerDeliveryBarrier(ctx, eventIndex, "step");
66551
+ let outcome;
66552
+ const earlierDelivered = awaitEarlierDeliveries(ctx, eventIndex, "step");
66431
66553
  ctx.pendingDeliveries++;
66432
66554
  ctx.promiseQueue = ctx.promiseQueue.then(async () => {
66433
66555
  try {
66434
66556
  const hydratedResult = await getOrHydrateStepReturnValue(ctx.stepHydrationCache, completedEventId, () => hydrateStepReturnValue(serializedResult, ctx.runId, ctx.encryptionKey, ctx.globalThis));
66435
- resolve3(hydratedResult);
66557
+ outcome = { ok: true, value: hydratedResult };
66436
66558
  } catch (error48) {
66437
- reject(error48);
66559
+ outcome = { ok: false, error: error48 };
66438
66560
  } finally {
66439
66561
  ctx.pendingDeliveries--;
66440
66562
  }
66563
+ void earlierDelivered.then(() => {
66564
+ barrier.markDelivered();
66565
+ if (outcome.ok) {
66566
+ resolve3(outcome.value);
66567
+ } else {
66568
+ reject(outcome.error);
66569
+ }
66570
+ });
66441
66571
  });
66442
66572
  return EventConsumerResult.Finished;
66443
66573
  }
@@ -66911,10 +67041,10 @@ function createContext(options) {
66911
67041
  g.Math.random = rng;
66912
67042
  const Date_ = g.Date;
66913
67043
  g.Date = /* @__PURE__ */ __name(function Date2(...args) {
66914
- if (args.length === 0) {
66915
- return new Date_(fixedTimestamp);
67044
+ if (new.target === void 0) {
67045
+ return new Date_(fixedTimestamp).toString();
66916
67046
  }
66917
- return new Date_(...args);
67047
+ return Reflect.construct(Date_, args.length === 0 ? [fixedTimestamp] : args, new.target);
66918
67048
  }, "Date");
66919
67049
  g.Date.prototype = Date_.prototype;
66920
67050
  Object.setPrototypeOf(g.Date, Date_);
@@ -67118,8 +67248,12 @@ function createCreateHook(ctx) {
67118
67248
  }
67119
67249
  if (event.eventType === "hook_received") {
67120
67250
  const eventIndex = ctx.eventsConsumer.eventIndex;
67121
- const barrier = registerDeliveryBarrier(ctx, eventIndex, "hook");
67122
- if (promises.length > 0) {
67251
+ const hasWaitingConsumer = promises.length > 0;
67252
+ const barrier = registerDeliveryBarrier(ctx, eventIndex, "hook", {
67253
+ armed: hasWaitingConsumer
67254
+ });
67255
+ if (hasWaitingConsumer) {
67256
+ const earlierDelivered = awaitEarlierDeliveries(ctx, eventIndex, "hook");
67123
67257
  const next2 = promises.shift();
67124
67258
  if (next2) {
67125
67259
  ctx.pendingDeliveries++;
@@ -67133,7 +67267,7 @@ function createCreateHook(ctx) {
67133
67267
  } finally {
67134
67268
  ctx.pendingDeliveries--;
67135
67269
  }
67136
- void awaitEarlierDeliveries(ctx, eventIndex, ["wait"]).then(() => {
67270
+ void earlierDelivered.then(() => {
67137
67271
  barrier.markDelivered();
67138
67272
  if (hydrateOutcome.ok) {
67139
67273
  next2.resolve(hydrateOutcome.value);
@@ -67146,13 +67280,16 @@ function createCreateHook(ctx) {
67146
67280
  } else {
67147
67281
  let outcome;
67148
67282
  const hydrated = withResolvers();
67149
- const claim = /* @__PURE__ */ __name(() => hydrated.promise.then(() => awaitEarlierDeliveries(ctx, eventIndex, ["wait"])).then(() => {
67150
- barrier.markDelivered();
67151
- if (outcome && !outcome.ok) {
67152
- throw outcome.error;
67153
- }
67154
- return outcome.value;
67155
- }), "claim");
67283
+ const claim = /* @__PURE__ */ __name(() => {
67284
+ barrier.arm();
67285
+ return hydrated.promise.then(() => awaitEarlierDeliveries(ctx, eventIndex, "hook")).then(() => {
67286
+ barrier.markDelivered();
67287
+ if (outcome && !outcome.ok) {
67288
+ throw outcome.error;
67289
+ }
67290
+ return outcome.value;
67291
+ });
67292
+ }, "claim");
67156
67293
  ctx.pendingDeliveries++;
67157
67294
  ctx.promiseQueue = ctx.promiseQueue.then(async () => {
67158
67295
  try {
@@ -67331,8 +67468,9 @@ function createSleep(ctx) {
67331
67468
  ctx.invocationsQueue.delete(correlationId);
67332
67469
  const eventIndex = ctx.eventsConsumer.eventIndex;
67333
67470
  const barrier = registerDeliveryBarrier(ctx, eventIndex, "wait");
67471
+ const earlierDelivered = awaitEarlierDeliveries(ctx, eventIndex, "wait");
67334
67472
  const queueAtCompletion = ctx.promiseQueue;
67335
- void queueAtCompletion.then(() => awaitEarlierDeliveries(ctx, eventIndex, ["hook"])).then(() => {
67473
+ void queueAtCompletion.then(() => earlierDelivered).then(() => {
67336
67474
  barrier.markDelivered();
67337
67475
  resolve3();
67338
67476
  });
@@ -67390,6 +67528,7 @@ async function runWorkflow(workflowCode2, workflowRun, events, encryptionKey, st
67390
67528
  const ulid5 = monotonicFactory(() => vmGlobalThis.Math.random());
67391
67529
  const generateNanoid = customRandom(urlAlphabet, 21, (size) => new Uint8Array(size).map(() => 256 * vmGlobalThis.Math.random()));
67392
67530
  const promiseQueueHolder = { current: Promise.resolve() };
67531
+ const deliveryIdleHolder = { current: /* @__PURE__ */ __name(() => true, "current") };
67393
67532
  const eventsConsumer = new EventsConsumer(events, {
67394
67533
  onConsumedEvent: /* @__PURE__ */ __name((event) => {
67395
67534
  updateTimestamp(+event.createdAt);
@@ -67397,7 +67536,8 @@ async function runWorkflow(workflowCode2, workflowRun, events, encryptionKey, st
67397
67536
  onUnconsumedEvent: /* @__PURE__ */ __name((event) => {
67398
67537
  workflowDiscontinuation.reject(new ReplayDivergenceError(`Replay could not consume event: eventType=${event.eventType}, correlationId=${event.correlationId}, eventId=${event.eventId}.`, { eventId: event.eventId }));
67399
67538
  }, "onUnconsumedEvent"),
67400
- getPromiseQueue: /* @__PURE__ */ __name(() => promiseQueueHolder.current, "getPromiseQueue")
67539
+ getPromiseQueue: /* @__PURE__ */ __name(() => promiseQueueHolder.current, "getPromiseQueue"),
67540
+ isDeliveryIdle: /* @__PURE__ */ __name(() => deliveryIdleHolder.current(), "isDeliveryIdle")
67401
67541
  });
67402
67542
  const workflowContext = {
67403
67543
  runId: workflowRun.runId,
@@ -67420,6 +67560,7 @@ async function runWorkflow(workflowCode2, workflowRun, events, encryptionKey, st
67420
67560
  pendingDeliveryBarriers: /* @__PURE__ */ new Map(),
67421
67561
  stepHydrationCache
67422
67562
  };
67563
+ deliveryIdleHolder.current = () => isDeliveryIdle(workflowContext);
67423
67564
  workflowContext.eventsConsumer.subscribe((event) => {
67424
67565
  if (!event) {
67425
67566
  return EventConsumerResult.NotConsumed;