@proompteng/temporal-bun-sdk 0.2.0 → 0.3.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 (60) hide show
  1. package/README.md +95 -0
  2. package/dist/src/client/serialization.d.ts +30 -1
  3. package/dist/src/client/serialization.d.ts.map +1 -1
  4. package/dist/src/client/serialization.js +76 -3
  5. package/dist/src/client/serialization.js.map +1 -1
  6. package/dist/src/client/types.d.ts +26 -0
  7. package/dist/src/client/types.d.ts.map +1 -1
  8. package/dist/src/client.d.ts +10 -2
  9. package/dist/src/client.d.ts.map +1 -1
  10. package/dist/src/client.js +243 -1
  11. package/dist/src/client.js.map +1 -1
  12. package/dist/src/index.d.ts +1 -1
  13. package/dist/src/index.d.ts.map +1 -1
  14. package/dist/src/index.js.map +1 -1
  15. package/dist/src/worker/concurrency.d.ts +8 -0
  16. package/dist/src/worker/concurrency.d.ts.map +1 -1
  17. package/dist/src/worker/concurrency.js +5 -9
  18. package/dist/src/worker/concurrency.js.map +1 -1
  19. package/dist/src/worker/runtime.d.ts.map +1 -1
  20. package/dist/src/worker/runtime.js +303 -29
  21. package/dist/src/worker/runtime.js.map +1 -1
  22. package/dist/src/worker/update-protocol.d.ts +33 -0
  23. package/dist/src/worker/update-protocol.d.ts.map +1 -0
  24. package/dist/src/worker/update-protocol.js +228 -0
  25. package/dist/src/worker/update-protocol.js.map +1 -0
  26. package/dist/src/workflow/context.d.ts +52 -1
  27. package/dist/src/workflow/context.d.ts.map +1 -1
  28. package/dist/src/workflow/context.js +212 -2
  29. package/dist/src/workflow/context.js.map +1 -1
  30. package/dist/src/workflow/definition.d.ts +29 -3
  31. package/dist/src/workflow/definition.d.ts.map +1 -1
  32. package/dist/src/workflow/definition.js +30 -4
  33. package/dist/src/workflow/definition.js.map +1 -1
  34. package/dist/src/workflow/determinism.d.ts +59 -0
  35. package/dist/src/workflow/determinism.d.ts.map +1 -1
  36. package/dist/src/workflow/determinism.js +76 -1
  37. package/dist/src/workflow/determinism.js.map +1 -1
  38. package/dist/src/workflow/errors.d.ts +3 -0
  39. package/dist/src/workflow/errors.d.ts.map +1 -1
  40. package/dist/src/workflow/errors.js +6 -0
  41. package/dist/src/workflow/errors.js.map +1 -1
  42. package/dist/src/workflow/executor.d.ts +53 -0
  43. package/dist/src/workflow/executor.d.ts.map +1 -1
  44. package/dist/src/workflow/executor.js +237 -6
  45. package/dist/src/workflow/executor.js.map +1 -1
  46. package/dist/src/workflow/inbound.d.ts +84 -0
  47. package/dist/src/workflow/inbound.d.ts.map +1 -0
  48. package/dist/src/workflow/inbound.js +65 -0
  49. package/dist/src/workflow/inbound.js.map +1 -0
  50. package/dist/src/workflow/index.d.ts +1 -0
  51. package/dist/src/workflow/index.d.ts.map +1 -1
  52. package/dist/src/workflow/index.js +1 -0
  53. package/dist/src/workflow/index.js.map +1 -1
  54. package/dist/src/workflow/replay.d.ts +24 -2
  55. package/dist/src/workflow/replay.d.ts.map +1 -1
  56. package/dist/src/workflow/replay.js +459 -14
  57. package/dist/src/workflow/replay.js.map +1 -1
  58. package/dist/src/workflows/index.d.ts +1 -1
  59. package/dist/src/workflows/index.d.ts.map +1 -1
  60. package/package.json +1 -1
@@ -18,9 +18,10 @@ import { CommandType } from '../proto/temporal/api/enums/v1/command_type_pb';
18
18
  import { WorkerVersioningMode } from '../proto/temporal/api/enums/v1/deployment_pb';
19
19
  import { EventType } from '../proto/temporal/api/enums/v1/event_type_pb';
