@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.
- package/README.md +95 -0
- package/dist/src/client/serialization.d.ts +30 -1
- package/dist/src/client/serialization.d.ts.map +1 -1
- package/dist/src/client/serialization.js +76 -3
- package/dist/src/client/serialization.js.map +1 -1
- package/dist/src/client/types.d.ts +26 -0
- package/dist/src/client/types.d.ts.map +1 -1
- package/dist/src/client.d.ts +10 -2
- package/dist/src/client.d.ts.map +1 -1
- package/dist/src/client.js +243 -1
- package/dist/src/client.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/worker/concurrency.d.ts +8 -0
- package/dist/src/worker/concurrency.d.ts.map +1 -1
- package/dist/src/worker/concurrency.js +5 -9
- package/dist/src/worker/concurrency.js.map +1 -1
- package/dist/src/worker/runtime.d.ts.map +1 -1
- package/dist/src/worker/runtime.js +303 -29
- package/dist/src/worker/runtime.js.map +1 -1
- package/dist/src/worker/update-protocol.d.ts +33 -0
- package/dist/src/worker/update-protocol.d.ts.map +1 -0
- package/dist/src/worker/update-protocol.js +228 -0
- package/dist/src/worker/update-protocol.js.map +1 -0
- package/dist/src/workflow/context.d.ts +52 -1
- package/dist/src/workflow/context.d.ts.map +1 -1
- package/dist/src/workflow/context.js +212 -2
- package/dist/src/workflow/context.js.map +1 -1
- package/dist/src/workflow/definition.d.ts +29 -3
- package/dist/src/workflow/definition.d.ts.map +1 -1
- package/dist/src/workflow/definition.js +30 -4
- package/dist/src/workflow/definition.js.map +1 -1
- package/dist/src/workflow/determinism.d.ts +59 -0
- package/dist/src/workflow/determinism.d.ts.map +1 -1
- package/dist/src/workflow/determinism.js +76 -1
- package/dist/src/workflow/determinism.js.map +1 -1
- package/dist/src/workflow/errors.d.ts +3 -0
- package/dist/src/workflow/errors.d.ts.map +1 -1
- package/dist/src/workflow/errors.js +6 -0
- package/dist/src/workflow/errors.js.map +1 -1
- package/dist/src/workflow/executor.d.ts +53 -0
- package/dist/src/workflow/executor.d.ts.map +1 -1
- package/dist/src/workflow/executor.js +237 -6
- package/dist/src/workflow/executor.js.map +1 -1
- package/dist/src/workflow/inbound.d.ts +84 -0
- package/dist/src/workflow/inbound.d.ts.map +1 -0
- package/dist/src/workflow/inbound.js +65 -0
- package/dist/src/workflow/inbound.js.map +1 -0
- package/dist/src/workflow/index.d.ts +1 -0
- package/dist/src/workflow/index.d.ts.map +1 -1
- package/dist/src/workflow/index.js +1 -0
- package/dist/src/workflow/index.js.map +1 -1
- package/dist/src/workflow/replay.d.ts +24 -2
- package/dist/src/workflow/replay.d.ts.map +1 -1
- package/dist/src/workflow/replay.js +459 -14
- package/dist/src/workflow/replay.js.map +1 -1
- package/dist/src/workflows/index.d.ts +1 -1
- package/dist/src/workflows/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@ import { decodePayloadsToValues, encodeValuesToPayloads } from '../common/payloa
|
|
|
5
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
...
|
|
84
|
-
|
|
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,12 @@ 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);
|
|
346
393
|
for (const event of events) {
|
|
347
394
|
switch (event.eventType) {
|
|
348
395
|
case EventType.ACTIVITY_TASK_SCHEDULED: {
|
|
@@ -401,6 +448,25 @@ const reconstructDeterminismState = (events, intake, failureMetadata) => Effect.
|
|
|
401
448
|
}
|
|
402
449
|
break;
|
|
403
450
|
}
|
|
451
|
+
case EventType.WORKFLOW_EXECUTION_SIGNALED: {
|
|
452
|
+
if (event.attributes?.case !== 'workflowExecutionSignaledEventAttributes') {
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
const attributes = event.attributes.value;
|
|
456
|
+
const payloads = yield* decodePayloadArray(intake.dataConverter, attributes.input);
|
|
457
|
+
const workflowTaskCompletedEventId = 'workflowTaskCompletedEventId' in attributes
|
|
458
|
+
? normalizeEventId(attributes
|
|
459
|
+
.workflowTaskCompletedEventId)
|
|
460
|
+
: null;
|
|
461
|
+
signalRecords.push({
|
|
462
|
+
signalName: attributes.signalName ?? 'unknown',
|
|
463
|
+
payloadHash: stableStringify(payloads),
|
|
464
|
+
eventId: normalizeEventId(event.eventId),
|
|
465
|
+
workflowTaskCompletedEventId,
|
|
466
|
+
identity: attributes.identity ?? null,
|
|
467
|
+
});
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
404
470
|
case EventType.WORKFLOW_EXECUTION_CONTINUED_AS_NEW: {
|
|
405
471
|
if (event.attributes?.case !== 'workflowExecutionContinuedAsNewEventAttributes') {
|
|
406
472
|
break;
|
|
@@ -419,15 +485,21 @@ const reconstructDeterminismState = (events, intake, failureMetadata) => Effect.
|
|
|
419
485
|
break;
|
|
420
486
|
}
|
|
421
487
|
}
|
|
488
|
+
let determinismState = {
|
|
489
|
+
commandHistory,
|
|
490
|
+
randomValues: [],
|
|
491
|
+
timeValues: [],
|
|
492
|
+
...(failureMetadata ? { failureMetadata } : {}),
|
|
493
|
+
signals: signalRecords,
|
|
494
|
+
queries: queryRecords,
|
|
495
|
+
...(updates.length > 0 ? { updates } : {}),
|
|
496
|
+
};
|
|
497
|
+
determinismState = mergePendingQueryRequests(determinismState, intake.queries);
|
|
422
498
|
return {
|
|
423
|
-
determinismState
|
|
424
|
-
commandHistory,
|
|
425
|
-
randomValues: [],
|
|
426
|
-
timeValues: [],
|
|
427
|
-
...(failureMetadata ? { failureMetadata } : {}),
|
|
428
|
-
},
|
|
499
|
+
determinismState,
|
|
429
500
|
lastEventId: resolveHistoryLastEventId(events),
|
|
430
501
|
hasDeterminismMarker: false,
|
|
502
|
+
...(replayUpdateInvocations.length > 0 ? { updates: replayUpdateInvocations } : {}),
|
|
431
503
|
};
|
|
432
504
|
});
|
|
433
505
|
const fromActivityTaskScheduled = (attributes, sequence, intake) => Effect.gen(function* () {
|
|
@@ -550,6 +622,190 @@ const fromContinueAsNew = (attributes, sequence, intake) => Effect.gen(function*
|
|
|
550
622
|
};
|
|
551
623
|
return intent;
|
|
552
624
|
});
|
|
625
|
+
const collectWorkflowUpdateEntries = (events) => {
|
|
626
|
+
const updates = [];
|
|
627
|
+
for (const event of events) {
|
|
628
|
+
if (!event.attributes) {
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
const historyEventId = normalizeEventId(event.eventId) ?? undefined;
|
|
632
|
+
switch (event.eventType) {
|
|
633
|
+
case EventType.WORKFLOW_EXECUTION_UPDATE_ADMITTED: {
|
|
634
|
+
if (event.attributes.case !== 'workflowExecutionUpdateAdmittedEventAttributes') {
|
|
635
|
+
break;
|
|
636
|
+
}
|
|
637
|
+
const attrs = event.attributes.value;
|
|
638
|
+
const updateId = normalizeUpdateId(attrs.request?.meta?.updateId);
|
|
639
|
+
if (!updateId) {
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
updates.push({
|
|
643
|
+
updateId,
|
|
644
|
+
stage: 'admitted',
|
|
645
|
+
handlerName: normalizeOptionalNonEmptyString(attrs.request?.input?.name),
|
|
646
|
+
identity: normalizeOptionalNonEmptyString(attrs.request?.meta?.identity),
|
|
647
|
+
historyEventId,
|
|
648
|
+
});
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
case EventType.WORKFLOW_EXECUTION_UPDATE_ACCEPTED: {
|
|
652
|
+
if (event.attributes.case !== 'workflowExecutionUpdateAcceptedEventAttributes') {
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
655
|
+
const attrs = event.attributes.value;
|
|
656
|
+
const updateId = normalizeUpdateId(attrs.acceptedRequest?.meta?.updateId);
|
|
657
|
+
if (!updateId) {
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
updates.push({
|
|
661
|
+
updateId,
|
|
662
|
+
stage: 'accepted',
|
|
663
|
+
handlerName: normalizeOptionalNonEmptyString(attrs.acceptedRequest?.input?.name),
|
|
664
|
+
identity: normalizeOptionalNonEmptyString(attrs.acceptedRequest?.meta?.identity),
|
|
665
|
+
messageId: normalizeOptionalNonEmptyString(attrs.acceptedRequestMessageId),
|
|
666
|
+
sequencingEventId: normalizeBigintIdentifier(attrs.acceptedRequestSequencingEventId),
|
|
667
|
+
historyEventId,
|
|
668
|
+
});
|
|
669
|
+
break;
|
|
670
|
+
}
|
|
671
|
+
case EventType.WORKFLOW_EXECUTION_UPDATE_REJECTED: {
|
|
672
|
+
if (event.attributes.case !== 'workflowExecutionUpdateRejectedEventAttributes') {
|
|
673
|
+
break;
|
|
674
|
+
}
|
|
675
|
+
const attrs = event.attributes.value;
|
|
676
|
+
const updateId = normalizeUpdateId(attrs.rejectedRequest?.meta?.updateId);
|
|
677
|
+
if (!updateId) {
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
updates.push({
|
|
681
|
+
updateId,
|
|
682
|
+
stage: 'rejected',
|
|
683
|
+
handlerName: normalizeOptionalNonEmptyString(attrs.rejectedRequest?.input?.name),
|
|
684
|
+
identity: normalizeOptionalNonEmptyString(attrs.rejectedRequest?.meta?.identity),
|
|
685
|
+
messageId: normalizeOptionalNonEmptyString(attrs.rejectedRequestMessageId),
|
|
686
|
+
sequencingEventId: normalizeBigintIdentifier(attrs.rejectedRequestSequencingEventId),
|
|
687
|
+
failureMessage: normalizeOptionalNonEmptyString(attrs.failure?.message),
|
|
688
|
+
historyEventId,
|
|
689
|
+
});
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
case EventType.WORKFLOW_EXECUTION_UPDATE_COMPLETED: {
|
|
693
|
+
if (event.attributes.case !== 'workflowExecutionUpdateCompletedEventAttributes') {
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
const attrs = event.attributes.value;
|
|
697
|
+
const updateId = normalizeUpdateId(attrs.meta?.updateId);
|
|
698
|
+
if (!updateId) {
|
|
699
|
+
break;
|
|
700
|
+
}
|
|
701
|
+
updates.push({
|
|
702
|
+
updateId,
|
|
703
|
+
stage: 'completed',
|
|
704
|
+
identity: normalizeOptionalNonEmptyString(attrs.meta?.identity),
|
|
705
|
+
acceptedEventId: normalizeBigintIdentifier(attrs.acceptedEventId),
|
|
706
|
+
outcome: resolveOutcomeStatus(attrs.outcome),
|
|
707
|
+
historyEventId,
|
|
708
|
+
});
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
711
|
+
default:
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return updates;
|
|
716
|
+
};
|
|
717
|
+
const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
|
|
718
|
+
const invocations = [];
|
|
719
|
+
for (const event of events) {
|
|
720
|
+
if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_ACCEPTED) {
|
|
721
|
+
if (event.attributes.case !== 'workflowExecutionUpdateAcceptedEventAttributes') {
|
|
722
|
+
continue;
|
|
723
|
+
}
|
|
724
|
+
const attrs = event.attributes.value;
|
|
725
|
+
const invocation = await buildUpdateInvocationFromRequest({
|
|
726
|
+
request: attrs.acceptedRequest,
|
|
727
|
+
protocolInstanceId: attrs.protocolInstanceId,
|
|
728
|
+
requestMessageId: attrs.acceptedRequestMessageId,
|
|
729
|
+
sequencingEventId: attrs.acceptedRequestSequencingEventId,
|
|
730
|
+
fallbackEventId: event.eventId,
|
|
731
|
+
dataConverter,
|
|
732
|
+
});
|
|
733
|
+
if (invocation) {
|
|
734
|
+
invocations.push(invocation);
|
|
735
|
+
}
|
|
736
|
+
continue;
|
|
737
|
+
}
|
|
738
|
+
if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_REJECTED) {
|
|
739
|
+
if (event.attributes.case !== 'workflowExecutionUpdateRejectedEventAttributes') {
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
const attrs = event.attributes.value;
|
|
743
|
+
const invocation = await buildUpdateInvocationFromRequest({
|
|
744
|
+
request: attrs.rejectedRequest,
|
|
745
|
+
protocolInstanceId: attrs.protocolInstanceId,
|
|
746
|
+
requestMessageId: attrs.rejectedRequestMessageId,
|
|
747
|
+
sequencingEventId: attrs.rejectedRequestSequencingEventId,
|
|
748
|
+
fallbackEventId: event.eventId,
|
|
749
|
+
dataConverter,
|
|
750
|
+
});
|
|
751
|
+
if (invocation) {
|
|
752
|
+
invocations.push(invocation);
|
|
753
|
+
}
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_ADMITTED) {
|
|
757
|
+
if (event.attributes.case !== 'workflowExecutionUpdateAdmittedEventAttributes') {
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
const attrs = event.attributes.value;
|
|
761
|
+
const invocation = await buildUpdateInvocationFromRequest({
|
|
762
|
+
request: attrs.request,
|
|
763
|
+
protocolInstanceId: 'history-admitted',
|
|
764
|
+
requestMessageId: `history-${event.eventId ?? 'update-admitted'}`,
|
|
765
|
+
sequencingEventId: event.eventId ? BigInt(event.eventId) : undefined,
|
|
766
|
+
fallbackEventId: event.eventId,
|
|
767
|
+
dataConverter,
|
|
768
|
+
});
|
|
769
|
+
if (invocation) {
|
|
770
|
+
invocations.push(invocation);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
return invocations;
|
|
775
|
+
};
|
|
776
|
+
const buildUpdateInvocationFromRequest = async (input) => {
|
|
777
|
+
const req = input.request;
|
|
778
|
+
const updateId = normalizeUpdateId(req?.meta?.updateId);
|
|
779
|
+
const updateName = req?.input?.name?.trim();
|
|
780
|
+
if (!updateId || !updateName) {
|
|
781
|
+
return undefined;
|
|
782
|
+
}
|
|
783
|
+
const values = (await decodePayloadsToValues(input.dataConverter, req?.input?.args?.payloads ?? []))?.map((value) => value) ?? [];
|
|
784
|
+
const payload = normalizeUpdateInvocationPayload(values);
|
|
785
|
+
const sequencing = input.sequencingEventId !== undefined
|
|
786
|
+
? input.sequencingEventId.toString()
|
|
787
|
+
: input.fallbackEventId !== undefined && input.fallbackEventId !== null
|
|
788
|
+
? String(input.fallbackEventId)
|
|
789
|
+
: undefined;
|
|
790
|
+
return {
|
|
791
|
+
protocolInstanceId: input.protocolInstanceId ?? 'history-replay',
|
|
792
|
+
requestMessageId: input.requestMessageId ?? updateId,
|
|
793
|
+
updateId,
|
|
794
|
+
name: updateName,
|
|
795
|
+
payload,
|
|
796
|
+
identity: req?.meta?.identity,
|
|
797
|
+
sequencingEventId: sequencing,
|
|
798
|
+
};
|
|
799
|
+
};
|
|
800
|
+
const normalizeUpdateInvocationPayload = (values) => {
|
|
801
|
+
if (!values || values.length === 0) {
|
|
802
|
+
return undefined;
|
|
803
|
+
}
|
|
804
|
+
if (values.length === 1) {
|
|
805
|
+
return values[0];
|
|
806
|
+
}
|
|
807
|
+
return values;
|
|
808
|
+
};
|
|
553
809
|
const decodePayloadArray = (converter, payloads) => Effect.tryPromise(async () => await decodePayloadsToValues(converter, payloads?.payloads ?? []));
|
|
554
810
|
const convertRetryPolicy = (policy) => {
|
|
555
811
|
if (!policy) {
|
|
@@ -575,6 +831,45 @@ const convertRetryPolicy = (policy) => {
|
|
|
575
831
|
...(nonRetryable !== undefined ? { nonRetryableErrorTypes: nonRetryable } : {}),
|
|
576
832
|
};
|
|
577
833
|
};
|
|
834
|
+
const normalizeUpdateId = (value) => {
|
|
835
|
+
if (typeof value !== 'string') {
|
|
836
|
+
return undefined;
|
|
837
|
+
}
|
|
838
|
+
const trimmed = value.trim();
|
|
839
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
840
|
+
};
|
|
841
|
+
const normalizeOptionalNonEmptyString = (value) => {
|
|
842
|
+
if (typeof value !== 'string') {
|
|
843
|
+
return undefined;
|
|
844
|
+
}
|
|
845
|
+
const trimmed = value.trim();
|
|
846
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
847
|
+
};
|
|
848
|
+
const normalizeBigintIdentifier = (value) => {
|
|
849
|
+
if (value === undefined || value === null) {
|
|
850
|
+
return undefined;
|
|
851
|
+
}
|
|
852
|
+
if (typeof value === 'string') {
|
|
853
|
+
return value.length > 0 ? value : undefined;
|
|
854
|
+
}
|
|
855
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
856
|
+
return value.toString();
|
|
857
|
+
}
|
|
858
|
+
if (typeof value === 'bigint') {
|
|
859
|
+
return value.toString();
|
|
860
|
+
}
|
|
861
|
+
return undefined;
|
|
862
|
+
};
|
|
863
|
+
const resolveOutcomeStatus = (outcome) => {
|
|
864
|
+
const caseName = outcome?.value?.case;
|
|
865
|
+
if (caseName === 'success') {
|
|
866
|
+
return 'success';
|
|
867
|
+
}
|
|
868
|
+
if (caseName === 'failure') {
|
|
869
|
+
return 'failure';
|
|
870
|
+
}
|
|
871
|
+
return undefined;
|
|
872
|
+
};
|
|
578
873
|
const sanitizeDeterminismMarkerEnvelope = (input) => {
|
|
579
874
|
const schemaVersion = input.schemaVersion;
|
|
580
875
|
if (schemaVersion !== DETERMINISM_MARKER_SCHEMA_VERSION) {
|
|
@@ -611,6 +906,9 @@ const sanitizeDeterminismState = (value) => {
|
|
|
611
906
|
const commandHistoryRaw = value.commandHistory;
|
|
612
907
|
const randomValuesRaw = value.randomValues;
|
|
613
908
|
const timeValuesRaw = value.timeValues;
|
|
909
|
+
const signalsRaw = Array.isArray(value.signals) ? value.signals : [];
|
|
910
|
+
const queriesRaw = Array.isArray(value.queries) ? value.queries : [];
|
|
911
|
+
const updatesRaw = value.updates;
|
|
614
912
|
if (!Array.isArray(commandHistoryRaw) || !Array.isArray(randomValuesRaw) || !Array.isArray(timeValuesRaw)) {
|
|
615
913
|
throw new Error('Determinism marker contained invalid determinism state shape');
|
|
616
914
|
}
|
|
@@ -627,11 +925,17 @@ const sanitizeDeterminismState = (value) => {
|
|
|
627
925
|
const randomValues = randomValuesRaw.map((val, index) => coerceNumber(val, `determinism.randomValues[${index}]`));
|
|
628
926
|
const timeValues = timeValuesRaw.map((val, index) => coerceNumber(val, `determinism.timeValues[${index}]`));
|
|
629
927
|
const failureMetadata = sanitizeFailureMetadata(value.failureMetadata);
|
|
928
|
+
const signals = signalsRaw.map((record, index) => sanitizeSignalRecord(record, index));
|
|
929
|
+
const queries = queriesRaw.map((record, index) => sanitizeQueryRecord(record, index));
|
|
930
|
+
const updates = sanitizeDeterminismUpdates(updatesRaw);
|
|
630
931
|
return {
|
|
631
932
|
commandHistory,
|
|
632
933
|
randomValues,
|
|
633
934
|
timeValues,
|
|
634
935
|
...(failureMetadata ? { failureMetadata } : {}),
|
|
936
|
+
signals,
|
|
937
|
+
queries,
|
|
938
|
+
...(updates ? { updates } : {}),
|
|
635
939
|
};
|
|
636
940
|
};
|
|
637
941
|
const sanitizeCommandMetadata = (value, index) => {
|
|
@@ -678,6 +982,81 @@ const sanitizeFailureMetadata = (value) => {
|
|
|
678
982
|
...(retryState !== undefined ? { retryState } : {}),
|
|
679
983
|
};
|
|
680
984
|
};
|
|
985
|
+
const sanitizeDeterminismUpdates = (value) => {
|
|
986
|
+
if (value === undefined || value === null) {
|
|
987
|
+
return undefined;
|
|
988
|
+
}
|
|
989
|
+
if (!Array.isArray(value)) {
|
|
990
|
+
throw new Error('Determinism marker contained invalid updates list');
|
|
991
|
+
}
|
|
992
|
+
return value.map((entry, index) => {
|
|
993
|
+
if (!isRecord(entry)) {
|
|
994
|
+
throw new Error(`Determinism marker update entry ${index} is invalid`);
|
|
995
|
+
}
|
|
996
|
+
const updateId = coerceString(entry.updateId, `determinism.updates[${index}].updateId`);
|
|
997
|
+
const stage = sanitizeUpdateStage(entry.stage, index);
|
|
998
|
+
const handlerName = coerceOptionalTrimmedString(entry.handlerName, `determinism.updates[${index}].handlerName`);
|
|
999
|
+
const identity = coerceOptionalTrimmedString(entry.identity, `determinism.updates[${index}].identity`);
|
|
1000
|
+
const sequencingEventId = coerceOptionalTrimmedString(entry.sequencingEventId, `determinism.updates[${index}].sequencingEventId`);
|
|
1001
|
+
const messageId = coerceOptionalTrimmedString(entry.messageId, `determinism.updates[${index}].messageId`);
|
|
1002
|
+
const acceptedEventId = coerceOptionalTrimmedString(entry.acceptedEventId, `determinism.updates[${index}].acceptedEventId`);
|
|
1003
|
+
const outcome = sanitizeUpdateOutcome(entry.outcome, index);
|
|
1004
|
+
const failureMessage = coerceOptionalTrimmedString(entry.failureMessage, `determinism.updates[${index}].failureMessage`);
|
|
1005
|
+
const historyEventId = coerceOptionalTrimmedString(entry.historyEventId, `determinism.updates[${index}].historyEventId`);
|
|
1006
|
+
return {
|
|
1007
|
+
updateId,
|
|
1008
|
+
stage,
|
|
1009
|
+
...(handlerName ? { handlerName } : {}),
|
|
1010
|
+
...(identity ? { identity } : {}),
|
|
1011
|
+
...(sequencingEventId ? { sequencingEventId } : {}),
|
|
1012
|
+
...(messageId ? { messageId } : {}),
|
|
1013
|
+
...(acceptedEventId ? { acceptedEventId } : {}),
|
|
1014
|
+
...(outcome ? { outcome } : {}),
|
|
1015
|
+
...(failureMessage ? { failureMessage } : {}),
|
|
1016
|
+
...(historyEventId ? { historyEventId } : {}),
|
|
1017
|
+
};
|
|
1018
|
+
});
|
|
1019
|
+
};
|
|
1020
|
+
const sanitizeSignalRecord = (value, index) => {
|
|
1021
|
+
if (!isRecord(value)) {
|
|
1022
|
+
throw new Error(`Determinism marker contained invalid signal entry at index ${index}`);
|
|
1023
|
+
}
|
|
1024
|
+
const signalName = coerceString(value.signalName, `determinism.signals[${index}].signalName`);
|
|
1025
|
+
const payloadHash = coerceString(value.payloadHash, `determinism.signals[${index}].payloadHash`);
|
|
1026
|
+
const handlerName = coerceOptionalString(value.handlerName, `determinism.signals[${index}].handlerName`);
|
|
1027
|
+
const eventId = normalizeOptionalEventId(value.eventId, `determinism.signals[${index}].eventId`);
|
|
1028
|
+
const workflowTaskCompletedEventId = normalizeOptionalEventId(value.workflowTaskCompletedEventId, `determinism.signals[${index}].workflowTaskCompletedEventId`);
|
|
1029
|
+
const identity = coerceOptionalString(value.identity, `determinism.signals[${index}].identity`);
|
|
1030
|
+
return {
|
|
1031
|
+
signalName,
|
|
1032
|
+
payloadHash,
|
|
1033
|
+
...(handlerName ? { handlerName } : {}),
|
|
1034
|
+
...(eventId ? { eventId } : {}),
|
|
1035
|
+
...(workflowTaskCompletedEventId ? { workflowTaskCompletedEventId } : {}),
|
|
1036
|
+
...(identity ? { identity } : {}),
|
|
1037
|
+
};
|
|
1038
|
+
};
|
|
1039
|
+
const sanitizeQueryRecord = (value, index) => {
|
|
1040
|
+
if (!isRecord(value)) {
|
|
1041
|
+
throw new Error(`Determinism marker contained invalid query entry at index ${index}`);
|
|
1042
|
+
}
|
|
1043
|
+
const queryName = coerceString(value.queryName, `determinism.queries[${index}].queryName`);
|
|
1044
|
+
const requestHash = coerceString(value.requestHash, `determinism.queries[${index}].requestHash`);
|
|
1045
|
+
const handlerName = coerceOptionalString(value.handlerName, `determinism.queries[${index}].handlerName`);
|
|
1046
|
+
const identity = coerceOptionalString(value.identity, `determinism.queries[${index}].identity`);
|
|
1047
|
+
const queryId = coerceOptionalString(value.queryId, `determinism.queries[${index}].queryId`);
|
|
1048
|
+
const resultHash = coerceOptionalString(value.resultHash, `determinism.queries[${index}].resultHash`);
|
|
1049
|
+
const failureHash = coerceOptionalString(value.failureHash, `determinism.queries[${index}].failureHash`);
|
|
1050
|
+
return {
|
|
1051
|
+
queryName,
|
|
1052
|
+
requestHash,
|
|
1053
|
+
...(handlerName ? { handlerName } : {}),
|
|
1054
|
+
...(identity ? { identity } : {}),
|
|
1055
|
+
...(queryId ? { queryId } : {}),
|
|
1056
|
+
...(resultHash ? { resultHash } : {}),
|
|
1057
|
+
...(failureHash ? { failureHash } : {}),
|
|
1058
|
+
};
|
|
1059
|
+
};
|
|
681
1060
|
const sanitizeRecordedAt = (value) => {
|
|
682
1061
|
if (typeof value === 'string' && value.length > 0) {
|
|
683
1062
|
return value;
|
|
@@ -717,6 +1096,14 @@ const coerceOptionalString = (value, label) => {
|
|
|
717
1096
|
}
|
|
718
1097
|
return value.length > 0 ? value : undefined;
|
|
719
1098
|
};
|
|
1099
|
+
const coerceOptionalTrimmedString = (value, label) => {
|
|
1100
|
+
const raw = coerceOptionalString(value, label);
|
|
1101
|
+
if (raw === undefined) {
|
|
1102
|
+
return undefined;
|
|
1103
|
+
}
|
|
1104
|
+
const trimmed = raw.trim();
|
|
1105
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
1106
|
+
};
|
|
720
1107
|
const coerceNumber = (value, label) => {
|
|
721
1108
|
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
722
1109
|
return value;
|
|
@@ -746,6 +1133,21 @@ const normalizeOptionalEventId = (value, label) => {
|
|
|
746
1133
|
throw new Error(`Determinism marker contained invalid ${label}`);
|
|
747
1134
|
}
|
|
748
1135
|
};
|
|
1136
|
+
const sanitizeUpdateStage = (value, index) => {
|
|
1137
|
+
if (value === 'admitted' || value === 'accepted' || value === 'rejected' || value === 'completed') {
|
|
1138
|
+
return value;
|
|
1139
|
+
}
|
|
1140
|
+
throw new Error(`Determinism marker update entry ${index} contained invalid stage`);
|
|
1141
|
+
};
|
|
1142
|
+
const sanitizeUpdateOutcome = (value, index) => {
|
|
1143
|
+
if (value === undefined || value === null) {
|
|
1144
|
+
return undefined;
|
|
1145
|
+
}
|
|
1146
|
+
if (value === 'success' || value === 'failure') {
|
|
1147
|
+
return value;
|
|
1148
|
+
}
|
|
1149
|
+
throw new Error(`Determinism marker update entry ${index} contained invalid outcome`);
|
|
1150
|
+
};
|
|
749
1151
|
const valuesEqual = (expected, actual) => {
|
|
750
1152
|
if (expected === undefined && actual === undefined) {
|
|
751
1153
|
return true;
|
|
@@ -755,5 +1157,48 @@ const valuesEqual = (expected, actual) => {
|
|
|
755
1157
|
}
|
|
756
1158
|
return Object.is(expected, actual);
|
|
757
1159
|
};
|
|
1160
|
+
const updatesEqual = (expected, actual) => {
|
|
1161
|
+
if (!expected && !actual) {
|
|
1162
|
+
return true;
|
|
1163
|
+
}
|
|
1164
|
+
if (!expected || !actual) {
|
|
1165
|
+
return false;
|
|
1166
|
+
}
|
|
1167
|
+
return (expected.updateId === actual.updateId &&
|
|
1168
|
+
expected.stage === actual.stage &&
|
|
1169
|
+
expected.handlerName === actual.handlerName &&
|
|
1170
|
+
expected.identity === actual.identity &&
|
|
1171
|
+
expected.sequencingEventId === actual.sequencingEventId &&
|
|
1172
|
+
expected.messageId === actual.messageId &&
|
|
1173
|
+
expected.outcome === actual.outcome &&
|
|
1174
|
+
expected.failureMessage === actual.failureMessage);
|
|
1175
|
+
};
|
|
1176
|
+
const recordsMatch = (expected, actual) => {
|
|
1177
|
+
if (expected === undefined && actual === undefined) {
|
|
1178
|
+
return true;
|
|
1179
|
+
}
|
|
1180
|
+
if (expected === undefined || actual === undefined) {
|
|
1181
|
+
return false;
|
|
1182
|
+
}
|
|
1183
|
+
return stableStringify(expected) === stableStringify(actual);
|
|
1184
|
+
};
|
|
1185
|
+
const mergePendingQueryRequests = (state, requests) => {
|
|
1186
|
+
if (!requests || requests.length === 0) {
|
|
1187
|
+
return state;
|
|
1188
|
+
}
|
|
1189
|
+
const nextQueries = state.queries ? [...state.queries] : [];
|
|
1190
|
+
for (const request of requests) {
|
|
1191
|
+
nextQueries.push({
|
|
1192
|
+
queryName: request.name,
|
|
1193
|
+
requestHash: stableStringify(request.args ?? []),
|
|
1194
|
+
...(request.metadata?.identity ? { identity: request.metadata.identity } : {}),
|
|
1195
|
+
...(request.id ? { queryId: request.id } : {}),
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
return {
|
|
1199
|
+
...state,
|
|
1200
|
+
queries: nextQueries,
|
|
1201
|
+
};
|
|
1202
|
+
};
|
|
758
1203
|
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
759
1204
|
//# sourceMappingURL=replay.js.map
|