@proompteng/temporal-bun-sdk 0.2.0 → 0.4.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 (123) hide show
  1. package/README.md +141 -10
  2. package/dist/src/bin/replay-command.d.ts.map +1 -1
  3. package/dist/src/bin/replay-command.js +6 -2
  4. package/dist/src/bin/replay-command.js.map +1 -1
  5. package/dist/src/bin/temporal-bun.d.ts +1 -1
  6. package/dist/src/bin/temporal-bun.d.ts.map +1 -1
  7. package/dist/src/bin/temporal-bun.js +74 -0
  8. package/dist/src/bin/temporal-bun.js.map +1 -1
  9. package/dist/src/client/layer.d.ts +2 -2
  10. package/dist/src/client/layer.d.ts.map +1 -1
  11. package/dist/src/client/retries.d.ts.map +1 -1
  12. package/dist/src/client/retries.js +27 -3
  13. package/dist/src/client/retries.js.map +1 -1
  14. package/dist/src/client/serialization.d.ts +34 -2
  15. package/dist/src/client/serialization.d.ts.map +1 -1
  16. package/dist/src/client/serialization.js +78 -5
  17. package/dist/src/client/serialization.js.map +1 -1
  18. package/dist/src/client/types.d.ts +26 -0
  19. package/dist/src/client/types.d.ts.map +1 -1
  20. package/dist/src/client.d.ts +22 -6
  21. package/dist/src/client.d.ts.map +1 -1
  22. package/dist/src/client.js +488 -39
  23. package/dist/src/client.js.map +1 -1
  24. package/dist/src/common/payloads/codecs.d.ts +38 -0
  25. package/dist/src/common/payloads/codecs.d.ts.map +1 -0
  26. package/dist/src/common/payloads/codecs.js +174 -0
  27. package/dist/src/common/payloads/codecs.js.map +1 -0
  28. package/dist/src/common/payloads/converter.d.ts +52 -3
  29. package/dist/src/common/payloads/converter.d.ts.map +1 -1
  30. package/dist/src/common/payloads/converter.js +340 -2
  31. package/dist/src/common/payloads/converter.js.map +1 -1
  32. package/dist/src/common/payloads/failure.d.ts +5 -6
  33. package/dist/src/common/payloads/failure.d.ts.map +1 -1
  34. package/dist/src/common/payloads/failure.js +3 -52
  35. package/dist/src/common/payloads/failure.js.map +1 -1
  36. package/dist/src/common/payloads/index.d.ts +1 -0
  37. package/dist/src/common/payloads/index.d.ts.map +1 -1
  38. package/dist/src/common/payloads/index.js +1 -0
  39. package/dist/src/common/payloads/index.js.map +1 -1
  40. package/dist/src/config.d.ts +9 -0
  41. package/dist/src/config.d.ts.map +1 -1
  42. package/dist/src/config.js +62 -1
  43. package/dist/src/config.js.map +1 -1
  44. package/dist/src/index.d.ts +1 -1
  45. package/dist/src/index.d.ts.map +1 -1
  46. package/dist/src/index.js.map +1 -1
  47. package/dist/src/interceptors/client.d.ts +28 -0
  48. package/dist/src/interceptors/client.d.ts.map +1 -0
  49. package/dist/src/interceptors/client.js +169 -0
  50. package/dist/src/interceptors/client.js.map +1 -0
  51. package/dist/src/interceptors/types.d.ts +33 -0
  52. package/dist/src/interceptors/types.d.ts.map +1 -0
  53. package/dist/src/interceptors/types.js +44 -0
  54. package/dist/src/interceptors/types.js.map +1 -0
  55. package/dist/src/interceptors/worker.d.ts +22 -0
  56. package/dist/src/interceptors/worker.d.ts.map +1 -0
  57. package/dist/src/interceptors/worker.js +156 -0
  58. package/dist/src/interceptors/worker.js.map +1 -0
  59. package/dist/src/runtime/cli-layer.d.ts +4 -3
  60. package/dist/src/runtime/cli-layer.d.ts.map +1 -1
  61. package/dist/src/runtime/cli-layer.js +5 -2
  62. package/dist/src/runtime/cli-layer.js.map +1 -1
  63. package/dist/src/runtime/effect-layers.d.ts +9 -0
  64. package/dist/src/runtime/effect-layers.d.ts.map +1 -1
  65. package/dist/src/runtime/effect-layers.js +15 -0
  66. package/dist/src/runtime/effect-layers.js.map +1 -1
  67. package/dist/src/worker/concurrency.d.ts +8 -0
  68. package/dist/src/worker/concurrency.d.ts.map +1 -1
  69. package/dist/src/worker/concurrency.js +5 -9
  70. package/dist/src/worker/concurrency.js.map +1 -1
  71. package/dist/src/worker/runtime.d.ts +5 -0
  72. package/dist/src/worker/runtime.d.ts.map +1 -1
  73. package/dist/src/worker/runtime.js +509 -40
  74. package/dist/src/worker/runtime.js.map +1 -1
  75. package/dist/src/worker/sticky-cache.d.ts +5 -0
  76. package/dist/src/worker/sticky-cache.d.ts.map +1 -1
  77. package/dist/src/worker/sticky-cache.js +26 -0
  78. package/dist/src/worker/sticky-cache.js.map +1 -1
  79. package/dist/src/worker/update-protocol.d.ts +33 -0
  80. package/dist/src/worker/update-protocol.d.ts.map +1 -0
  81. package/dist/src/worker/update-protocol.js +243 -0
  82. package/dist/src/worker/update-protocol.js.map +1 -0
  83. package/dist/src/worker.js +1 -0
  84. package/dist/src/worker.js.map +1 -1
  85. package/dist/src/workflow/commands.d.ts +38 -2
  86. package/dist/src/workflow/commands.d.ts.map +1 -1
  87. package/dist/src/workflow/commands.js +153 -1
  88. package/dist/src/workflow/commands.js.map +1 -1
  89. package/dist/src/workflow/context.d.ts +111 -3
  90. package/dist/src/workflow/context.d.ts.map +1 -1
  91. package/dist/src/workflow/context.js +526 -6
  92. package/dist/src/workflow/context.js.map +1 -1
  93. package/dist/src/workflow/definition.d.ts +29 -3
  94. package/dist/src/workflow/definition.d.ts.map +1 -1
  95. package/dist/src/workflow/definition.js +30 -4
  96. package/dist/src/workflow/definition.js.map +1 -1
  97. package/dist/src/workflow/determinism.d.ts +64 -0
  98. package/dist/src/workflow/determinism.d.ts.map +1 -1
  99. package/dist/src/workflow/determinism.js +120 -3
  100. package/dist/src/workflow/determinism.js.map +1 -1
  101. package/dist/src/workflow/errors.d.ts +6 -0
  102. package/dist/src/workflow/errors.d.ts.map +1 -1
  103. package/dist/src/workflow/errors.js +12 -0
  104. package/dist/src/workflow/errors.js.map +1 -1
  105. package/dist/src/workflow/executor.d.ts +56 -0
  106. package/dist/src/workflow/executor.d.ts.map +1 -1
  107. package/dist/src/workflow/executor.js +300 -9
  108. package/dist/src/workflow/executor.js.map +1 -1
  109. package/dist/src/workflow/inbound.d.ts +84 -0
  110. package/dist/src/workflow/inbound.d.ts.map +1 -0
  111. package/dist/src/workflow/inbound.js +65 -0
  112. package/dist/src/workflow/inbound.js.map +1 -0
  113. package/dist/src/workflow/index.d.ts +1 -0
  114. package/dist/src/workflow/index.d.ts.map +1 -1
  115. package/dist/src/workflow/index.js +1 -0
  116. package/dist/src/workflow/index.js.map +1 -1
  117. package/dist/src/workflow/replay.d.ts +24 -2
  118. package/dist/src/workflow/replay.d.ts.map +1 -1
  119. package/dist/src/workflow/replay.js +679 -15
  120. package/dist/src/workflow/replay.js.map +1 -1
  121. package/dist/src/workflows/index.d.ts +1 -1
  122. package/dist/src/workflows/index.d.ts.map +1 -1
  123. package/package.json +1 -1