20
20
  import { WorkflowTaskFailedCause } from '../proto/temporal/api/enums/v1/failed_cause_pb';
21
+ import { QueryResultType } from '../proto/temporal/api/enums/v1/query_pb';
21
22
  import { HistoryEventFilterType, TimeoutType, VersioningBehavior } from '../proto/temporal/api/enums/v1/workflow_pb';
22
23
  import { StickyExecutionAttributesSchema, TaskQueueSchema, } from '../proto/temporal/api/taskqueue/v1/message_pb';
23
- import { GetWorkflowExecutionHistoryRequestSchema, PollActivityTaskQueueRequestSchema, PollWorkflowTaskQueueRequestSchema, RespondActivityTaskCanceledRequestSchema, RespondActivityTaskCompletedRequestSchema, RespondActivityTaskFailedRequestSchema, RespondWorkflowTaskCompletedRequestSchema, RespondWorkflowTaskFailedRequestSchema, } from '../proto/temporal/api/workflowservice/v1/request_response_pb';
24
+ import { GetWorkflowExecutionHistoryRequestSchema, PollActivityTaskQueueRequestSchema, PollWorkflowTaskQueueRequestSchema, RespondActivityTaskCanceledRequestSchema, RespondActivityTaskCompletedRequestSchema, RespondActivityTaskFailedRequestSchema, RespondQueryTaskCompletedRequestSchema, RespondWorkflowTaskCompletedRequestSchema, RespondWorkflowTaskFailedRequestSchema, } from '../proto/temporal/api/workflowservice/v1/request_response_pb';
24
25
  import { WorkflowService } from '../proto/temporal/api/workflowservice/v1/service_pb';
25
26
  import { WorkflowNondeterminismError } from '../workflow/errors';
26
27
  import { WorkflowExecutor } from '../workflow/executor';
@@ -30,6 +31,24 @@ import { runWithActivityContext } from './activity-context';
30
31
  import { checkWorkerVersioningCapability, registerWorkerBuildIdCompatibility } from './build-id';
31
32
  import { makeWorkerScheduler, } from './concurrency';
32
33
  import { makeStickyCache, } from './sticky-cache';
34
+ import { buildUpdateProtocolMessages, collectWorkflowUpdates } from './update-protocol';
35
+ const mergeUpdateInvocations = (historyInvocations, messageInvocations) => {
36
+ const merged = [];
37
+ const seen = new Set();
38
+ for (const invocation of historyInvocations ?? []) {
39
+ if (!seen.has(invocation.updateId)) {
40
+ merged.push(invocation);
41
+ seen.add(invocation.updateId);
42
+ }
43
+ }
44
+ for (const invocation of messageInvocations ?? []) {
45
+ if (!seen.has(invocation.updateId)) {
46
+ merged.push(invocation);
47
+ seen.add(invocation.updateId);
48
+ }
49
+ }
50
+ return merged;
51
+ };
33
52
  const POLL_TIMEOUT_MS = 60_000;
34
53
  const RESPOND_TIMEOUT_MS = 15_000;
35
54
  const HISTORY_FETCH_TIMEOUT_MS = 60_000;
@@ -98,6 +117,12 @@ export class WorkerRuntime {
98
117
  activityConcurrency,
99
118
  hooks: options.schedulerHooks,
100
119
  logger,
120
+ metrics: {
121
+ workflowTaskStarted: runtimeMetrics.workflowTaskStarted,
122
+ workflowTaskCompleted: runtimeMetrics.workflowTaskCompleted,
123
+ activityTaskStarted: runtimeMetrics.activityTaskStarted,
124
+ activityTaskCompleted: runtimeMetrics.activityTaskCompleted,
125
+ },
101
126
  }));
102
127
  const stickyCacheCandidate = options.stickyCache;
103
128
  const hasStickyCacheInstance = WorkerRuntime.#isStickyCacheInstance(stickyCacheCandidate);
@@ -267,7 +292,11 @@ export class WorkerRuntime {
267
292
  if (!state) {
268
293
  return false;
269
294
  }
270
- return state.commandHistory.length > 0 || state.randomValues.length > 0 || state.timeValues.length > 0;
295
+ return (state.commandHistory.length > 0 ||
296
+ state.randomValues.length > 0 ||
297
+ state.timeValues.length > 0 ||
298
+ (state.signals?.length ?? 0) > 0 ||
299
+ (state.queries?.length ?? 0) > 0);
271
300
  }
