@proompteng/temporal-bun-sdk 0.4.0 → 0.5.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 +8 -1
- package/dist/src/bin/temporal-bun.js +1 -1
- package/dist/src/common/payloads/converter.js +1 -1
- package/dist/src/common/payloads/converter.js.map +1 -1
- package/dist/src/common/payloads/json-codec.d.ts.map +1 -1
- package/dist/src/common/payloads/json-codec.js +4 -6
- package/dist/src/common/payloads/json-codec.js.map +1 -1
- package/dist/src/config.d.ts +16 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +49 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/interceptors/client.d.ts.map +1 -1
- package/dist/src/interceptors/client.js +24 -3
- package/dist/src/interceptors/client.js.map +1 -1
- package/dist/src/interceptors/worker.d.ts.map +1 -1
- package/dist/src/interceptors/worker.js +24 -3
- package/dist/src/interceptors/worker.js.map +1 -1
- package/dist/src/observability/index.d.ts +4 -0
- package/dist/src/observability/index.d.ts.map +1 -1
- package/dist/src/observability/index.js +4 -0
- package/dist/src/observability/index.js.map +1 -1
- package/dist/src/observability/metrics.d.ts.map +1 -1
- package/dist/src/observability/metrics.js +70 -10
- package/dist/src/observability/metrics.js.map +1 -1
- package/dist/src/observability/opentelemetry.d.ts +20 -0
- package/dist/src/observability/opentelemetry.d.ts.map +1 -0
- package/dist/src/observability/opentelemetry.js +334 -0
- package/dist/src/observability/opentelemetry.js.map +1 -0
- package/dist/src/proto/temporal/api/activity/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/batch/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/command/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/common/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/deployment/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/batch_operation_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/command_type_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/common_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/deployment_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/event_type_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/failed_cause_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/namespace_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/nexus_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/query_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/reset_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/schedule_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/task_queue_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/update_pb.js +1 -1
- package/dist/src/proto/temporal/api/enums/v1/workflow_pb.js +1 -1
- package/dist/src/proto/temporal/api/errordetails/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/export/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/failure/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/filter/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/history/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/namespace/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/nexus/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/operatorservice/v1/request_response_pb.js +1 -1
- package/dist/src/proto/temporal/api/operatorservice/v1/service_pb.js +1 -1
- package/dist/src/proto/temporal/api/protocol/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/query/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/replication/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/rules/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/enhanced_stack_trace_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/task_complete_metadata_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/user_metadata_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/worker_config_pb.js +1 -1
- package/dist/src/proto/temporal/api/sdk/v1/workflow_metadata_pb.js +1 -1
- package/dist/src/proto/temporal/api/taskqueue/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/update/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/version/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/worker/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/workflow/v1/message_pb.js +1 -1
- package/dist/src/proto/temporal/api/workflowservice/v1/request_response_pb.js +1 -1
- package/dist/src/proto/temporal/api/workflowservice/v1/service_pb.js +1 -1
- package/dist/src/worker/runtime.d.ts +1 -0
- package/dist/src/worker/runtime.d.ts.map +1 -1
- package/dist/src/worker/runtime.js +649 -61
- package/dist/src/worker/runtime.js.map +1 -1
- package/dist/src/worker/sticky-cache.d.ts +10 -0
- package/dist/src/worker/sticky-cache.d.ts.map +1 -1
- package/dist/src/worker/sticky-cache.js.map +1 -1
- package/dist/src/workflow/commands.d.ts +2 -1
- package/dist/src/workflow/commands.d.ts.map +1 -1
- package/dist/src/workflow/commands.js +9 -7
- package/dist/src/workflow/commands.js.map +1 -1
- package/dist/src/workflow/context.d.ts.map +1 -1
- package/dist/src/workflow/context.js +28 -9
- package/dist/src/workflow/context.js.map +1 -1
- package/dist/src/workflow/definition.js.map +1 -1
- package/dist/src/workflow/determinism.d.ts +4 -0
- package/dist/src/workflow/determinism.d.ts.map +1 -1
- package/dist/src/workflow/determinism.js +104 -17
- package/dist/src/workflow/determinism.js.map +1 -1
- package/dist/src/workflow/executor.d.ts +3 -0
- package/dist/src/workflow/executor.d.ts.map +1 -1
- package/dist/src/workflow/executor.js +80 -11
- package/dist/src/workflow/executor.js.map +1 -1
- package/dist/src/workflow/inbound.d.ts +1 -0
- package/dist/src/workflow/inbound.d.ts.map +1 -1
- package/dist/src/workflow/inbound.js +1 -0
- package/dist/src/workflow/inbound.js.map +1 -1
- package/dist/src/workflow/index.d.ts +5 -0
- package/dist/src/workflow/index.d.ts.map +1 -1
- package/dist/src/workflow/index.js +3 -0
- package/dist/src/workflow/index.js.map +1 -1
- package/dist/src/workflow/log.d.ts +18 -0
- package/dist/src/workflow/log.d.ts.map +1 -0
- package/dist/src/workflow/log.js +36 -0
- package/dist/src/workflow/log.js.map +1 -0
- package/dist/src/workflow/replay.d.ts +31 -3
- package/dist/src/workflow/replay.d.ts.map +1 -1
- package/dist/src/workflow/replay.js +543 -66
- package/dist/src/workflow/replay.js.map +1 -1
- package/package.json +4 -3
|
@@ -7,7 +7,8 @@ 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
8
|
import { intentsEqual, stableStringify } from './determinism';
|
|
9
9
|
export const DETERMINISM_MARKER_NAME = 'temporal-bun-sdk/determinism';
|
|
10
|
-
const
|
|
10
|
+
const DETERMINISM_MARKER_SCHEMA_VERSION_V1 = 1;
|
|
11
|
+
const DETERMINISM_MARKER_SCHEMA_VERSION_V2 = 2;
|
|
11
12
|
const DETERMINISM_MARKER_DETAIL_KEY = 'snapshot';
|
|
12
13
|
/**
|
|
13
14
|
* Encode a workflow determinism snapshot into a marker payload map suitable for
|
|
@@ -15,13 +16,25 @@ const DETERMINISM_MARKER_DETAIL_KEY = 'snapshot';
|
|
|
15
16
|
*/
|
|
16
17
|
export const encodeDeterminismMarkerDetails = (converter, input) => Effect.tryPromise(async () => {
|
|
17
18
|
const recordedAt = (input.recordedAt ?? new Date()).toISOString();
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
const markerType = input.markerType ?? 'full';
|
|
20
|
+
if (markerType === 'delta' && !input.determinismDelta) {
|
|
21
|
+
throw new Error('Determinism marker delta payload missing');
|
|
22
|
+
}
|
|
23
|
+
const envelope = markerType === 'delta'
|
|
24
|
+
? {
|
|
25
|
+
schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V2,
|
|
26
|
+
workflow: input.info,
|
|
27
|
+
determinismDelta: input.determinismDelta,
|
|
28
|
+
lastEventId: normalizeEventId(input.lastEventId),
|
|
29
|
+
recordedAtIso: recordedAt,
|
|
30
|
+
}
|
|
31
|
+
: {
|
|
32
|
+
schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V1,
|
|
33
|
+
workflow: input.info,
|
|
34
|
+
determinismState: input.determinismState,
|
|
35
|
+
lastEventId: normalizeEventId(input.lastEventId),
|
|
36
|
+
recordedAtIso: recordedAt,
|
|
37
|
+
};
|
|
25
38
|
const payloads = await encodeValuesToPayloads(converter, [envelope]);
|
|
26
39
|
if (!payloads || payloads.length === 0) {
|
|
27
40
|
throw new Error('Failed to encode determinism marker payloads');
|
|
@@ -30,6 +43,20 @@ export const encodeDeterminismMarkerDetails = (converter, input) => Effect.tryPr
|
|
|
30
43
|
[DETERMINISM_MARKER_DETAIL_KEY]: create(PayloadsSchema, { payloads }),
|
|
31
44
|
};
|
|
32
45
|
});
|
|
46
|
+
export const measurePayloadsByteSize = (details) => {
|
|
47
|
+
let total = 0;
|
|
48
|
+
for (const payloads of Object.values(details)) {
|
|
49
|
+
for (const payload of payloads.payloads ?? []) {
|
|
50
|
+
total += payload.data?.byteLength ?? 0;
|
|
51
|
+
const metadata = payload.metadata ?? {};
|
|
52
|
+
for (const value of Object.values(metadata)) {
|
|
53
|
+
total += value?.byteLength ?? 0;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return total;
|
|
58
|
+
};
|
|
59
|
+
export const encodeDeterminismMarkerDetailsWithSize = (converter, input) => encodeDeterminismMarkerDetails(converter, input).pipe(Effect.map((details) => ({ details, sizeBytes: measurePayloadsByteSize(details) })));
|
|
33
60
|
/**
|
|
34
61
|
* Decode determinism snapshot metadata from a marker payload map. Returns
|
|
35
62
|
* `undefined` when the supplied details do not contain a determinism snapshot.
|
|
@@ -57,10 +84,14 @@ export const decodeDeterminismMarkerEnvelope = (input) => Effect.tryPromise(asyn
|
|
|
57
84
|
export const ingestWorkflowHistory = (intake) => Effect.gen(function* () {
|
|
58
85
|
const events = sortHistoryEvents(intake.history ?? []);
|
|
59
86
|
const shouldUseDeterminismMarker = intake.ignoreDeterminismMarker !== true;
|
|
60
|
-
let
|
|
61
|
-
|
|
87
|
+
let markerState;
|
|
88
|
+
let markerLastEventId = null;
|
|
89
|
+
let markerEventId = null;
|
|
90
|
+
let markerEventIndex = null;
|
|
91
|
+
let markerInvalid = false;
|
|
92
|
+
let hasMarker = false;
|
|
62
93
|
if (shouldUseDeterminismMarker) {
|
|
63
|
-
for (const event of events) {
|
|
94
|
+
for (const [index, event] of events.entries()) {
|
|
64
95
|
if (event.eventType !== EventType.MARKER_RECORDED) {
|
|
65
96
|
continue;
|
|
66
97
|
}
|
|
@@ -72,36 +103,82 @@ export const ingestWorkflowHistory = (intake) => Effect.gen(function* () {
|
|
|
72
103
|
details: event.attributes.value.details,
|
|
73
104
|
}), () => Effect.succeed(undefined));
|
|
74
105
|
if (decoded) {
|
|
75
|
-
|
|
106
|
+
const applied = applyDeterminismMarker(markerState, decoded);
|
|
107
|
+
if (!applied) {
|
|
108
|
+
markerInvalid = true;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
markerState = applied;
|
|
112
|
+
markerLastEventId = decoded.lastEventId;
|
|
113
|
+
markerEventId = normalizeEventId(event.eventId);
|
|
114
|
+
markerEventIndex = index;
|
|
115
|
+
hasMarker = true;
|
|
76
116
|
}
|
|
77
117
|
}
|
|
78
118
|
}
|
|
79
119
|
const extractedFailureMetadata = extractFailureMetadata(events);
|
|
80
|
-
const
|
|
81
|
-
if (shouldUseDeterminismMarker &&
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
};
|
|
120
|
+
const historyLastEventId = resolveHistoryLastEventId(events) ?? null;
|
|
121
|
+
if (shouldUseDeterminismMarker && hasMarker && !markerInvalid && markerState && markerEventIndex !== null) {
|
|
122
|
+
const historyKinds = collectCommandKinds(events.slice(0, markerEventIndex + 1));
|
|
123
|
+
const markerKinds = markerState.commandHistory.map((entry) => entry.intent.kind);
|
|
124
|
+
if (!commandKindsMatch(markerKinds, historyKinds)) {
|
|
125
|
+
markerInvalid = true;
|
|
126
|
+
markerState = undefined;
|
|
88
127
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
128
|
+
}
|
|
129
|
+
const eventsAfterMarker = shouldUseDeterminismMarker && hasMarker && markerEventIndex !== null ? events.slice(markerEventIndex + 1) : events;
|
|
130
|
+
const replayUpdateInvocationsAfterMarker = yield* Effect.tryPromise(async () => collectWorkflowUpdateInvocations(eventsAfterMarker, intake.dataConverter));
|
|
131
|
+
const updateEntriesAfterMarker = collectWorkflowUpdateEntries(eventsAfterMarker);
|
|
132
|
+
const replayUpdateInvocationsFull = yield* Effect.tryPromise(async () => collectWorkflowUpdateInvocations(events, intake.dataConverter));
|
|
133
|
+
const updateEntriesFull = collectWorkflowUpdateEntries(events);
|
|
134
|
+
const markerCoversHistory = markerEventId !== null && markerEventId === historyLastEventId;
|
|
135
|
+
const historyCommandCount = countHistoryCommandEvents(events);
|
|
136
|
+
if (shouldUseDeterminismMarker && hasMarker && !markerInvalid && markerState) {
|
|
137
|
+
if (markerCoversHistory || eventsAfterMarker.length === 0) {
|
|
138
|
+
let determinismState = cloneDeterminismState(markerState);
|
|
139
|
+
if (!determinismState.failureMetadata && extractedFailureMetadata) {
|
|
140
|
+
determinismState = {
|
|
141
|
+
...determinismState,
|
|
142
|
+
failureMetadata: extractedFailureMetadata,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
if ((!determinismState.updates || determinismState.updates.length === 0) &&
|
|
146
|
+
updateEntriesAfterMarker.length > 0) {
|
|
147
|
+
determinismState = {
|
|
148
|
+
...determinismState,
|
|
149
|
+
updates: updateEntriesAfterMarker,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
determinismState = mergePendingQueryRequests(determinismState, intake.queries);
|
|
153
|
+
const latestEventId = historyLastEventId ?? markerLastEventId ?? null;
|
|
154
|
+
if (determinismState.commandHistory.length !== historyCommandCount) {
|
|
155
|
+
return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntriesFull, replayUpdateInvocationsFull);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
determinismState,
|
|
159
|
+
lastEventId: latestEventId,
|
|
160
|
+
hasDeterminismMarker: true,
|
|
161
|
+
markerState: cloneDeterminismState(markerState),
|
|
162
|
+
...(replayUpdateInvocationsFull.length > 0 ? { updates: replayUpdateInvocationsFull } : {}),
|
|
93
163
|
};
|
|
94
164
|
}
|
|
95
|
-
|
|
96
|
-
|
|
165
|
+
const replayed = yield* reconstructDeterminismState(eventsAfterMarker, intake, extractedFailureMetadata, updateEntriesAfterMarker, replayUpdateInvocationsAfterMarker, {
|
|
166
|
+
seedState: markerState,
|
|
167
|
+
historyLastEventId,
|
|
168
|
+
seedHistoryEvents: markerEventIndex !== null ? events.slice(0, markerEventIndex + 1) : undefined,
|
|
169
|
+
});
|
|
170
|
+
if (replayed.determinismState.commandHistory.length !== historyCommandCount) {
|
|
171
|
+
return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntriesFull, replayUpdateInvocationsFull);
|
|
172
|
+
}
|
|
97
173
|
return {
|
|
98
|
-
|
|
99
|
-
lastEventId: latestEventId,
|
|
174
|
+
...replayed,
|
|
100
175
|
hasDeterminismMarker: true,
|
|
101
|
-
|
|
176
|
+
markerState: cloneDeterminismState(markerState),
|
|
177
|
+
lastEventId: historyLastEventId ?? markerLastEventId ?? null,
|
|
178
|
+
...(replayUpdateInvocationsFull.length > 0 ? { updates: replayUpdateInvocationsFull } : {}),
|
|
102
179
|
};
|
|
103
180
|
}
|
|
104
|
-
return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata,
|
|
181
|
+
return yield* reconstructDeterminismState(events, intake, extractedFailureMetadata, updateEntriesFull, replayUpdateInvocationsFull);
|
|
105
182
|
});
|
|
106
183
|
/**
|
|
107
184
|
* Diff determinism state against freshly emitted intents to produce rich
|
|
@@ -220,6 +297,7 @@ export const cloneDeterminismState = (state) => ({
|
|
|
220
297
|
})),
|
|
221
298
|
randomValues: [...state.randomValues],
|
|
222
299
|
timeValues: [...state.timeValues],
|
|
300
|
+
...(state.logCount !== undefined ? { logCount: state.logCount } : {}),
|
|
223
301
|
failureMetadata: state.failureMetadata ? { ...state.failureMetadata } : undefined,
|
|
224
302
|
signals: state.signals ? state.signals.map((record) => ({ ...record })) : [],
|
|
225
303
|
queries: state.queries ? state.queries.map((record) => ({ ...record })) : [],
|
|
@@ -384,14 +462,295 @@ const resolveCommandMismatchMetadata = (expectedEntry, actualEntry) => {
|
|
|
384
462
|
eventType,
|
|
385
463
|
};
|
|
386
464
|
};
|
|
387
|
-
const
|
|
388
|
-
let sequence = 0;
|
|
389
|
-
const commandHistory = [];
|
|
390
|
-
const signalRecords = [];
|
|
391
|
-
const queryRecords = [];
|
|
392
|
-
const updates = precomputedUpdates ?? collectWorkflowUpdateEntries(events);
|
|
465
|
+
const seedDeterminismEventMaps = (state, seedEvents) => {
|
|
393
466
|
const scheduledActivities = new Map();
|
|
394
467
|
const timersByStartEventId = new Map();
|
|
468
|
+
if (!state) {
|
|
469
|
+
if (seedEvents) {
|
|
470
|
+
seedDeterminismEventMapsFromHistory(scheduledActivities, timersByStartEventId, seedEvents);
|
|
471
|
+
}
|
|
472
|
+
return { scheduledActivities, timersByStartEventId };
|
|
473
|
+
}
|
|
474
|
+
for (const entry of state.commandHistory) {
|
|
475
|
+
const eventId = entry.metadata?.eventId;
|
|
476
|
+
if (!eventId) {
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
if (entry.intent.kind === 'schedule-activity') {
|
|
480
|
+
scheduledActivities.set(eventId, entry.intent.activityId);
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
if (entry.intent.kind === 'start-timer') {
|
|
484
|
+
timersByStartEventId.set(eventId, entry.intent.timerId);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (seedEvents) {
|
|
488
|
+
seedDeterminismEventMapsFromHistory(scheduledActivities, timersByStartEventId, seedEvents);
|
|
489
|
+
}
|
|
490
|
+
return { scheduledActivities, timersByStartEventId };
|
|
491
|
+
};
|
|
492
|
+
const seedDeterminismEventMapsFromHistory = (scheduledActivities, timersByStartEventId, events) => {
|
|
493
|
+
for (const event of events) {
|
|
494
|
+
switch (event.eventType) {
|
|
495
|
+
case EventType.ACTIVITY_TASK_SCHEDULED: {
|
|
496
|
+
if (event.attributes?.case !== 'activityTaskScheduledEventAttributes') {
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
const activityId = event.attributes.value.activityId;
|
|
500
|
+
const eventId = normalizeEventId(event.eventId);
|
|
501
|
+
if (activityId && eventId && !scheduledActivities.has(eventId)) {
|
|
502
|
+
scheduledActivities.set(eventId, activityId);
|
|
503
|
+
}
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
case EventType.TIMER_STARTED: {
|
|
507
|
+
if (event.attributes?.case !== 'timerStartedEventAttributes') {
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
const timerId = event.attributes.value.timerId;
|
|
511
|
+
const eventId = normalizeEventId(event.eventId);
|
|
512
|
+
if (timerId && eventId && !timersByStartEventId.has(eventId)) {
|
|
513
|
+
timersByStartEventId.set(eventId, timerId);
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
default:
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
const countHistoryCommandEvents = (events) => {
|
|
523
|
+
const { scheduledActivities, timersByStartEventId } = seedDeterminismEventMaps(undefined, events);
|
|
524
|
+
let count = 0;
|
|
525
|
+
for (const event of events) {
|
|
526
|
+
switch (event.eventType) {
|
|
527
|
+
case EventType.ACTIVITY_TASK_SCHEDULED: {
|
|
528
|
+
if (event.attributes?.case !== 'activityTaskScheduledEventAttributes') {
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
if (event.attributes.value.activityType?.name) {
|
|
532
|
+
count += 1;
|
|
533
|
+
}
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
case EventType.TIMER_STARTED: {
|
|
537
|
+
if (event.attributes?.case !== 'timerStartedEventAttributes') {
|
|
538
|
+
break;
|
|
539
|
+
}
|
|
540
|
+
if (durationToMillis(event.attributes.value.startToFireTimeout) !== undefined) {
|
|
541
|
+
count += 1;
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
case EventType.START_CHILD_WORKFLOW_EXECUTION_INITIATED: {
|
|
546
|
+
if (event.attributes?.case !== 'startChildWorkflowExecutionInitiatedEventAttributes') {
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
if (event.attributes.value.workflowType?.name) {
|
|
550
|
+
count += 1;
|
|
551
|
+
}
|
|
552
|
+
break;
|
|
553
|
+
}
|
|
554
|
+
case EventType.ACTIVITY_TASK_CANCEL_REQUESTED: {
|
|
555
|
+
if (event.attributes?.case !== 'activityTaskCancelRequestedEventAttributes') {
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
const scheduledEventId = normalizeBigintIdentifier(event.attributes.value.scheduledEventId);
|
|
559
|
+
if (scheduledEventId && scheduledActivities.has(scheduledEventId)) {
|
|
560
|
+
count += 1;
|
|
561
|
+
}
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
case EventType.TIMER_CANCELED: {
|
|
565
|
+
if (event.attributes?.case !== 'timerCanceledEventAttributes') {
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
const startedEventId = normalizeBigintIdentifier(event.attributes.value.startedEventId);
|
|
569
|
+
if (startedEventId && timersByStartEventId.has(startedEventId)) {
|
|
570
|
+
count += 1;
|
|
571
|
+
}
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
case EventType.SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
575
|
+
if (event.attributes?.case !== 'signalExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
if (event.attributes.value.signalName) {
|
|
579
|
+
count += 1;
|
|
580
|
+
}
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
case EventType.REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
584
|
+
if (event.attributes?.case !== 'requestCancelExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
count += 1;
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
case EventType.WORKFLOW_EXECUTION_CONTINUED_AS_NEW: {
|
|
591
|
+
if (event.attributes?.case !== 'workflowExecutionContinuedAsNewEventAttributes') {
|
|
592
|
+
break;
|
|
593
|
+
}
|
|
594
|
+
count += 1;
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
case EventType.MARKER_RECORDED: {
|
|
598
|
+
if (event.attributes?.case !== 'markerRecordedEventAttributes') {
|
|
599
|
+
break;
|
|
600
|
+
}
|
|
601
|
+
if (event.attributes.value.markerName !== DETERMINISM_MARKER_NAME) {
|
|
602
|
+
count += 1;
|
|
603
|
+
}
|
|
604
|
+
break;
|
|
605
|
+
}
|
|
606
|
+
case EventType.UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: {
|
|
607
|
+
if (event.attributes?.case !== 'upsertWorkflowSearchAttributesEventAttributes') {
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
count += 1;
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
case EventType.WORKFLOW_PROPERTIES_MODIFIED: {
|
|
614
|
+
if (event.attributes?.case !== 'workflowPropertiesModifiedEventAttributes') {
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
count += 1;
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
default:
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return count;
|
|
625
|
+
};
|
|
626
|
+
const commandKindsMatch = (expected, actual) => {
|
|
627
|
+
if (expected.length !== actual.length) {
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
for (let index = 0; index < expected.length; index += 1) {
|
|
631
|
+
if (expected[index] !== actual[index]) {
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
return true;
|
|
636
|
+
};
|
|
637
|
+
const collectCommandKinds = (events) => {
|
|
638
|
+
const { scheduledActivities, timersByStartEventId } = seedDeterminismEventMaps(undefined, events);
|
|
639
|
+
const kinds = [];
|
|
640
|
+
for (const event of events) {
|
|
641
|
+
switch (event.eventType) {
|
|
642
|
+
case EventType.ACTIVITY_TASK_SCHEDULED: {
|
|
643
|
+
if (event.attributes?.case !== 'activityTaskScheduledEventAttributes') {
|
|
644
|
+
break;
|
|
645
|
+
}
|
|
646
|
+
if (event.attributes.value.activityType?.name) {
|
|
647
|
+
kinds.push('schedule-activity');
|
|
648
|
+
}
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
case EventType.TIMER_STARTED: {
|
|
652
|
+
if (event.attributes?.case !== 'timerStartedEventAttributes') {
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
655
|
+
if (event.attributes.value.timerId) {
|
|
656
|
+
kinds.push('start-timer');
|
|
657
|
+
}
|
|
658
|
+
break;
|
|
659
|
+
}
|
|
660
|
+
case EventType.START_CHILD_WORKFLOW_EXECUTION_INITIATED: {
|
|
661
|
+
if (event.attributes?.case !== 'startChildWorkflowExecutionInitiatedEventAttributes') {
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
if (event.attributes.value.workflowType?.name) {
|
|
665
|
+
kinds.push('start-child-workflow');
|
|
666
|
+
}
|
|
667
|
+
break;
|
|
668
|
+
}
|
|
669
|
+
case EventType.ACTIVITY_TASK_CANCEL_REQUESTED: {
|
|
670
|
+
if (event.attributes?.case !== 'activityTaskCancelRequestedEventAttributes') {
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
const scheduledKey = normalizeEventId(event.attributes.value.scheduledEventId);
|
|
674
|
+
if (scheduledKey && scheduledActivities.has(scheduledKey)) {
|
|
675
|
+
kinds.push('request-cancel-activity');
|
|
676
|
+
}
|
|
677
|
+
break;
|
|
678
|
+
}
|
|
679
|
+
case EventType.TIMER_CANCELED: {
|
|
680
|
+
if (event.attributes?.case !== 'timerCanceledEventAttributes') {
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
const startedKey = normalizeEventId(event.attributes.value.startedEventId);
|
|
684
|
+
if (startedKey && timersByStartEventId.has(startedKey)) {
|
|
685
|
+
kinds.push('cancel-timer');
|
|
686
|
+
}
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
case EventType.SIGNAL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
690
|
+
if (event.attributes?.case !== 'signalExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
if (event.attributes.value.signalName) {
|
|
694
|
+
kinds.push('signal-external-workflow');
|
|
695
|
+
}
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
698
|
+
case EventType.REQUEST_CANCEL_EXTERNAL_WORKFLOW_EXECUTION_INITIATED: {
|
|
699
|
+
if (event.attributes?.case !== 'requestCancelExternalWorkflowExecutionInitiatedEventAttributes') {
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
if (event.attributes.value.workflowExecution?.workflowId) {
|
|
703
|
+
kinds.push('request-cancel-external-workflow');
|
|
704
|
+
}
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
case EventType.WORKFLOW_EXECUTION_CONTINUED_AS_NEW: {
|
|
708
|
+
if (event.attributes?.case !== 'workflowExecutionContinuedAsNewEventAttributes') {
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
711
|
+
kinds.push('continue-as-new');
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
case EventType.MARKER_RECORDED: {
|
|
715
|
+
if (event.attributes?.case !== 'markerRecordedEventAttributes') {
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
if (event.attributes.value.markerName !== DETERMINISM_MARKER_NAME) {
|
|
719
|
+
kinds.push('record-marker');
|
|
720
|
+
}
|
|
721
|
+
break;
|
|
722
|
+
}
|
|
723
|
+
case EventType.UPSERT_WORKFLOW_SEARCH_ATTRIBUTES: {
|
|
724
|
+
if (event.attributes?.case !== 'upsertWorkflowSearchAttributesEventAttributes') {
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
kinds.push('upsert-search-attributes');
|
|
728
|
+
break;
|
|
729
|
+
}
|
|
730
|
+
case EventType.WORKFLOW_PROPERTIES_MODIFIED: {
|
|
731
|
+
if (event.attributes?.case !== 'workflowPropertiesModifiedEventAttributes') {
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
kinds.push('modify-workflow-properties');
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
default:
|
|
738
|
+
break;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return kinds;
|
|
742
|
+
};
|
|
743
|
+
const reconstructDeterminismState = (events, intake, failureMetadata, precomputedUpdates, replayUpdateInvocations = [], options) => Effect.gen(function* () {
|
|
744
|
+
const seededState = options?.seedState ? cloneDeterminismState(options.seedState) : undefined;
|
|
745
|
+
let sequence = seededState?.commandHistory.length ?? 0;
|
|
746
|
+
const commandHistory = seededState ? [...seededState.commandHistory] : [];
|
|
747
|
+
const signalRecords = seededState ? [...seededState.signals] : [];
|
|
748
|
+
const queryRecords = seededState ? [...seededState.queries] : [];
|
|
749
|
+
const updateEntries = precomputedUpdates ?? collectWorkflowUpdateEntries(events);
|
|
750
|
+
const updates = seededState?.updates && seededState.updates.length > 0
|
|
751
|
+
? [...seededState.updates, ...updateEntries]
|
|
752
|
+
: updateEntries;
|
|
753
|
+
const { scheduledActivities, timersByStartEventId } = seedDeterminismEventMaps(seededState, options?.seedHistoryEvents);
|
|
395
754
|
for (const event of events) {
|
|
396
755
|
switch (event.eventType) {
|
|
397
756
|
case EventType.ACTIVITY_TASK_SCHEDULED: {
|
|
@@ -582,11 +941,12 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
|
|
|
582
941
|
break;
|
|
583
942
|
}
|
|
584
943
|
}
|
|
944
|
+
const resolvedFailureMetadata = failureMetadata ?? seededState?.failureMetadata;
|
|
585
945
|
let determinismState = {
|
|
586
946
|
commandHistory,
|
|
587
|
-
randomValues: [],
|
|
588
|
-
timeValues: [],
|
|
589
|
-
...(
|
|
947
|
+
randomValues: seededState?.randomValues ?? [],
|
|
948
|
+
timeValues: seededState?.timeValues ?? [],
|
|
949
|
+
...(resolvedFailureMetadata ? { failureMetadata: resolvedFailureMetadata } : {}),
|
|
590
950
|
signals: signalRecords,
|
|
591
951
|
queries: queryRecords,
|
|
592
952
|
...(updates.length > 0 ? { updates } : {}),
|
|
@@ -594,7 +954,7 @@ const reconstructDeterminismState = (events, intake, failureMetadata, precompute
|
|
|
594
954
|
determinismState = mergePendingQueryRequests(determinismState, intake.queries);
|
|
595
955
|
return {
|
|
596
956
|
determinismState,
|
|
597
|
-
lastEventId: resolveHistoryLastEventId(events),
|
|
957
|
+
lastEventId: options?.historyLastEventId ?? resolveHistoryLastEventId(events),
|
|
598
958
|
hasDeterminismMarker: false,
|
|
599
959
|
...(replayUpdateInvocations.length > 0 ? { updates: replayUpdateInvocations } : {}),
|
|
600
960
|
};
|
|
@@ -891,7 +1251,19 @@ const collectWorkflowUpdateEntries = (events) => {
|
|
|
891
1251
|
return updates;
|
|
892
1252
|
};
|
|
893
1253
|
const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
|
|
894
|
-
const
|
|
1254
|
+
const candidates = [];
|
|
1255
|
+
let order = 0;
|
|
1256
|
+
const parseSequencing = (value) => {
|
|
1257
|
+
if (!value) {
|
|
1258
|
+
return undefined;
|
|
1259
|
+
}
|
|
1260
|
+
try {
|
|
1261
|
+
return BigInt(value);
|
|
1262
|
+
}
|
|
1263
|
+
catch {
|
|
1264
|
+
return undefined;
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
895
1267
|
for (const event of events) {
|
|
896
1268
|
if (event.eventType === EventType.WORKFLOW_EXECUTION_UPDATE_ACCEPTED) {
|
|
897
1269
|
if (event.attributes.case !== 'workflowExecutionUpdateAcceptedEventAttributes') {
|
|
@@ -907,25 +1279,8 @@ const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
|
|
|
907
1279
|
dataConverter,
|
|
908
1280
|
});
|
|
909
1281
|
if (invocation) {
|
|
910
|
-
|
|
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);
|
|
1282
|
+
candidates.push({ invocation, sequencing: parseSequencing(invocation.sequencingEventId), order });
|
|
1283
|
+
order += 1;
|
|
929
1284
|
}
|
|
930
1285
|
continue;
|
|
931
1286
|
}
|
|
@@ -943,11 +1298,59 @@ const collectWorkflowUpdateInvocations = async (events, dataConverter) => {
|
|
|
943
1298
|
dataConverter,
|
|
944
1299
|
});
|
|
945
1300
|
if (invocation) {
|
|
946
|
-
|
|
1301
|
+
candidates.push({ invocation, sequencing: parseSequencing(invocation.sequencingEventId), order });
|
|
1302
|
+
order += 1;
|
|
947
1303
|
}
|
|
948
1304
|
}
|
|
949
1305
|
}
|
|
950
|
-
|
|
1306
|
+
const byUpdateId = new Map();
|
|
1307
|
+
for (const candidate of candidates) {
|
|
1308
|
+
const updateId = candidate.invocation.updateId;
|
|
1309
|
+
const existing = byUpdateId.get(updateId);
|
|
1310
|
+
if (!existing) {
|
|
1311
|
+
byUpdateId.set(updateId, candidate);
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1314
|
+
const existingSeq = existing.sequencing;
|
|
1315
|
+
const candidateSeq = candidate.sequencing;
|
|
1316
|
+
if (candidateSeq !== undefined && existingSeq !== undefined) {
|
|
1317
|
+
if (candidateSeq < existingSeq) {
|
|
1318
|
+
byUpdateId.set(updateId, candidate);
|
|
1319
|
+
}
|
|
1320
|
+
continue;
|
|
1321
|
+
}
|
|
1322
|
+
if (candidateSeq !== undefined && existingSeq === undefined) {
|
|
1323
|
+
byUpdateId.set(updateId, candidate);
|
|
1324
|
+
continue;
|
|
1325
|
+
}
|
|
1326
|
+
if (candidateSeq === undefined && existingSeq !== undefined) {
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
if (candidate.order < existing.order) {
|
|
1330
|
+
byUpdateId.set(updateId, candidate);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
return [...byUpdateId.values()]
|
|
1334
|
+
.sort((left, right) => {
|
|
1335
|
+
const leftSeq = left.sequencing;
|
|
1336
|
+
const rightSeq = right.sequencing;
|
|
1337
|
+
if (leftSeq !== undefined && rightSeq !== undefined) {
|
|
1338
|
+
if (leftSeq < rightSeq) {
|
|
1339
|
+
return -1;
|
|
1340
|
+
}
|
|
1341
|
+
if (leftSeq > rightSeq) {
|
|
1342
|
+
return 1;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
else if (leftSeq !== undefined) {
|
|
1346
|
+
return -1;
|
|
1347
|
+
}
|
|
1348
|
+
else if (rightSeq !== undefined) {
|
|
1349
|
+
return 1;
|
|
1350
|
+
}
|
|
1351
|
+
return left.order - right.order;
|
|
1352
|
+
})
|
|
1353
|
+
.map((entry) => entry.invocation);
|
|
951
1354
|
};
|
|
952
1355
|
const buildUpdateInvocationFromRequest = async (input) => {
|
|
953
1356
|
const req = input.request;
|
|
@@ -1089,19 +1492,63 @@ const resolveOutcomeStatus = (outcome) => {
|
|
|
1089
1492
|
}
|
|
1090
1493
|
return undefined;
|
|
1091
1494
|
};
|
|
1495
|
+
const applyDeterminismDelta = (state, delta) => ({
|
|
1496
|
+
...state,
|
|
1497
|
+
commandHistory: [...state.commandHistory, ...(delta.commandHistory ?? [])],
|
|
1498
|
+
randomValues: [...state.randomValues, ...(delta.randomValues ?? [])],
|
|
1499
|
+
timeValues: [...state.timeValues, ...(delta.timeValues ?? [])],
|
|
1500
|
+
signals: [...state.signals, ...(delta.signals ?? [])],
|
|
1501
|
+
queries: [...state.queries, ...(delta.queries ?? [])],
|
|
1502
|
+
...(delta.updates
|
|
1503
|
+
? { updates: [...(state.updates ?? []), ...(delta.updates ?? [])] }
|
|
1504
|
+
: state.updates
|
|
1505
|
+
? { updates: [...state.updates] }
|
|
1506
|
+
: {}),
|
|
1507
|
+
...(delta.logCount !== undefined ? { logCount: delta.logCount } : {}),
|
|
1508
|
+
...(delta.failureMetadata ? { failureMetadata: delta.failureMetadata } : {}),
|
|
1509
|
+
});
|
|
1510
|
+
const applyDeterminismMarker = (state, marker) => {
|
|
1511
|
+
if (marker.schemaVersion === 1) {
|
|
1512
|
+
return marker.determinismState;
|
|
1513
|
+
}
|
|
1514
|
+
const base = marker.determinismState ?? state;
|
|
1515
|
+
if (!base) {
|
|
1516
|
+
return undefined;
|
|
1517
|
+
}
|
|
1518
|
+
if (!marker.determinismDelta) {
|
|
1519
|
+
return base;
|
|
1520
|
+
}
|
|
1521
|
+
return applyDeterminismDelta(base, marker.determinismDelta);
|
|
1522
|
+
};
|
|
1092
1523
|
const sanitizeDeterminismMarkerEnvelope = (input) => {
|
|
1093
1524
|
const schemaVersion = input.schemaVersion;
|
|
1094
|
-
if (schemaVersion !==
|
|
1525
|
+
if (schemaVersion !== DETERMINISM_MARKER_SCHEMA_VERSION_V1 &&
|
|
1526
|
+
schemaVersion !== DETERMINISM_MARKER_SCHEMA_VERSION_V2) {
|
|
1095
1527
|
throw new Error(`Unsupported determinism marker schema version: ${String(schemaVersion)}`);
|
|
1096
1528
|
}
|
|
1097
1529
|
const workflow = sanitizeWorkflowInfo(input.workflow);
|
|
1098
|
-
const determinismState = sanitizeDeterminismState(input.determinismState);
|
|
1099
1530
|
const lastEventId = normalizeEventId(input.lastEventId);
|
|
1100
1531
|
const recordedAt = sanitizeRecordedAt(input.recordedAtIso);
|
|
1532
|
+
if (schemaVersion === DETERMINISM_MARKER_SCHEMA_VERSION_V1) {
|
|
1533
|
+
const determinismState = sanitizeDeterminismState(input.determinismState);
|
|
1534
|
+
return {
|
|
1535
|
+
schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V1,
|
|
1536
|
+
workflow,
|
|
1537
|
+
determinismState,
|
|
1538
|
+
lastEventId,
|
|
1539
|
+
recordedAtIso: recordedAt,
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
const determinismState = input.determinismState !== undefined ? sanitizeDeterminismState(input.determinismState) : undefined;
|
|
1543
|
+
const determinismDelta = input.determinismDelta !== undefined ? sanitizeDeterminismDelta(input.determinismDelta) : undefined;
|
|
1544
|
+
if (!determinismState && !determinismDelta) {
|
|
1545
|
+
throw new Error('Determinism marker missing determinism state or delta');
|
|
1546
|
+
}
|
|
1101
1547
|
return {
|
|
1102
|
-
schemaVersion:
|
|
1548
|
+
schemaVersion: DETERMINISM_MARKER_SCHEMA_VERSION_V2,
|
|
1103
1549
|
workflow,
|
|
1104
|
-
determinismState,
|
|
1550
|
+
...(determinismState ? { determinismState } : {}),
|
|
1551
|
+
...(determinismDelta ? { determinismDelta } : {}),
|
|
1105
1552
|
lastEventId,
|
|
1106
1553
|
recordedAtIso: recordedAt,
|
|
1107
1554
|
};
|
|
@@ -1125,6 +1572,7 @@ const sanitizeDeterminismState = (value) => {
|
|
|
1125
1572
|
const commandHistoryRaw = value.commandHistory;
|
|
1126
1573
|
const randomValuesRaw = value.randomValues;
|
|
1127
1574
|
const timeValuesRaw = value.timeValues;
|
|
1575
|
+
const logCountRaw = value.logCount;
|
|
1128
1576
|
const signalsRaw = Array.isArray(value.signals) ? value.signals : [];
|
|
1129
1577
|
const queriesRaw = Array.isArray(value.queries) ? value.queries : [];
|
|
1130
1578
|
const updatesRaw = value.updates;
|
|
@@ -1143,6 +1591,7 @@ const sanitizeDeterminismState = (value) => {
|
|
|
1143
1591
|
});
|
|
1144
1592
|
const randomValues = randomValuesRaw.map((val, index) => coerceNumber(val, `determinism.randomValues[${index}]`));
|
|
1145
1593
|
const timeValues = timeValuesRaw.map((val, index) => coerceNumber(val, `determinism.timeValues[${index}]`));
|
|
1594
|
+
const logCount = coerceOptionalNumber(logCountRaw, 'determinism.logCount');
|
|
1146
1595
|
const failureMetadata = sanitizeFailureMetadata(value.failureMetadata);
|
|
1147
1596
|
const signals = signalsRaw.map((record, index) => sanitizeSignalRecord(record, index));
|
|
1148
1597
|
const queries = queriesRaw.map((record, index) => sanitizeQueryRecord(record, index));
|
|
@@ -1151,12 +1600,40 @@ const sanitizeDeterminismState = (value) => {
|
|
|
1151
1600
|
commandHistory,
|
|
1152
1601
|
randomValues,
|
|
1153
1602
|
timeValues,
|
|
1603
|
+
...(logCount !== undefined ? { logCount } : {}),
|
|
1154
1604
|
...(failureMetadata ? { failureMetadata } : {}),
|
|
1155
1605
|
signals,
|
|
1156
1606
|
queries,
|
|
1157
1607
|
...(updates ? { updates } : {}),
|
|
1158
1608
|
};
|
|
1159
1609
|
};
|
|
1610
|
+
const sanitizeDeterminismDelta = (value) => {
|
|
1611
|
+
if (!isRecord(value)) {
|
|
1612
|
+
throw new Error('Determinism marker contained invalid determinism delta');
|
|
1613
|
+
}
|
|
1614
|
+
const commandHistoryRaw = value.commandHistory ?? [];
|
|
1615
|
+
const randomValuesRaw = value.randomValues ?? [];
|
|
1616
|
+
const timeValuesRaw = value.timeValues ?? [];
|
|
1617
|
+
const signalsRaw = value.signals ?? [];
|
|
1618
|
+
const queriesRaw = value.queries ?? [];
|
|
1619
|
+
if (!Array.isArray(commandHistoryRaw) ||
|
|
1620
|
+
!Array.isArray(randomValuesRaw) ||
|
|
1621
|
+
!Array.isArray(timeValuesRaw) ||
|
|
1622
|
+
!Array.isArray(signalsRaw) ||
|
|
1623
|
+
!Array.isArray(queriesRaw)) {
|
|
1624
|
+
throw new Error('Determinism marker contained invalid determinism delta shape');
|
|
1625
|
+
}
|
|
1626
|
+
return sanitizeDeterminismState({
|
|
1627
|
+
commandHistory: commandHistoryRaw,
|
|
1628
|
+
randomValues: randomValuesRaw,
|
|
1629
|
+
timeValues: timeValuesRaw,
|
|
1630
|
+
signals: signalsRaw,
|
|
1631
|
+
queries: queriesRaw,
|
|
1632
|
+
...(value.updates !== undefined ? { updates: value.updates } : {}),
|
|
1633
|
+
...(value.logCount !== undefined ? { logCount: value.logCount } : {}),
|
|
1634
|
+
...(value.failureMetadata !== undefined ? { failureMetadata: value.failureMetadata } : {}),
|
|
1635
|
+
});
|
|
1636
|
+
};
|
|
1160
1637
|
const sanitizeCommandMetadata = (value, index) => {
|
|
1161
1638
|
if (value === undefined || value === null) {
|
|
1162
1639
|
return undefined;
|