@@ -2,10 +2,10 @@ import { create } from '@bufbuild/protobuf';
2
2
  import { Effect } from 'effect';
3
3
  import { durationToMillis } from '../common/duration';
4
4
  import { decodePayloadsToValues, encodeValuesToPayloads } from '../common/payloads';
5
- import { PayloadsSchema } from '../proto/temporal/api/common/v1/message_pb';
5
+ import { PayloadsSchema, } from '../proto/temporal/api/common/v1/message_pb';
6
6
  import { EventType } from '../proto/temporal/api/enums/v1/event_type_pb';
7
7
  import { ParentClosePolicy, TimeoutType, WorkflowIdReusePolicy } from '../proto/temporal/api/enums/v1/workflow_pb';
8
- import { intentsEqual } from './determinism';
8
+ import { intentsEqual, stableStringify } from './determinism';
9
9
  export const DETERMINISM_MARKER_NAME = 'temporal-bun-sdk/determinism';
10
10
  const DETERMINISM_MARKER_SCHEMA_VERSION = 1;
11
11
  const DETERMINISM_MARKER_DETAIL_KEY = 'snapshot';
@@ -58,6 +58,7 @@ export const ingestWorkflowHistory = (intake) => Effect.gen(function* () {
58
58
  const events = sortHistoryEvents(intake.history ?? []);
59
59
  const shouldUseDeterminismMarker = intake.ignoreDeterminismMarker !== true;
60
60
  let markerSnapshot;
61
+ const updateEntries = collectWorkflowUpdateEntries(events);
61
62
  if (shouldUseDeterminismMarker) {
62
63
  for (const event of events) {
63
64
  if (event.eventType !== EventType.MARKER_RECORDED) {
@@ -76,21 +77,31 @@ export const ingestWorkflowHistory = (intake) => Effect.gen(function* () {
76
77
  }
77
78
  }
78
79
  const extractedFailureMetadata = extractFailureMetadata(events);
80
+ const replayUpdateInvocations = yield* Effect.tryPromise(async () => collectWorkflowUpdateInvocations(events, intake.dataConverter));
79
81
  if (shouldUseDeterminismMarker && markerSnapshot) {
80
- const determinismState = markerSnapshot.determinismState.failureMetadata
81
- ? markerSnapshot.determinismState
82
- : {
83
- ...markerSnapshot.determinismState,
84
- ...(extractedFailureMetadata ? { failureMetadata: extractedFailureMetadata } : {}),
82
+ let determinismState = cloneDeterminismState(markerSnapshot.determinismState);
83
+ if (!determinismState.failureMetadata && extractedFailureMetadata) {
84
+ determinismState = {
85
+ ...determinismState,
86
+ failureMetadata: extractedFailureMetadata,
85
87
  };
88
+ }
89
+ if ((!determinismState.updates || determinismState.updates.length === 0) && updateEntries.length > 0) {
90
+ determinismState = {
91
+ ...determinismState,
92
+ updates: updateEntries,
93
+ };
94
+ }
95
+ determinismState = mergePendingQueryRequests(determinismState, intake.queries);
86
96
  const latestEventId = resolveHistoryLastEventId(events) ?? markerSnapshot.lastEventId ?? null;
87
97
  return {
88
98
  determinismState,
89
99
  lastEventId: latestEventId,
90
100
  hasDeterminismMarker: true,
101
+ ...(replayUpdateInvocations.length > 0 ? { updates: replayUpdateInvocations } : {}),
91
102
  };
92
103
  }
93
- return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata);
104
+ return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntries, replayUpdateInvocations);
94
105
  });
95
106
  /**
96
107
  * Diff determinism state against freshly emitted intents to produce rich
@@ -153,6 +164,36 @@ export const diffDeterminismState = (expected, actual) => Effect.sync(() => {
153
164
  });
154
165
  }
155
166
  }
167
+ const expectedSignals = expected.signals ?? [];
168
+ const actualSignals = actual.signals ?? [];
169
+ const maxSignals = Math.max(expectedSignals.length, actualSignals.length);
170
+ for (let index = 0; index < maxSignals; index += 1) {
171
+ const expectedSignal = expectedSignals[index];
172
+ const actualSignal = actualSignals[index];
173
+ if (!recordsMatch(expectedSignal, actualSignal)) {
174
+ mismatches.push({ kind: 'signal', index, expected: expectedSignal, actual: actualSignal });
175
+ }
176
+ }
177
+ const expectedQueries = expected.queries ?? [];
178
+ const actualQueries = actual.queries ?? [];
179
+ const maxQueries = Math.max(expectedQueries.length, actualQueries.length);
180
+ for (let index = 0; index < maxQueries; index += 1) {
181
+ const expectedQuery = expectedQueries[index];
182
+ const actualQuery = actualQueries[index];
183
+ if (!recordsMatch(expectedQuery, actualQuery)) {
184
+ mismatches.push({ kind: 'query', index, expected: expectedQuery, actual: actualQuery });
185
+ }
186
+ }
187
+ const expectedUpdates = expected.updates ?? [];
188
+ const actualUpdates = actual.updates ?? [];
189
+ const maxUpdates = Math.max(expectedUpdates.length, actualUpdates.length);
190
+ for (let index = 0; index < maxUpdates; index += 1) {
191
+ const expectedEntry = expectedUpdates[index];
192
+ const actualEntry = actualUpdates[index];
193
+ if (!updatesEqual(expectedEntry, actualEntry)) {
194
+ mismatches.push({ kind: 'update', index, expected: expectedEntry, actual: actualEntry });
195
+ }
196
+ }
156
197
  return { mismatches };
157
198
  });
158
199
  export const resolveHistoryLastEventId = (events) => {
@@ -180,6 +221,9 @@ export const cloneDeterminismState = (state) => ({
180
221
  randomValues: [...state.randomValues],
181
222
  timeValues: [...state.timeValues],
182
223
  failureMetadata: state.failureMetadata ? { ...state.failureMetadata } : undefined,
224
+ signals: state.signals ? state.signals.map((record) => ({ ...record })) : [],
225
+ queries: state.queries ? state.queries.map((record) => ({ ...record })) : [],
226
+ updates: state.updates ? state.updates.map((entry) => ({ ...entry })) : undefined,
183
227
  });
184
228
  const sortHistoryEvents = (events) => events.slice().sort((left, right) => {
185
229
  const leftId = resolveEventIdForSort(left);
@@ -340,9 +384,14 @@ const resolveCommandMismatchMetadata = (expectedEntry, actualEntry) => {
340
384
  eventType,
341
385
  };
342
386
  };
343
- const reconstructDeterminismState = (events, intake, failureMetadata) => Effect.gen(function* () {
387
+ const reconstructDeterminismState = (events, intake, failureMetadata, precomputedUpdates, replayUpdateInvocations = []) => Effect.gen(function* () {
344
388
  let sequence = 0;
345
389
  const commandHistory = [];
390
+ const signalRecords = [];
391
+ const queryRecords = [];
392
+ const updates = precomputedUpdates ?? collectWorkflowUpdateEntries(events);
393
+ const scheduledActivities = new Map();
394
+ const timersByStartEventId = new Map();
346
395
  for (const event of events) {
347
396
  switch (event.eventType) {
348
397
  case EventType.ACTIVITY_TASK_SCHEDULED: {
@@ -355,6 +404,10 @@ const reconstructDeterminismState = (events, intake, failureMetadata) => Effect.
355
404
  intent,
356
405
  metadata: buildCommandMetadata(event, event.attributes.value),
357
406
  });
407
+ const scheduledKey = normalizeEventId(event.eventId);
408
+ if (scheduledKey) {
409
+ scheduledActivities.set(scheduledKey, intent.activityId);
410
+ }
358
411
  sequence += 1;
359
412
  }
360
413
  break;
@@ -369,6 +422,10 @@ const reconstructDeterminismState = (events, intake, failureMetadata) => Effect.
369
422
  intent,
370
423
  metadata: buildCommandMetadata(event, event.attributes.value),
371
424
  });
425
+ const startKey = normalizeEventId(event.eventId);
426
+ if (startKey) {
427
+ timersByStartEventId.set(startKey, intent.timerId);
428
+ }
372
429
  sequence += 1;
373
430
  }
374
431
  break;
@@ -387,6 +444,34 @@ const reconstructDeterminismState = (events, intake, failureMetadata) => Effect.
387
444
  }
388
445
  break;
389
446
  }
447
+ case EventType.ACTIVITY_TASK_CANCEL_REQUESTED: {
448
+ if (event.attributes?.case !== 'activityTaskCancelRequestedEventAttributes') {
449
+ break;
450
+ }
451
+ const intent = fromActivityTaskCancelRequested(event.attributes.value, sequence, scheduledActivities);
452
+ if (intent) {
453
+ commandHistory.push({
454
+ intent,
455
+ metadata: buildCommandMetadata(event, event.attributes.value),
456
+ });
457
+ sequence += 1;
458
+ }
459
+ break;
460
+ }
461
+ case EventType.TIMER_CANCELED: {
462
+ if (event.attributes?.case !== 'timerCanceledEventAttributes') {
463
+ break;
464
+ }
465
+ const intent = fromTimerCanceled(event.attributes.value, sequence, timersByStartEventId);
466
+ if (intent) {
467
+ commandHistory.push({
468
+ intent,
469
+ metadata: buildCommandMetadata(event, event.attributes.value),
470
+ });
471
+ sequence += 1;
472
+ }
473
+ break;
474
+ }
390
475
  case EventType.SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
391
476
  if (event.attributes?.case !== 'signalExternalWorkflowExecutionInitiatedEventAttributes') {
392
477
  break;
@@ -401,6 +486,39 @@ const reconstructDeterminismState = (events, intake, failureMetadata) => Effect.
401
486
  }
402
487
  break;
403
488
  }
489
+ case EventType.REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
490
+ if (event.attributes?.case !== 'requestCancelExternalWorkflowExecutionInitiatedEventAttributes') {
491
+ break;
492
+ }
493
+ const intent = fromRequestCancelExternalWorkflow(event.attributes.value, sequence, intake);
494
+ if (intent) {
495
+ commandHistory.push({
496
+ intent,
497
+ metadata: buildCommandMetadata(event, event.attributes.value),
498
+ });
499
+ sequence += 1;
500
+ }
501
+ break;
502
+ }
503
+ case EventType.WORKFLOW_EXECUTION_SIGNALED: {
504
+ if (event.attributes?.case !== 'workflowExecutionSignaledEventAttributes') {
505
+ break;
506
+ }
507
+ const attributes = event.attributes.value;
508
+ const payloads = yield* decodePayloadArray(intake.dataConverter, attributes.input);
509
+ const workflowTaskCompletedEventId = 'workflowTaskCompletedEventId' in attributes
510
+ ? normalizeEventId(attributes
511
+ .workflowTaskCompletedEventId)
512
+ : null;
513
+ signalRecords.push({
514
+ signalName: attributes.signalName ?? 'unknown',
515
+ payloadHash: stableStringify(payloads),
516
+ eventId: normalizeEventId(event.eventId),
517
+ workflowTaskCompletedEventId,
518
+ identity: attributes.identity ?? null,
519
+ });
520
+ break;
521
+ }
404
522
  case EventType.WORKFLOW_EXECUTION_CONTINUED_AS_NEW: {
405
523
  if (event.attributes?.case !== 'workflowExecutionContinuedAsNewEventAttributes') {
406
524
  break;
@@ -415,19 +533,70 @@ const reconstructDeterminismState = (events, intake, failureMetadata) => Effect.
415
533
  }
416
534
  break;
417
535
  }
536
+ case EventType.MARKER_RECORDED: {
537
+ if (event.attributes?.case !== 'markerRecordedEventAttributes') {
538
+ break;
539
+ }
540
+ if (event.attributes.value.markerName === DETERMINISM_MARKER_NAME) {
541
+ break;
542
+ }
543
+ const intent = yield* fromMarkerRecorded(event.attributes.value, sequence, intake);
544
+ if (intent) {
545
+ commandHistory.push({
546
+ intent,
547
+ metadata: buildCommandMetadata(event, event.attributes.value),
548
+ });
549
+ sequence += 1;
550
+ }
551
+ break;
552
+ }
553
+ case EventType.UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: {
554
+ if (event.attributes?.case !== 'upsertWorkflowSearchAttributesEventAttributes') {
555
+ break;
556
+ }
557
+ const intent = yield* fromUpsertSearchAttributes(event.attributes.value, sequence, intake);
558
+ if (intent) {
559
+ commandHistory.push({
560
+ intent,
561
+ metadata: buildCommandMetadata(event, event.attributes.value),
562
+ });
563
+ sequence += 1;
564
+ }
565
+ break;
566
+ }
567
+ case EventType.WORKFLOW_PROPERTIES_MODIFIED: {
568
+ if (event.attributes?.case !== 'workflowPropertiesModifiedEventAttributes') {
569
+ break;
570
+ }
571
+ const intent = yield* fromWorkflowPropertiesModified(event.attributes.value, sequence, intake);
572
+ if (intent) {
573
+ commandHistory.push({
574
+ intent,
575
+ metadata: buildCommandMetadata(event, event.attributes.value),
576
+ });
577
+ sequence += 1;
578
+ }
579
+ break;
580
+ }
418
581
  default:
419
582
  break;
420
583
  }
421
584
  }
585
+ let determinismState = {
586
+ commandHistory,
587
+ randomValues: [],
588
+ timeValues: [],
589
+ ...(failureMetadata ? { failureMetadata } : {}),
590
+ signals: signalRecords,
591
+ queries: queryRecords,
592
+ ...(updates.length > 0 ? { updates } : {}),
593
+ };
594
+ determinismState = mergePendingQueryRequests(determinismState, intake.queries);
422
595
  return {
423
- determinismState: {
424
- commandHistory,
425
- randomValues: [],
426
- timeValues: [],
427
- ...(failureMetadata ? { failureMetadata } : {}),
428
- },
596
+ determinismState,
429
597
  lastEventId: resolveHistoryLastEventId(events),
430
598
  hasDeterminismMarker: false,
599
+ ...(replayUpdateInvocations.length > 0 ? { updates: replayUpdateInvocations } : {}),
431
600
  };
432
601
  });
433
602
  const fromActivityTaskScheduled = (attributes, sequence, intake) => Effect.gen(function* () {
@@ -550,7 +719,313 @@ const fromContinueAsNew = (attributes, sequence, intake) => Effect.gen(function*
550
719
  };
551
720
  return intent;
552
721
  });
722
+ const fromActivityTaskCancelRequested = (attributes, sequence, scheduledActivities) => {
723
+ const scheduledEventId = normalizeBigintIdentifier(attributes.scheduledEventId);
724
+ if (!scheduledEventId) {
725
+ return undefined;
726
+ }
727
+ const activityId = scheduledActivities.get(scheduledEventId);
728
+ if (!activityId) {
729
+ return undefined;
730
+ }
731
+ return {
732
+ id: `cancel-activity-${sequence}`,
733
+ kind: 'request-cancel-activity',
734
+ sequence,
735
+ activityId,
736
+ scheduledEventId,
737
+ };
738
+ };
739
+ const fromTimerCanceled = (attributes, sequence, timersByStartEventId) => {
740
+ const startedEventId = normalizeBigintIdentifier(attributes.startedEventId);
741
+ const timerId = attributes.timerId || (startedEventId ? timersByStartEventId.get(startedEventId) : undefined);
742
+ if (!timerId) {
743
+ return undefined;
744
+ }
745
+ return {
746
+ id: `cancel-timer-${sequence}`,
747
+ kind: 'cancel-timer',
748
+ sequence,
749
+ timerId,
750
+ startedEventId,
751
+ };
752
+ };
753
+ const fromRequestCancelExternalWorkflow = (attributes, sequence, intake) => {
754
+ const workflowId = attributes.workflowExecution?.workflowId ?? intake.info.workflowId;
755
+ if (!workflowId) {
756
+ return undefined;
757
+ }
758
+ return {
759
+ id: `cancel-external-${sequence}`,
760
+ kind: 'request-cancel-external-workflow',
761
+ sequence,
762
+ namespace: attributes.namespace || intake.info.namespace,
763
+ workflowId,
764
+ runId: attributes.workflowExecution?.runId || undefined,
765
+ childWorkflowOnly: attributes.childWorkflowOnly ?? false,
766
+ reason: attributes.reason || undefined,
767
+ };
768
+ };
769
+ const fromMarkerRecorded = (attributes, sequence, intake) => Effect.gen(function* () {
770
+ const markerName = attributes.markerName || 'marker';
771
+ const details = yield* decodeMarkerDetails(intake.dataConverter, attributes.details);
772
+ return {
773
+ id: `record-marker-${sequence}`,
774
+ kind: 'record-marker',
775
+ sequence,
776
+ markerName,
777
+ details,
778
+ };
779
+ });
780
+ const fromUpsertSearchAttributes = (attributes, sequence, intake) => Effect.gen(function* () {
781
+ const searchAttributes = yield* decodeSearchAttributes(intake.dataConverter, attributes.searchAttributes);
782
+ if (!searchAttributes) {
783
+ return undefined;
784
+ }
785
+ return {
786
+ id: `upsert-search-attributes-${sequence}`,
787
+ kind: 'upsert-search-attributes',
788
+ sequence,
789
+ searchAttributes,
790
+ };
791
+ });
792
+ const fromWorkflowPropertiesModified = (attributes, sequence, intake) => Effect.gen(function* () {
793
+ const memo = yield* decodeMemo(intake.dataConverter, attributes.upsertedMemo);
794
+ return {
795
+ id: `modify-workflow-properties-${sequence}`,
796
+ kind: 'modify-workflow-properties',
797
+ sequence,
798
+ memo,
799
+ };
800
+ });
801
+ const collectWorkflowUpdateEntries = (events) => {
802
+ const updates = [];
803
+ for (const event of events) {
804
+ if (!event.attributes) {
805
+ continue;
806
+ }
807
+ const historyEventId = normalizeEventId(event.eventId) ?? undefined;
808
+ switch (event.eventType) {
809
+ case EventType.WORKFLOW_EXECUTION_UPDATE_ADMITTED: {
810
+ if (event.attributes.case !== 'workflowExecutionUpdateAdmittedEventAttributes') {
811
+ break;
812
+ }
813
+ const attrs = event.attributes.value;
814
+ const updateId = normalizeUpdateId(attrs.request?.meta?.updateId);
815
+ if (!updateId) {
816
+ break;
817
+ }
818
+ updates.push({
819
+ updateId,
820
+ stage: 'admitted',
821
+ handlerName: normalizeOptionalNonEmptyString(attrs.request?.input?.name),
822
+ identity: normalizeOptionalNonEmptyString(attrs.request?.meta?.identity),
823
+ historyEventId,
824
+ });
825
+ break;
826
+ }
827
+ case EventType.WORKFLOW_EXECUTION_UPDATE_ACCEPTED: {
828
+ if (event.attributes.case !== 'workflowExecutionUpdateAcceptedEventAttributes') {
829
+ break;
830
+ }
831
+ const attrs = event.attributes.value;
832
+ const updateId = normalizeUpdateId(attrs.acceptedRequest?.meta?.updateId);
833
+ if (!updateId) {
834
+ break;
835
+ }
836
+ updates.push({
837
+ updateId,
838
+ stage: 'accepted',
839
+ handlerName: normalizeOptionalNonEmptyString(attrs.acceptedRequest?.input?.name),
840
+ identity: normalizeOptionalNonEmptyString(attrs.acceptedRequest?.meta?.identity),
841
+ messageId: normalizeOptionalNonEmptyString(attrs.acceptedRequestMessageId),
842
+ sequencingEventId: normalizeBigintIdentifier(attrs.acceptedRequestSequencingEventId),
843
+ historyEventId,
844
+ });
845
+ break;
846
+ }
847
+ case EventType.WORKFLOW_EXECUTION_UPDATE_REJECTED: {
848
+ if (event.attributes.case !== 'workflowExecutionUpdateRejectedEventAttributes') {
849
+ break;
850
+ }
851
+ const attrs = event.attributes.value;
852
+ const updateId = normalizeUpdateId(attrs.rejectedRequest?.meta?.updateId);
853
+ if (!updateId) {
854
+ break;
855
+ }
856
+ updates.push({
857
+ updateId,
858
+ stage: 'rejected',
859
+ handlerName: normalizeOptionalNonEmptyString(attrs.rejectedRequest?.input?.name),
860
+ identity: normalizeOptionalNonEmptyString(attrs.rejectedRequest?.meta?.identity),
861
+ messageId: normalizeOptionalNonEmptyString(attrs.rejectedRequestMessageId),
862
+ sequencingEventId: normalizeBigintIdentifier(attrs.rejectedRequestSequencingEventId),
863
+ failureMessage: normalizeOptionalNonEmptyString(attrs.failure?.message),
864
+ historyEventId,
865
+ });
866
+ break;
867
+ }
868
+ case EventType.WORKFLOW_EXECUTION_UPDATE_COMPLETED: {
869
+ if (event.attributes.case !== 'workflowExecutionUpdateCompletedEventAttributes') {
870
+ break;
871
+ }
872
+ const attrs = event.attributes.value;
873
+ const updateId = normalizeUpdateId(attrs.meta?.updateId);
874
+ if (!updateId) {
875
+ break;
876
+ }
877
+ updates.push({
878
+ updateId,
879
+ stage: 'completed',
880
+ identity: normalizeOptionalNonEmptyString(attrs.meta?.identity),
881
+ acceptedEventId: normalizeBigintIdentifier(attrs.acceptedEventId),
882
+ outcome: resolveOutcomeStatus(attrs.outcome),
883
+ historyEventId,
884
+ });
885
+ break;
886
+ }
887
+ default:
888
+ break;
889
+ }
890
+ }
891
+ return updates;
892
+ };
893
+ const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
894
+ const invocations = [];
895
+ for (const event of events) {
896
+ if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_ACCEPTED) {
897
+ if (event.attributes.case !== 'workflowExecutionUpdateAcceptedEventAttributes') {
898
+ continue;
899
+ }
900
+ const attrs = event.attributes.value;
901
+ const invocation = await buildUpdateInvocationFromRequest({
902
+ request: attrs.acceptedRequest,
903
+ protocolInstanceId: attrs.protocolInstanceId,
904
+ requestMessageId: attrs.acceptedRequestMessageId,
905
+ sequencingEventId: attrs.acceptedRequestSequencingEventId,
906
+ fallbackEventId: event.eventId,
907
+ dataConverter,
908
+ });
909
+ if (invocation) {
910
+ invocations.push(invocation);
911
+ }
912
+ continue;
913
+ }
914
+ if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_REJECTED) {
915
+ if (event.attributes.case !== 'workflowExecutionUpdateRejectedEventAttributes') {
916
+ continue;
917
+ }
918
+ const attrs = event.attributes.value;
919
+ const invocation = await buildUpdateInvocationFromRequest({
920
+ request: attrs.rejectedRequest,
921
+ protocolInstanceId: attrs.protocolInstanceId,
922
+ requestMessageId: attrs.rejectedRequestMessageId,
923
+ sequencingEventId: attrs.rejectedRequestSequencingEventId,
924
+ fallbackEventId: event.eventId,
925
+ dataConverter,
926
+ });
927
+ if (invocation) {
928
+ invocations.push(invocation);
929
+ }
930
+ continue;
931
+ }
932
+ if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_ADMITTED) {
933
+ if (event.attributes.case !== 'workflowExecutionUpdateAdmittedEventAttributes') {
934
+ continue;
935
+ }
936
+ const attrs = event.attributes.value;
937
+ const invocation = await buildUpdateInvocationFromRequest({
938
+ request: attrs.request,
939
+ protocolInstanceId: 'history-admitted',
940
+ requestMessageId: `history-${event.eventId ?? 'update-admitted'}`,
941
+ sequencingEventId: event.eventId ? BigInt(event.eventId) : undefined,
942
+ fallbackEventId: event.eventId,
943
+ dataConverter,
944
+ });
945
+ if (invocation) {
946
+ invocations.push(invocation);
947
+ }
948
+ }
949
+ }
950
+ return invocations;
951
+ };
952
+ const buildUpdateInvocationFromRequest = async (input) => {
953
+ const req = input.request;
954
+ const updateId = normalizeUpdateId(req?.meta?.updateId);
955
+ const updateName = req?.input?.name?.trim();
956
+ if (!updateId || !updateName) {
957
+ return undefined;
958
+ }
959
+ const values = (await decodePayloadsToValues(input.dataConverter, req?.input?.args?.payloads ?? []))?.map((value) => value) ?? [];
960
+ const payload = normalizeUpdateInvocationPayload(values);
961
+ const sequencing = input.sequencingEventId !== undefined
962
+ ? input.sequencingEventId.toString()
963
+ : input.fallbackEventId !== undefined && input.fallbackEventId !== null
964
+ ? String(input.fallbackEventId)
965
+ : undefined;
966
+ return {
967
+ protocolInstanceId: input.protocolInstanceId ?? 'history-replay',
968
+ requestMessageId: input.requestMessageId ?? updateId,
969
+ updateId,
970
+ name: updateName,
971
+ payload,
972
+ identity: req?.meta?.identity,
973
+ sequencingEventId: sequencing,
974
+ };
975
+ };
976
+ const normalizeUpdateInvocationPayload = (values) => {
977
+ if (!values || values.length === 0) {
978
+ return undefined;
979
+ }
980
+ if (values.length === 1) {
981
+ return values[0];
982
+ }
983
+ return values;
984
+ };
553
985
  const decodePayloadArray = (converter, payloads) => Effect.tryPromise(async () => await decodePayloadsToValues(converter, payloads?.payloads ?? []));
986
+ const decodeMarkerDetails = (converter, details) => Effect.tryPromise(async () => {
987
+ if (!details || Object.keys(details).length === 0) {
988
+ return undefined;
989
+ }
990
+ const decoded = {};
991
+ for (const [key, payloads] of Object.entries(details)) {
992
+ const values = await decodePayloadsToValues(converter, payloads?.payloads ?? []);
993
+ if (values.length === 0) {
994
+ continue;
995
+ }
996
+ decoded[key] = values.length === 1 ? values[0] : values;
997
+ }
998
+ return Object.keys(decoded).length > 0 ? decoded : undefined;
999
+ });
1000
+ const decodeSearchAttributes = (converter, input) => Effect.tryPromise(async () => {
1001
+ const fields = input?.indexedFields;
1002
+ if (!fields || Object.keys(fields).length === 0) {
1003
+ return undefined;
1004
+ }
1005
+ const decoded = {};
1006
+ for (const [key, payload] of Object.entries(fields)) {
1007
+ const values = await decodePayloadsToValues(converter, payload ? [payload] : []);
1008
+ if (values.length === 0) {
1009
+ continue;
1010
+ }
1011
+ decoded[key] = values.length === 1 ? values[0] : values;
1012
+ }
1013
+ return decoded;
1014
+ });
1015
+ const decodeMemo = (converter, memo) => Effect.tryPromise(async () => {
1016
+ const decoded = {};
1017
+ const fields = memo?.fields ?? {};
1018
+ for (const [key, payload] of Object.entries(fields)) {
1019
+ const values = await decodePayloadsToValues(converter, payload ? [payload] : []);
1020
+ if (values.length === 0) {
1021
+ decoded[key] = undefined;
1022
+ }
1023
+ else {
1024
+ decoded[key] = values.length === 1 ? values[0] : values;
1025
+ }
1026
+ }
1027
+ return decoded;
1028
+ });
554
1029
  const convertRetryPolicy = (policy) => {
555
1030
  if (!policy) {
556
1031
  return undefined;
@@ -575,6 +1050,45 @@ const convertRetryPolicy = (policy) => {
575
1050
  ...(nonRetryable !== undefined ? { nonRetryableErrorTypes: nonRetryable } : {}),
576
1051
  };
577
1052
  };
1053
+ const normalizeUpdateId = (value) => {
1054
+ if (typeof value !== 'string') {
1055
+ return undefined;
1056
+ }
1057
+ const trimmed = value.trim();
1058
+ return trimmed.length > 0 ? trimmed : undefined;
1059
+ };
1060
+ const normalizeOptionalNonEmptyString = (value) => {
1061
+ if (typeof value !== 'string') {
1062
+ return undefined;
1063
+ }
1064
+ const trimmed = value.trim();
1065
+ return trimmed.length > 0 ? trimmed : undefined;
1066
+ };
1067
+ const normalizeBigintIdentifier = (value) => {
1068
+ if (value === undefined || value === null) {
1069
+ return undefined;
1070
+ }
1071
+ if (typeof value === 'string') {
1072
+ return value.length > 0 ? value : undefined;
1073
+ }
1074
+ if (typeof value === 'number' && Number.isFinite(value)) {
1075
+ return value.toString();
1076
+ }
1077
+ if (typeof value === 'bigint') {
1078
+ return value.toString();
1079
+ }
1080
+ return undefined;
1081
+ };
1082
+ const resolveOutcomeStatus = (outcome) => {
1083
+ const caseName = outcome?.value?.case;
1084
+ if (caseName === 'success') {
1085
+ return 'success';
1086
+ }
1087
+ if (caseName === 'failure') {
1088
+ return 'failure';
1089
+ }
1090
+ return undefined;
1091
+ };
578
1092
  const sanitizeDeterminismMarkerEnvelope = (input) => {
579
1093
  const schemaVersion = input.schemaVersion;
580
1094
  if (schemaVersion !== DETERMINISM_MARKER_SCHEMA_VERSION) {
@@ -611,6 +1125,9 @@ const sanitizeDeterminismState = (value) => {
611
1125
  const commandHistoryRaw = value.commandHistory;
612
1126
  const randomValuesRaw = value.randomValues;
613
1127
  const timeValuesRaw = value.timeValues;
1128
+ const signalsRaw = Array.isArray(value.signals) ? value.signals : [];
1129
+ const queriesRaw = Array.isArray(value.queries) ? value.queries : [];
1130
+ const updatesRaw = value.updates;
614
1131
  if (!Array.isArray(commandHistoryRaw) || !Array.isArray(randomValuesRaw) || !Array.isArray(timeValuesRaw)) {
615
1132
  throw new Error('Determinism marker contained invalid determinism state shape');
616
1133
  }
@@ -627,11 +1144,17 @@ const sanitizeDeterminismState = (value) => {
627
1144
  const randomValues = randomValuesRaw.map((val, index) => coerceNumber(val, `determinism.randomValues[${index}]`));
628
1145
  const timeValues = timeValuesRaw.map((val, index) => coerceNumber(val, `determinism.timeValues[${index}]`));
629
1146
  const failureMetadata = sanitizeFailureMetadata(value.failureMetadata);
1147
+ const signals = signalsRaw.map((record, index) => sanitizeSignalRecord(record, index));
1148
+ const queries = queriesRaw.map((record, index) => sanitizeQueryRecord(record, index));
1149
+ const updates = sanitizeDeterminismUpdates(updatesRaw);
630
1150
  return {
631
1151
  commandHistory,
632
1152
  randomValues,
633
1153
  timeValues,
634
1154
  ...(failureMetadata ? { failureMetadata } : {}),
1155
+ signals,
1156
+ queries,
1157
+ ...(updates ? { updates } : {}),
635
1158
  };
636
1159
  };
637
1160
  const sanitizeCommandMetadata = (value, index) => {
@@ -678,6 +1201,81 @@ const sanitizeFailureMetadata = (value) => {
678
1201
  ...(retryState !== undefined ? { retryState } : {}),
679
1202
  };
680
1203
  };
1204
+ const sanitizeDeterminismUpdates = (value) => {
1205
+ if (value === undefined || value === null) {
1206
+ return undefined;
1207
+ }
1208
+ if (!Array.isArray(value)) {
1209
+ throw new Error('Determinism marker contained invalid updates list');
1210
+ }
1211
+ return value.map((entry, index) => {
1212
+ if (!isRecord(entry)) {
1213
+ throw new Error(`Determinism marker update entry ${index} is invalid`);
1214
+ }
1215
+ const updateId = coerceString(entry.updateId, `determinism.updates[${index}].updateId`);
1216
+ const stage = sanitizeUpdateStage(entry.stage, index);
1217
+ const handlerName = coerceOptionalTrimmedString(entry.handlerName, `determinism.updates[${index}].handlerName`);
1218
+ const identity = coerceOptionalTrimmedString(entry.identity, `determinism.updates[${index}].identity`);
1219
+ const sequencingEventId = coerceOptionalTrimmedString(entry.sequencingEventId, `determinism.updates[${index}].sequencingEventId`);
1220
+ const messageId = coerceOptionalTrimmedString(entry.messageId, `determinism.updates[${index}].messageId`);
1221
+ const acceptedEventId = coerceOptionalTrimmedString(entry.acceptedEventId, `determinism.updates[${index}].acceptedEventId`);
1222
+ const outcome = sanitizeUpdateOutcome(entry.outcome, index);
1223
+ const failureMessage = coerceOptionalTrimmedString(entry.failureMessage, `determinism.updates[${index}].failureMessage`);
1224
+ const historyEventId = coerceOptionalTrimmedString(entry.historyEventId, `determinism.updates[${index}].historyEventId`);
1225
+ return {
1226
+ updateId,
1227
+ stage,
1228
+ ...(handlerName ? { handlerName } : {}),
1229
+ ...(identity ? { identity } : {}),
1230
+ ...(sequencingEventId ? { sequencingEventId } : {}),
1231
+ ...(messageId ? { messageId } : {}),
1232
+ ...(acceptedEventId ? { acceptedEventId } : {}),
1233
+ ...(outcome ? { outcome } : {}),
1234
+ ...(failureMessage ? { failureMessage } : {}),
1235
+ ...(historyEventId ? { historyEventId } : {}),
1236
+ };
1237
+ });
1238
+ };
1239
+ const sanitizeSignalRecord = (value, index) => {
1240
+ if (!isRecord(value)) {
1241
+ throw new Error(`Determinism marker contained invalid signal entry at index ${index}`);
1242
+ }
1243
+ const signalName = coerceString(value.signalName, `determinism.signals[${index}].signalName`);
1244
+ const payloadHash = coerceString(value.payloadHash, `determinism.signals[${index}].payloadHash`);
1245
+ const handlerName = coerceOptionalString(value.handlerName, `determinism.signals[${index}].handlerName`);
1246
+ const eventId = normalizeOptionalEventId(value.eventId, `determinism.signals[${index}].eventId`);
1247
+ const workflowTaskCompletedEventId = normalizeOptionalEventId(value.workflowTaskCompletedEventId, `determinism.signals[${index}].workflowTaskCompletedEventId`);
1248
+ const identity = coerceOptionalString(value.identity, `determinism.signals[${index}].identity`);
1249
+ return {
1250
+ signalName,
1251
+ payloadHash,
1252
+ ...(handlerName ? { handlerName } : {}),
1253
+ ...(eventId ? { eventId } : {}),
1254
+ ...(workflowTaskCompletedEventId ? { workflowTaskCompletedEventId } : {}),
1255
+ ...(identity ? { identity } : {}),
1256
+ };
1257
+ };
1258
+ const sanitizeQueryRecord = (value, index) => {
1259
+ if (!isRecord(value)) {
1260
+ throw new Error(`Determinism marker contained invalid query entry at index ${index}`);
1261
+ }
1262
+ const queryName = coerceString(value.queryName, `determinism.queries[${index}].queryName`);
1263
+ const requestHash = coerceString(value.requestHash, `determinism.queries[${index}].requestHash`);
1264
+ const handlerName = coerceOptionalString(value.handlerName, `determinism.queries[${index}].handlerName`);
1265
+ const identity = coerceOptionalString(value.identity, `determinism.queries[${index}].identity`);
1266
+ const queryId = coerceOptionalString(value.queryId, `determinism.queries[${index}].queryId`);
1267
+ const resultHash = coerceOptionalString(value.resultHash, `determinism.queries[${index}].resultHash`);
1268
+ const failureHash = coerceOptionalString(value.failureHash, `determinism.queries[${index}].failureHash`);
1269
+ return {
1270
+ queryName,
1271
+ requestHash,
1272
+ ...(handlerName ? { handlerName } : {}),
1273
+ ...(identity ? { identity } : {}),
1274
+ ...(queryId ? { queryId } : {}),
1275
+ ...(resultHash ? { resultHash } : {}),
1276
+ ...(failureHash ? { failureHash } : {}),
1277
+ };
1278
+ };
681
1279
  const sanitizeRecordedAt = (value) => {
682
1280
  if (typeof value === 'string' && value.length > 0) {
683
1281
  return value;
@@ -717,6 +1315,14 @@ const coerceOptionalString = (value, label) => {
717
1315
  }
718
1316
  return value.length > 0 ? value : undefined;
719
1317
  };
1318
+ const coerceOptionalTrimmedString = (value, label) => {
1319
+ const raw = coerceOptionalString(value, label);
1320
+ if (raw === undefined) {
1321
+ return undefined;
1322
+ }
1323
+ const trimmed = raw.trim();
1324
+ return trimmed.length > 0 ? trimmed : undefined;
1325
+ };
720
1326
  const coerceNumber = (value, label) => {
721
1327
  if (typeof value === 'number' && Number.isFinite(value)) {
722
1328
  return value;
@@ -746,6 +1352,21 @@ const normalizeOptionalEventId = (value, label) => {
746
1352
  throw new Error(`Determinism marker contained invalid ${label}`);
747
1353
  }
748
1354
  };
1355
+ const sanitizeUpdateStage = (value, index) => {
1356
+ if (value === 'admitted' || value === 'accepted' || value === 'rejected' || value === 'completed') {
1357
+ return value;
1358
+ }
1359
+ throw new Error(`Determinism marker update entry ${index} contained invalid stage`);
1360
+ };
1361
+ const sanitizeUpdateOutcome = (value, index) => {
1362
+ if (value === undefined || value === null) {
1363
+ return undefined;
1364
+ }
1365
+ if (value === 'success' || value === 'failure') {
1366
+ return value;
1367
+ }
1368
+ throw new Error(`Determinism marker update entry ${index} contained invalid outcome`);
1369
+ };
749
1370
  const valuesEqual = (expected, actual) => {
750
1371
  if (expected === undefined && actual === undefined) {
751
1372
  return true;
@@ -755,5 +1376,48 @@ const valuesEqual = (expected, actual) => {
755
1376
  }
756
1377
  return Object.is(expected, actual);
757
1378
  };
1379
+ const updatesEqual = (expected, actual) => {
1380
+ if (!expected && !actual) {
1381
+ return true;
1382
+ }
1383
+ if (!expected || !actual) {
1384
+ return false;
1385
+ }
1386
+ return (expected.updateId === actual.updateId &&
1387
+ expected.stage === actual.stage &&
1388
+ expected.handlerName === actual.handlerName &&
1389
+ expected.identity === actual.identity &&
1390
+ expected.sequencingEventId === actual.sequencingEventId &&
1391
+ expected.messageId === actual.messageId &&
1392
+ expected.outcome === actual.outcome &&
1393
+ expected.failureMessage === actual.failureMessage);
1394
+ };
1395
+ const recordsMatch = (expected, actual) => {
1396
+ if (expected === undefined && actual === undefined) {
1397
+ return true;
1398
+ }
1399
+ if (expected === undefined || actual === undefined) {
1400
+ return false;
1401
+ }
1402
+ return stableStringify(expected) === stableStringify(actual);
1403
+ };
1404
+ const mergePendingQueryRequests = (state, requests) => {
1405
+ if (!requests || requests.length === 0) {
1406
+ return state;
1407
+ }
1408
+ const nextQueries = state.queries ? [...state.queries] : [];
1409
+ for (const request of requests) {
1410
+ nextQueries.push({
1411
+ queryName: request.name,
1412
+ requestHash: stableStringify(request.args ?? []),
1413
+ ...(request.metadata?.identity ? { identity: request.metadata.identity } : {}),
1414
+ ...(request.id ? { queryId: request.id } : {}),
1415
+ });
1416
+ }
1417
+ return {
1418
+ ...state,
1419
+ queries: nextQueries,
1420
+ };
1421
+ };
758
1422
  const isRecord = (value) => typeof value === 'object' && value !== null;
759
1423
  //# sourceMappingURL=replay.js.map