272
301
  #resolvePreviousHistoryEventId(response) {
273
302
  const previous = response.previousStartedEventId;
@@ -320,6 +349,10 @@ export class WorkerRuntime {
320
349
  heartbeatFailures: await makeCounter('temporal_worker_heartbeat_failures_total', 'Activity heartbeat failures'),
321
350
  activityFailures: await makeCounter('temporal_worker_activity_failures_total', 'Activity failures delivered to Temporal'),
322
351
  workflowFailures: await makeCounter('temporal_worker_workflow_failures_total', 'Workflow failure responses sent to Temporal'),
352
+ workflowTaskStarted: await makeCounter('temporal_worker_workflow_tasks_started_total', 'Workflow tasks dispatched to the scheduler'),
353
+ workflowTaskCompleted: await makeCounter('temporal_worker_workflow_tasks_completed_total', 'Workflow tasks completed by the scheduler'),
354
+ activityTaskStarted: await makeCounter('temporal_worker_activity_tasks_started_total', 'Activity tasks dispatched to the scheduler'),
355
+ activityTaskCompleted: await makeCounter('temporal_worker_activity_tasks_completed_total', 'Activity tasks completed by the scheduler'),
323
356
  };
324
357
  }
325
358
  async run() {
@@ -519,15 +552,45 @@ export class WorkerRuntime {
519
552
  const workflowType = this.#resolveWorkflowType(response, historyEvents);
520
553
  const args = await this.#decodeWorkflowArgs(historyEvents);
521
554
  const workflowInfo = this.#buildWorkflowInfo(workflowType, execution);
522
- const stickyKey = this.#buildStickyKey(execution.workflowId, execution.runId);
523
- const stickyEntry = stickyKey ? await this.#getStickyEntry(stickyKey) : undefined;
524
- const historyReplay = await this.#ingestDeterminismState(workflowInfo, historyEvents);
525
- const hasHistorySnapshot = this.#isValidDeterminismSnapshot(historyReplay?.determinismState);
555
+ const collectedUpdates = await collectWorkflowUpdates({
556
+ messages: response.messages ?? [],
557
+ dataConverter: this.#dataConverter,
558
+ log: (level, message, fields) => this.#log(level, message, fields),
559
+ });
526
560
  const baseLogFields = this.#workflowLogFields(execution, workflowType, {
527
561
  workflowTaskAttempt,
528
562
  stickyScheduling: this.#stickySchedulingEnabled,
529
563
  nondeterminismRetry,
530
564
  });
565
+ const signalDeliveries = await this.#extractSignalDeliveries(historyEvents);
566
+ if (signalDeliveries.length > 0) {
567
+ this.#log('debug', 'workflow signal deliveries buffered', {
568
+ ...baseLogFields,
569
+ signalCount: signalDeliveries.length,
570
+ });
571
+ }
572
+ const queryRequests = await this.#extractWorkflowQueryRequests(response);
573
+ const hasQueryRequests = queryRequests.length > 0;
574
+ const hasMultiQueries = queryRequests.some((request) => request.source === 'multi');
575
+ const hasLegacyQueries = queryRequests.some((request) => request.source === 'legacy');
576
+ if (hasQueryRequests) {
577
+ this.#log('debug', 'workflow queries pending evaluation', {
578
+ ...baseLogFields,
579
+ queryCount: queryRequests.length,
580
+ });
581
+ }
582
+ this.#log('info', 'debug: workflow task metadata', {
583
+ ...baseLogFields,
584
+ taskTokenBytes: response.taskToken?.length ?? 0,
585
+ queryCount: queryRequests.length,
586
+ historyEventCount: response.history?.events?.length ?? 0,
587
+ });
588
+ const stickyKey = this.#buildStickyKey(execution.workflowId, execution.runId);
589
+ const stickyEntry = stickyKey ? await this.#getStickyEntry(stickyKey) : undefined;
590
+ const historyReplay = await this.#ingestDeterminismState(workflowInfo, historyEvents, {
591
+ queryRequests,
592
+ });
593
+ const hasHistorySnapshot = this.#isValidDeterminismSnapshot(historyReplay?.determinismState);
531
594
  let previousState;
532
595
  if (stickyEntry && this.#isValidDeterminismSnapshot(stickyEntry.determinismState)) {
533
596
  const historyBaselineEventId = this.#resolvePreviousHistoryEventId(response) ?? historyReplay?.lastEventId ?? null;
@@ -575,6 +638,8 @@ export class WorkerRuntime {
575
638
  const expectedDeterminismState = previousState;
576
639
  try {
577
640
  const activityResults = await this.#extractActivityResolutions(historyEvents);
641
+ const replayUpdates = historyReplay?.updates ?? [];
642
+ const mergedUpdates = mergeUpdateInvocations(replayUpdates, collectedUpdates.invocations);
578
643
  const output = await this.#executor.execute({
579
644
  workflowType,
580
645
  workflowId: execution.workflowId,
@@ -584,10 +649,49 @@ export class WorkerRuntime {
584
649
  arguments: args,
585
650
  determinismState: previousState,
586
651
  activityResults,
652
+ signalDeliveries,
653
+ queryRequests,
654
+ updates: mergedUpdates,
655
+ });
656
+ this.#log('debug', 'workflow query evaluation summary', {
657
+ ...baseLogFields,
658
+ queryResultCount: output.queryResults.length,
659
+ });
660
+ this.#log('debug', 'workflow query results raw', {
661
+ ...baseLogFields,
662
+ queryResults: output.queryResults.map((entry) => ({
663
+ name: entry.request.name,
664
+ source: entry.request.source,
665
+ id: entry.request.id ?? null,
666
+ })),
587
667
  });
668
+ const multiQueryResults = {};
669
+ let legacyQueryResult;
670
+ for (const entry of output.queryResults) {
671
+ this.#log('debug', 'workflow query evaluation completed', {
672
+ ...baseLogFields,
673
+ querySource: entry.request.source,
674
+ queryName: entry.request.name,
675
+ queryId: entry.request.id ?? null,
676
+ });
677
+ if (entry.request.source === 'multi' && entry.request.id) {
678
+ multiQueryResults[entry.request.id] = entry.result;
679
+ }
680
+ else if (entry.request.source === 'legacy') {
681
+ legacyQueryResult = entry;
682
+ }
683
+ }
588
684
  const cacheBaselineEventId = this.#resolveCurrentStartedEventId(response) ?? historyReplay?.lastEventId ?? null;
589
685
  const shouldRecordMarker = output.completion === 'pending';
590
686
  let commandsForResponse = output.commands;
687
+ const dispatchesForNewMessages = (output.updateDispatches ?? []).filter((dispatch) => collectedUpdates.requestsByUpdateId.has(dispatch.updateId));
688
+ const updateProtocolMessages = await buildUpdateProtocolMessages({
689
+ dispatches: dispatchesForNewMessages,
690
+ collected: collectedUpdates,
691
+ dataConverter: this.#dataConverter,
692
+ defaultIdentity: this.#identity,
693
+ log: (level, message, fields) => this.#log(level, message, fields),
694
+ });
591
695
  if (stickyKey) {
592
696
  if (output.completion === 'pending') {
593
697
  await this.#upsertStickyEntry(stickyKey, output.determinismState, cacheBaselineEventId, workflowType);
@@ -614,24 +718,36 @@ export class WorkerRuntime {
614
718
  const markerCommand = this.#buildDeterminismMarkerCommand(markerDetails);
615
719
  commandsForResponse = this.#injectDeterminismMarker(commandsForResponse, markerCommand);
616
720
  }
617
- const completion = create(RespondWorkflowTaskCompletedRequestSchema, {
618
- taskToken: response.taskToken,
619
- commands: commandsForResponse,
620
- identity: this.#identity,
621
- namespace: this.#namespace,
622
- deploymentOptions: this.#deploymentOptions,
623
- ...(this.#stickySchedulingEnabled ? { stickyAttributes: this.#stickyAttributes } : {}),
624
- ...(this.#versioningBehavior !== null ? { versioningBehavior: this.#versioningBehavior } : {}),
625
- });
626
- try {
627
- await this.#workflowService.respondWorkflowTaskCompleted(completion, { timeoutMs: RESPOND_TIMEOUT_MS });
628
- }
629
- catch (rpcError) {
630
- if (this.#isTaskNotFoundError(rpcError)) {
631
- this.#logWorkflowTaskNotFound('respondWorkflowTaskCompleted', execution);
632
- return;
721
+ const shouldRespondWorkflowTask = hasMultiQueries || !hasLegacyQueries;
722
+ if (shouldRespondWorkflowTask) {
723
+ const completion = create(RespondWorkflowTaskCompletedRequestSchema, {
724
+ taskToken: response.taskToken,
725
+ commands: commandsForResponse,
726
+ identity: this.#identity,
727
+ namespace: this.#namespace,
728
+ deploymentOptions: this.#deploymentOptions,
729
+ queryResults: multiQueryResults,
730
+ ...(this.#stickySchedulingEnabled && !hasLegacyQueries ? { stickyAttributes: this.#stickyAttributes } : {}),
731
+ ...(this.#versioningBehavior !== null ? { versioningBehavior: this.#versioningBehavior } : {}),
732
+ ...(updateProtocolMessages.length > 0 ? { messages: updateProtocolMessages } : {}),
733
+ });
734
+ try {
735
+ await this.#workflowService.respondWorkflowTaskCompleted(completion, { timeoutMs: RESPOND_TIMEOUT_MS });
633
736
  }
634
- throw rpcError;
737
+ catch (rpcError) {
738
+ this.#log('error', 'debug: respondWorkflowTaskCompleted failed', {
739
+ ...baseLogFields,
740
+ error: rpcError instanceof Error ? rpcError.message : String(rpcError),
741
+ });
742
+ if (this.#isTaskNotFoundError(rpcError)) {
743
+ this.#logWorkflowTaskNotFound('respondWorkflowTaskCompleted', execution);
744
+ return;
745
+ }
746
+ throw rpcError;
747
+ }
748
+ }
749
+ if (legacyQueryResult) {
750
+ await this.#respondLegacyQueryTask(response, legacyQueryResult);
635
751
  }
636
752
  }
637
753
  catch (error) {
@@ -693,7 +809,7 @@ export class WorkerRuntime {
693
809
  runId,
694
810
  };
695
811
  }
696
- async #ingestDeterminismState(workflowInfo, historyEvents) {
812
+ async #ingestDeterminismState(workflowInfo, historyEvents, options) {
697
813
  if (historyEvents.length === 0) {
698
814
  return undefined;
699
815
  }
@@ -701,6 +817,7 @@ export class WorkerRuntime {
701
817
  info: workflowInfo,
702
818
  history: historyEvents,
703
819
  dataConverter: this.#dataConverter,
820
+ queries: options?.queryRequests,
704
821
  }));
705
822
  }
706
823
  async #collectWorkflowHistory(execution, _response) {
@@ -817,6 +934,107 @@ export class WorkerRuntime {
817
934
  }
818
935
  return resolutions;
819
936
  }
937
+ async #extractSignalDeliveries(events) {
938
+ const deliveries = [];
939
+ const normalizeEventId = (value) => {
940
+ if (value === undefined || value === null) {
941
+ return null;
942
+ }
943
+ if (typeof value === 'string') {
944
+ return value;
945
+ }
946
+ return value.toString();
947
+ };
948
+ for (const event of events) {
949
+ if (event.eventType !== EventType.WORKFLOW_EXECUTION_SIGNALED) {
950
+ continue;
951
+ }
952
+ if (event.attributes?.case !== 'workflowExecutionSignaledEventAttributes') {
953
+ continue;
954
+ }
955
+ const attrs = event.attributes.value;
956
+ const args = await decodePayloadsToValues(this.#dataConverter, attrs.input?.payloads ?? []);
957
+ const workflowTaskCompletedEventId = 'workflowTaskCompletedEventId' in attrs
958
+ ? normalizeEventId(attrs
959
+ .workflowTaskCompletedEventId)
960
+ : null;
961
+ deliveries.push({
962
+ name: attrs.signalName ?? 'unknown',
963
+ args,
964
+ metadata: {
965
+ eventId: normalizeEventId(event.eventId),
966
+ workflowTaskCompletedEventId,
967
+ identity: attrs.identity ?? null,
968
+ },
969
+ });
970
+ }
971
+ return deliveries;
972
+ }
973
+ async #extractWorkflowQueryRequests(response) {
974
+ const requests = [];
975
+ const map = response.queries ?? {};
976
+ this.#log('info', 'debug: workflow query payloads detected', {
977
+ namespace: this.#namespace,
978
+ taskQueue: this.#taskQueue,
979
+ workflowId: response.workflowExecution?.workflowId,
980
+ runId: response.workflowExecution?.runId,
981
+ queryMapSize: Object.keys(map).length,
982
+ hasLegacyQuery: Boolean(response.query),
983
+ });
984
+ for (const [id, query] of Object.entries(map)) {
985
+ const args = await decodePayloadsToValues(this.#dataConverter, query.queryArgs?.payloads ?? []);
986
+ const header = await this.#decodeQueryHeader(query);
987
+ requests.push({
988
+ id,
989
+ name: query.queryType ?? 'query',
990
+ args,
991
+ metadata: header ? { header } : undefined,
992
+ source: 'multi',
993
+ });
994
+ }
995
+ if (response.query) {
996
+ const args = await decodePayloadsToValues(this.#dataConverter, response.query.queryArgs?.payloads ?? []);
997
+ const header = await this.#decodeQueryHeader(response.query);
998
+ requests.push({
999
+ name: response.query.queryType ?? 'query',
1000
+ args,
1001
+ metadata: header ? { header } : undefined,
1002
+ source: 'legacy',
1003
+ });
1004
+ }
1005
+ return requests;
1006
+ }
1007
+ async #decodeQueryHeader(query) {
1008
+ const fields = query?.header?.fields;
1009
+ if (!fields || Object.keys(fields).length === 0) {
1010
+ return undefined;
1011
+ }
1012
+ const decoded = {};
1013
+ for (const [key, payload] of Object.entries(fields)) {
1014
+ const values = await decodePayloadsToValues(this.#dataConverter, payload ? [payload] : []);
1015
+ decoded[key] = values.length === 0 ? undefined : values.length === 1 ? values[0] : Object.freeze([...values]);
1016
+ }
1017
+ return Object.keys(decoded).length > 0 ? decoded : undefined;
1018
+ }
1019
+ async #respondLegacyQueryTask(response, entry) {
1020
+ const queryResult = entry.result;
1021
+ this.#log('debug', 'responding to legacy workflow query', {
1022
+ namespace: this.#namespace,
1023
+ workflowId: response.workflowExecution?.workflowId,
1024
+ runId: response.workflowExecution?.runId,
1025
+ queryName: entry.request.name,
1026
+ });
1027
+ const request = create(RespondQueryTaskCompletedRequestSchema, {
1028
+ taskToken: response.taskToken ?? new Uint8Array(),
1029
+ completedType: queryResult.resultType ?? QueryResultType.ANSWERED,
1030
+ queryResult: queryResult.answer,
1031
+ errorMessage: queryResult.errorMessage ?? '',
1032
+ namespace: this.#namespace,
1033
+ failure: queryResult.failure,
1034
+ cause: WorkflowTaskFailedCause.UNSPECIFIED,
1035
+ });
1036
+ await this.#workflowService.respondQueryTaskCompleted(request, { timeoutMs: RESPOND_TIMEOUT_MS });
1037
+ }
820
1038
  async #fetchWorkflowHistoryPage(execution, nextPageToken) {
821
1039
  if (!execution.workflowId || !execution.runId) {
822
1040
  return { events: [], nextPageToken: new Uint8Array() };
@@ -931,11 +1149,13 @@ export class WorkerRuntime {
931
1149
  };
932
1150
  }
933
1151
  async #computeNondeterminismMismatches(error, expectedState) {
934
- const baseline = expectedState ?? {
935
- commandHistory: [],
936
- randomValues: [],
937
- timeValues: [],
938
- failureMetadata: undefined,
1152
+ const baseline = {
1153
+ commandHistory: expectedState?.commandHistory ?? [],
1154
+ randomValues: expectedState?.randomValues ?? [],
1155
+ timeValues: expectedState?.timeValues ?? [],
1156
+ failureMetadata: expectedState?.failureMetadata,
1157
+ signals: expectedState?.signals ?? [],
1158
+ queries: expectedState?.queries ?? [],
939
1159
  };
940
1160
  const hint = error.details?.hint;
941
1161
  if (!hint) {
@@ -944,6 +1164,8 @@ export class WorkerRuntime {
944
1164
  const commandIndex = this.#parseIndexFromHint(hint, 'commandIndex');
945
1165
  const randomIndex = this.#parseIndexFromHint(hint, 'randomIndex');
946
1166
  const timeIndex = this.#parseIndexFromHint(hint, 'timeIndex');
1167
+ const signalIndex = this.#parseIndexFromHint(hint, 'signalIndex');
1168
+ const queryIndex = this.#parseIndexFromHint(hint, 'queryIndex');
947
1169
  const mutableActual = {
948
1170
  commandHistory: baseline.commandHistory.map((entry) => ({
949
1171
  intent: entry.intent,
@@ -952,6 +1174,8 @@ export class WorkerRuntime {
952
1174
  randomValues: [...baseline.randomValues],
953
1175
  timeValues: [...baseline.timeValues],
954
1176
  failureMetadata: baseline.failureMetadata ? { ...baseline.failureMetadata } : undefined,
1177
+ signals: baseline.signals.map((record) => ({ ...record })),
1178
+ queries: baseline.queries.map((record) => ({ ...record })),
955
1179
  };
956
1180
  let mutated = false;
957
1181
  if (commandIndex !== null) {
@@ -980,6 +1204,36 @@ export class WorkerRuntime {
980
1204
  mutableActual.timeValues[timeIndex] = Number.NaN;
981
1205
  mutated = true;
982
1206
  }
1207
+ if (signalIndex !== null) {
1208
+ const receivedSignal = this.#asSignalRecord(error.details?.received);
1209
+ if (receivedSignal) {
1210
+ if (signalIndex < mutableActual.signals.length) {
1211
+ mutableActual.signals = mutableActual.signals.map((record, idx) => idx === signalIndex ? receivedSignal : record);
1212
+ }
1213
+ else {
1214
+ mutableActual.signals = [...mutableActual.signals, receivedSignal];
1215
+ }
1216
+ }
1217
+ else if (signalIndex < mutableActual.signals.length) {
1218
+ mutableActual.signals = mutableActual.signals.filter((_, idx) => idx !== signalIndex);
1219
+ }
1220
+ mutated = true;
1221
+ }
1222
+ if (queryIndex !== null) {
1223
+ const receivedQuery = this.#asQueryRecord(error.details?.received);
1224
+ if (receivedQuery) {
1225
+ if (queryIndex < mutableActual.queries.length) {
1226
+ mutableActual.queries = mutableActual.queries.map((record, idx) => idx === queryIndex ? receivedQuery : record);
1227
+ }
1228
+ else {
1229
+ mutableActual.queries = [...mutableActual.queries, receivedQuery];
1230
+ }
1231
+ }
1232
+ else if (queryIndex < mutableActual.queries.length) {
1233
+ mutableActual.queries = mutableActual.queries.filter((_, idx) => idx !== queryIndex);
1234
+ }
1235
+ mutated = true;
1236
+ }
983
1237
  if (!mutated) {
984
1238
  return [];
985
1239
  }
@@ -988,6 +1242,8 @@ export class WorkerRuntime {
988
1242
  randomValues: mutableActual.randomValues,
989
1243
  timeValues: mutableActual.timeValues,
990
1244
  failureMetadata: mutableActual.failureMetadata,
1245
+ signals: mutableActual.signals,
1246
+ queries: mutableActual.queries,
991
1247
  };
992
1248
  const diff = await Effect.runPromise(diffDeterminismState(baseline, actualState));
993
1249
  return diff.mismatches;
@@ -1030,6 +1286,24 @@ export class WorkerRuntime {
1030
1286
  array.push(Number.NaN);
1031
1287
  }
1032
1288
  }
1289
+ #asSignalRecord(value) {
1290
+ if (!value || typeof value !== 'object') {
1291
+ return undefined;
1292
+ }
1293
+ if ('signalName' in value && 'payloadHash' in value) {
1294
+ return value;
1295
+ }
1296
+ return undefined;
1297
+ }
1298
+ #asQueryRecord(value) {
1299
+ if (!value || typeof value !== 'object') {
1300
+ return undefined;
1301
+ }
1302
+ if ('queryName' in value && 'requestHash' in value) {
1303
+ return value;
1304
+ }
1305
+ return undefined;
1306
+ }
1033
1307
  static #isStickyCacheInstance(value) {
1034
1308
  if (!value || typeof value !== 'object') {
1035
1309
  return false;