@rivetkit/workflow-engine 2.3.2 → 2.3.3-rc.2
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/dist/schemas/v1.ts +25 -0
- package/dist/tsup/{chunk-GQWOLYBA.cjs → chunk-OCUQRGRS.cjs} +93 -1
- package/dist/tsup/chunk-OCUQRGRS.cjs.map +1 -0
- package/dist/tsup/{chunk-KA2T56AJ.js → chunk-Z3WC5MXC.js} +93 -1
- package/dist/tsup/chunk-Z3WC5MXC.js.map +1 -0
- package/dist/tsup/index.cjs +2 -2
- package/dist/tsup/index.d.cts +24 -1
- package/dist/tsup/index.d.ts +24 -1
- package/dist/tsup/index.js +1 -1
- package/dist/tsup/testing.cjs +23 -23
- package/dist/tsup/testing.js +1 -1
- package/package.json +1 -1
- package/schemas/serde.ts +16 -0
- package/schemas/v1.bare +10 -1
- package/schemas/versioned.ts +1 -0
- package/src/context.ts +93 -0
- package/src/types.ts +17 -2
- package/dist/tsup/chunk-GQWOLYBA.cjs.map +0 -1
- package/dist/tsup/chunk-KA2T56AJ.js.map +0 -1
package/dist/tsup/index.cjs
CHANGED
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
|
|
46
|
-
var
|
|
46
|
+
var _chunkOCUQRGRScjs = require('./chunk-OCUQRGRS.cjs');
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
|
|
@@ -89,5 +89,5 @@ var _chunkGQWOLYBAcjs = require('./chunk-GQWOLYBA.cjs');
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
|
|
92
|
-
exports.CancelledError =
|
|
92
|
+
exports.CancelledError = _chunkOCUQRGRScjs.CancelledError; exports.CriticalError = _chunkOCUQRGRScjs.CriticalError; exports.DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL = _chunkOCUQRGRScjs.DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL; exports.DEFAULT_MAX_RETRIES = _chunkOCUQRGRScjs.DEFAULT_MAX_RETRIES; exports.DEFAULT_RETRY_BACKOFF_BASE = _chunkOCUQRGRScjs.DEFAULT_RETRY_BACKOFF_BASE; exports.DEFAULT_RETRY_BACKOFF_MAX = _chunkOCUQRGRScjs.DEFAULT_RETRY_BACKOFF_MAX; exports.DEFAULT_STEP_TIMEOUT = _chunkOCUQRGRScjs.DEFAULT_STEP_TIMEOUT; exports.EntryInProgressError = _chunkOCUQRGRScjs.EntryInProgressError; exports.EvictedError = _chunkOCUQRGRScjs.EvictedError; exports.HistoryDivergedError = _chunkOCUQRGRScjs.HistoryDivergedError; exports.JoinError = _chunkOCUQRGRScjs.JoinError; exports.Loop = _chunkOCUQRGRScjs.Loop; exports.MessageWaitError = _chunkOCUQRGRScjs.MessageWaitError; exports.RaceError = _chunkOCUQRGRScjs.RaceError; exports.RollbackCheckpointError = _chunkOCUQRGRScjs.RollbackCheckpointError; exports.RollbackError = _chunkOCUQRGRScjs.RollbackError; exports.SleepError = _chunkOCUQRGRScjs.SleepError; exports.StepExhaustedError = _chunkOCUQRGRScjs.StepExhaustedError; exports.StepFailedError = _chunkOCUQRGRScjs.StepFailedError; exports.WorkflowContextImpl = _chunkOCUQRGRScjs.WorkflowContextImpl; exports.appendLoopIteration = _chunkOCUQRGRScjs.appendLoopIteration; exports.appendName = _chunkOCUQRGRScjs.appendName; exports.createEntry = _chunkOCUQRGRScjs.createEntry; exports.createHistorySnapshot = _chunkOCUQRGRScjs.createHistorySnapshot; exports.createStorage = _chunkOCUQRGRScjs.createStorage; exports.deleteEntriesWithPrefix = _chunkOCUQRGRScjs.deleteEntriesWithPrefix; exports.emptyLocation = _chunkOCUQRGRScjs.emptyLocation; exports.extractErrorInfo = _chunkOCUQRGRScjs.extractErrorInfo; exports.flush = _chunkOCUQRGRScjs.flush; exports.generateId = _chunkOCUQRGRScjs.generateId; exports.getEntry = _chunkOCUQRGRScjs.getEntry; exports.getOrCreateMetadata = _chunkOCUQRGRScjs.getOrCreateMetadata; exports.isLocationPrefix = _chunkOCUQRGRScjs.isLocationPrefix; exports.isLoopIterationMarker = _chunkOCUQRGRScjs.isLoopIterationMarker; exports.loadMetadata = _chunkOCUQRGRScjs.loadMetadata; exports.loadStorage = _chunkOCUQRGRScjs.loadStorage; exports.locationToKey = _chunkOCUQRGRScjs.locationToKey; exports.locationsEqual = _chunkOCUQRGRScjs.locationsEqual; exports.parentLocation = _chunkOCUQRGRScjs.parentLocation; exports.registerName = _chunkOCUQRGRScjs.registerName; exports.replayWorkflowFromStep = _chunkOCUQRGRScjs.replayWorkflowFromStep; exports.resolveName = _chunkOCUQRGRScjs.resolveName; exports.runWorkflow = _chunkOCUQRGRScjs.runWorkflow; exports.setEntry = _chunkOCUQRGRScjs.setEntry;
|
|
93
93
|
//# sourceMappingURL=index.cjs.map
|
package/dist/tsup/index.d.cts
CHANGED
|
@@ -101,10 +101,20 @@ interface RemovedEntry {
|
|
|
101
101
|
originalType: EntryKindType;
|
|
102
102
|
originalName?: string;
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Version check entry data - records which version of a code path this
|
|
106
|
+
* workflow instance is pinned to at a given location.
|
|
107
|
+
*/
|
|
108
|
+
interface VersionCheckEntry {
|
|
109
|
+
/** The version this instance resolved to at this location. */
|
|
110
|
+
resolved: number;
|
|
111
|
+
/** The `latest` value seen when this entry was first resolved (diagnostics). */
|
|
112
|
+
latest: number;
|
|
113
|
+
}
|
|
104
114
|
/**
|
|
105
115
|
* All possible entry kind types.
|
|
106
116
|
*/
|
|
107
|
-
type EntryKindType = "step" | "loop" | "sleep" | "message" | "rollback_checkpoint" | "join" | "race" | "removed";
|
|
117
|
+
type EntryKindType = "step" | "loop" | "sleep" | "message" | "rollback_checkpoint" | "join" | "race" | "removed" | "version_check";
|
|
108
118
|
/**
|
|
109
119
|
* Type-specific entry data.
|
|
110
120
|
*/
|
|
@@ -132,6 +142,9 @@ type EntryKind = {
|
|
|
132
142
|
} | {
|
|
133
143
|
type: "removed";
|
|
134
144
|
data: RemovedEntry;
|
|
145
|
+
} | {
|
|
146
|
+
type: "version_check";
|
|
147
|
+
data: VersionCheckEntry;
|
|
135
148
|
};
|
|
136
149
|
/**
|
|
137
150
|
* An entry in the workflow history.
|
|
@@ -458,6 +471,7 @@ interface WorkflowContextInterface {
|
|
|
458
471
|
value: T;
|
|
459
472
|
}>;
|
|
460
473
|
removed(name: string, originalType: EntryKindType): Promise<void>;
|
|
474
|
+
getVersion(name: string, latest: number): Promise<number>;
|
|
461
475
|
isEvicted(): boolean;
|
|
462
476
|
}
|
|
463
477
|
/**
|
|
@@ -781,6 +795,15 @@ declare class WorkflowContextImpl implements WorkflowContextInterface {
|
|
|
781
795
|
private executeRace;
|
|
782
796
|
removed(name: string, originalType: EntryKindType): Promise<void>;
|
|
783
797
|
private executeRemoved;
|
|
798
|
+
getVersion(name: string, latest: number): Promise<number>;
|
|
799
|
+
private executeGetVersion;
|
|
800
|
+
/**
|
|
801
|
+
* Returns true if any history entry under the current location scope
|
|
802
|
+
* (other than `excludeKey`) has not yet been visited this run. Used by
|
|
803
|
+
* getVersion to detect an old in-flight instance whose scope was already
|
|
804
|
+
* executed by code that predates a version gate.
|
|
805
|
+
*/
|
|
806
|
+
private hasUnvisitedUnderCurrentScope;
|
|
784
807
|
}
|
|
785
808
|
|
|
786
809
|
/**
|
package/dist/tsup/index.d.ts
CHANGED
|
@@ -101,10 +101,20 @@ interface RemovedEntry {
|
|
|
101
101
|
originalType: EntryKindType;
|
|
102
102
|
originalName?: string;
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Version check entry data - records which version of a code path this
|
|
106
|
+
* workflow instance is pinned to at a given location.
|
|
107
|
+
*/
|
|
108
|
+
interface VersionCheckEntry {
|
|
109
|
+
/** The version this instance resolved to at this location. */
|
|
110
|
+
resolved: number;
|
|
111
|
+
/** The `latest` value seen when this entry was first resolved (diagnostics). */
|
|
112
|
+
latest: number;
|
|
113
|
+
}
|
|
104
114
|
/**
|
|
105
115
|
* All possible entry kind types.
|
|
106
116
|
*/
|
|
107
|
-
type EntryKindType = "step" | "loop" | "sleep" | "message" | "rollback_checkpoint" | "join" | "race" | "removed";
|
|
117
|
+
type EntryKindType = "step" | "loop" | "sleep" | "message" | "rollback_checkpoint" | "join" | "race" | "removed" | "version_check";
|
|
108
118
|
/**
|
|
109
119
|
* Type-specific entry data.
|
|
110
120
|
*/
|
|
@@ -132,6 +142,9 @@ type EntryKind = {
|
|
|
132
142
|
} | {
|
|
133
143
|
type: "removed";
|
|
134
144
|
data: RemovedEntry;
|
|
145
|
+
} | {
|
|
146
|
+
type: "version_check";
|
|
147
|
+
data: VersionCheckEntry;
|
|
135
148
|
};
|
|
136
149
|
/**
|
|
137
150
|
* An entry in the workflow history.
|
|
@@ -458,6 +471,7 @@ interface WorkflowContextInterface {
|
|
|
458
471
|
value: T;
|
|
459
472
|
}>;
|
|
460
473
|
removed(name: string, originalType: EntryKindType): Promise<void>;
|
|
474
|
+
getVersion(name: string, latest: number): Promise<number>;
|
|
461
475
|
isEvicted(): boolean;
|
|
462
476
|
}
|
|
463
477
|
/**
|
|
@@ -781,6 +795,15 @@ declare class WorkflowContextImpl implements WorkflowContextInterface {
|
|
|
781
795
|
private executeRace;
|
|
782
796
|
removed(name: string, originalType: EntryKindType): Promise<void>;
|
|
783
797
|
private executeRemoved;
|
|
798
|
+
getVersion(name: string, latest: number): Promise<number>;
|
|
799
|
+
private executeGetVersion;
|
|
800
|
+
/**
|
|
801
|
+
* Returns true if any history entry under the current location scope
|
|
802
|
+
* (other than `excludeKey`) has not yet been visited this run. Used by
|
|
803
|
+
* getVersion to detect an old in-flight instance whose scope was already
|
|
804
|
+
* executed by code that predates a version gate.
|
|
805
|
+
*/
|
|
806
|
+
private hasUnvisitedUnderCurrentScope;
|
|
784
807
|
}
|
|
785
808
|
|
|
786
809
|
/**
|
package/dist/tsup/index.js
CHANGED
package/dist/tsup/testing.cjs
CHANGED
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
var
|
|
50
|
+
var _chunkOCUQRGRScjs = require('./chunk-OCUQRGRS.cjs');
|
|
51
51
|
|
|
52
52
|
// src/testing.ts
|
|
53
53
|
var InMemoryWorkflowMessageDriver = class {
|
|
@@ -97,7 +97,7 @@ var InMemoryWorkflowMessageDriver = class {
|
|
|
97
97
|
}
|
|
98
98
|
async waitForMessages(messageNames, abortSignal) {
|
|
99
99
|
if (abortSignal.aborted) {
|
|
100
|
-
throw new (0,
|
|
100
|
+
throw new (0, _chunkOCUQRGRScjs.EvictedError)();
|
|
101
101
|
}
|
|
102
102
|
const nameSet = messageNames.length > 0 ? new Set(messageNames) : void 0;
|
|
103
103
|
if (this.#messages.some(
|
|
@@ -118,7 +118,7 @@ var InMemoryWorkflowMessageDriver = class {
|
|
|
118
118
|
},
|
|
119
119
|
abortSignal,
|
|
120
120
|
onAbort: () => {
|
|
121
|
-
waiter.reject(new (0,
|
|
121
|
+
waiter.reject(new (0, _chunkOCUQRGRScjs.EvictedError)());
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
abortSignal.addEventListener("abort", waiter.onAbort, {
|
|
@@ -158,56 +158,56 @@ var InMemoryDriver = (_class = class {constructor() { _class.prototype.__init.ca
|
|
|
158
158
|
__init4() {this.workerPollInterval = 100}
|
|
159
159
|
__init5() {this.messageDriver = this.#inMemoryMessageDriver}
|
|
160
160
|
async get(key) {
|
|
161
|
-
await
|
|
162
|
-
const entry = this.kv.get(
|
|
161
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, this.latency);
|
|
162
|
+
const entry = this.kv.get(_chunkOCUQRGRScjs.keyToHex.call(void 0, key));
|
|
163
163
|
return _nullishCoalesce((entry == null ? void 0 : entry.value), () => ( null));
|
|
164
164
|
}
|
|
165
165
|
async set(key, value) {
|
|
166
|
-
await
|
|
167
|
-
this.kv.set(
|
|
166
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, this.latency);
|
|
167
|
+
this.kv.set(_chunkOCUQRGRScjs.keyToHex.call(void 0, key), { key, value });
|
|
168
168
|
}
|
|
169
169
|
async delete(key) {
|
|
170
|
-
await
|
|
171
|
-
this.kv.delete(
|
|
170
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, this.latency);
|
|
171
|
+
this.kv.delete(_chunkOCUQRGRScjs.keyToHex.call(void 0, key));
|
|
172
172
|
}
|
|
173
173
|
async deletePrefix(prefix) {
|
|
174
|
-
await
|
|
174
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, this.latency);
|
|
175
175
|
for (const [hexKey, entry] of this.kv) {
|
|
176
|
-
if (
|
|
176
|
+
if (_chunkOCUQRGRScjs.keyStartsWith.call(void 0, entry.key, prefix)) {
|
|
177
177
|
this.kv.delete(hexKey);
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
async deleteRange(start, end) {
|
|
182
|
-
await
|
|
182
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, this.latency);
|
|
183
183
|
for (const [hexKey, entry] of this.kv) {
|
|
184
|
-
if (
|
|
184
|
+
if (_chunkOCUQRGRScjs.compareKeys.call(void 0, entry.key, start) >= 0 && _chunkOCUQRGRScjs.compareKeys.call(void 0, entry.key, end) < 0) {
|
|
185
185
|
this.kv.delete(hexKey);
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
async list(prefix) {
|
|
190
|
-
await
|
|
190
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, this.latency);
|
|
191
191
|
const results = [];
|
|
192
192
|
for (const entry of this.kv.values()) {
|
|
193
|
-
if (
|
|
193
|
+
if (_chunkOCUQRGRScjs.keyStartsWith.call(void 0, entry.key, prefix)) {
|
|
194
194
|
results.push({ key: entry.key, value: entry.value });
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
|
-
return results.sort((a, b) =>
|
|
197
|
+
return results.sort((a, b) => _chunkOCUQRGRScjs.compareKeys.call(void 0, a.key, b.key));
|
|
198
198
|
}
|
|
199
199
|
async batch(writes) {
|
|
200
|
-
await
|
|
200
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, this.latency);
|
|
201
201
|
for (const { key, value } of writes) {
|
|
202
|
-
this.kv.set(
|
|
202
|
+
this.kv.set(_chunkOCUQRGRScjs.keyToHex.call(void 0, key), { key, value });
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
async setAlarm(workflowId, wakeAt) {
|
|
206
|
-
await
|
|
206
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, this.latency);
|
|
207
207
|
this.alarms.set(workflowId, wakeAt);
|
|
208
208
|
}
|
|
209
209
|
async clearAlarm(workflowId) {
|
|
210
|
-
await
|
|
210
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, this.latency);
|
|
211
211
|
this.alarms.delete(workflowId);
|
|
212
212
|
}
|
|
213
213
|
async waitForMessages(messageNames, abortSignal) {
|
|
@@ -218,7 +218,7 @@ var InMemoryDriver = (_class = class {constructor() { _class.prototype.__init.ca
|
|
|
218
218
|
}
|
|
219
219
|
while (true) {
|
|
220
220
|
if (abortSignal.aborted) {
|
|
221
|
-
throw new (0,
|
|
221
|
+
throw new (0, _chunkOCUQRGRScjs.EvictedError)();
|
|
222
222
|
}
|
|
223
223
|
const messages = await this.messageDriver.receiveMessages({
|
|
224
224
|
names: messageNames.length > 0 ? messageNames : void 0,
|
|
@@ -228,7 +228,7 @@ var InMemoryDriver = (_class = class {constructor() { _class.prototype.__init.ca
|
|
|
228
228
|
if (messages.length > 0) {
|
|
229
229
|
return;
|
|
230
230
|
}
|
|
231
|
-
await
|
|
231
|
+
await _chunkOCUQRGRScjs.sleep.call(void 0, Math.max(1, this.latency));
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
/**
|
|
@@ -324,5 +324,5 @@ var InMemoryDriver = (_class = class {constructor() { _class.prototype.__init.ca
|
|
|
324
324
|
|
|
325
325
|
|
|
326
326
|
|
|
327
|
-
exports.CancelledError =
|
|
327
|
+
exports.CancelledError = _chunkOCUQRGRScjs.CancelledError; exports.CriticalError = _chunkOCUQRGRScjs.CriticalError; exports.DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL = _chunkOCUQRGRScjs.DEFAULT_LOOP_HISTORY_PRUNE_INTERVAL; exports.DEFAULT_MAX_RETRIES = _chunkOCUQRGRScjs.DEFAULT_MAX_RETRIES; exports.DEFAULT_RETRY_BACKOFF_BASE = _chunkOCUQRGRScjs.DEFAULT_RETRY_BACKOFF_BASE; exports.DEFAULT_RETRY_BACKOFF_MAX = _chunkOCUQRGRScjs.DEFAULT_RETRY_BACKOFF_MAX; exports.DEFAULT_STEP_TIMEOUT = _chunkOCUQRGRScjs.DEFAULT_STEP_TIMEOUT; exports.EntryInProgressError = _chunkOCUQRGRScjs.EntryInProgressError; exports.EvictedError = _chunkOCUQRGRScjs.EvictedError; exports.HistoryDivergedError = _chunkOCUQRGRScjs.HistoryDivergedError; exports.InMemoryDriver = InMemoryDriver; exports.JoinError = _chunkOCUQRGRScjs.JoinError; exports.Loop = _chunkOCUQRGRScjs.Loop; exports.MessageWaitError = _chunkOCUQRGRScjs.MessageWaitError; exports.RaceError = _chunkOCUQRGRScjs.RaceError; exports.RollbackCheckpointError = _chunkOCUQRGRScjs.RollbackCheckpointError; exports.RollbackError = _chunkOCUQRGRScjs.RollbackError; exports.SleepError = _chunkOCUQRGRScjs.SleepError; exports.StepExhaustedError = _chunkOCUQRGRScjs.StepExhaustedError; exports.StepFailedError = _chunkOCUQRGRScjs.StepFailedError; exports.WorkflowContextImpl = _chunkOCUQRGRScjs.WorkflowContextImpl; exports.appendLoopIteration = _chunkOCUQRGRScjs.appendLoopIteration; exports.appendName = _chunkOCUQRGRScjs.appendName; exports.createEntry = _chunkOCUQRGRScjs.createEntry; exports.createHistorySnapshot = _chunkOCUQRGRScjs.createHistorySnapshot; exports.createStorage = _chunkOCUQRGRScjs.createStorage; exports.deleteEntriesWithPrefix = _chunkOCUQRGRScjs.deleteEntriesWithPrefix; exports.emptyLocation = _chunkOCUQRGRScjs.emptyLocation; exports.extractErrorInfo = _chunkOCUQRGRScjs.extractErrorInfo; exports.flush = _chunkOCUQRGRScjs.flush; exports.generateId = _chunkOCUQRGRScjs.generateId; exports.getEntry = _chunkOCUQRGRScjs.getEntry; exports.getOrCreateMetadata = _chunkOCUQRGRScjs.getOrCreateMetadata; exports.isLocationPrefix = _chunkOCUQRGRScjs.isLocationPrefix; exports.isLoopIterationMarker = _chunkOCUQRGRScjs.isLoopIterationMarker; exports.loadMetadata = _chunkOCUQRGRScjs.loadMetadata; exports.loadStorage = _chunkOCUQRGRScjs.loadStorage; exports.locationToKey = _chunkOCUQRGRScjs.locationToKey; exports.locationsEqual = _chunkOCUQRGRScjs.locationsEqual; exports.parentLocation = _chunkOCUQRGRScjs.parentLocation; exports.registerName = _chunkOCUQRGRScjs.registerName; exports.replayWorkflowFromStep = _chunkOCUQRGRScjs.replayWorkflowFromStep; exports.resolveName = _chunkOCUQRGRScjs.resolveName; exports.runWorkflow = _chunkOCUQRGRScjs.runWorkflow; exports.setEntry = _chunkOCUQRGRScjs.setEntry;
|
|
328
328
|
//# sourceMappingURL=testing.cjs.map
|
package/dist/tsup/testing.js
CHANGED
package/package.json
CHANGED
package/schemas/serde.ts
CHANGED
|
@@ -307,6 +307,14 @@ function entryKindToBare(kind: InternalEntryKind): v1.EntryKind {
|
|
|
307
307
|
originalName: kind.data.originalName ?? null,
|
|
308
308
|
},
|
|
309
309
|
};
|
|
310
|
+
case "version_check":
|
|
311
|
+
return {
|
|
312
|
+
tag: "VersionCheckEntry",
|
|
313
|
+
val: {
|
|
314
|
+
resolved: kind.data.resolved,
|
|
315
|
+
latest: kind.data.latest,
|
|
316
|
+
},
|
|
317
|
+
};
|
|
310
318
|
}
|
|
311
319
|
}
|
|
312
320
|
|
|
@@ -396,6 +404,14 @@ function entryKindFromBare(kind: v1.EntryKind): InternalEntryKind {
|
|
|
396
404
|
originalName: kind.val.originalName ?? undefined,
|
|
397
405
|
},
|
|
398
406
|
};
|
|
407
|
+
case "VersionCheckEntry":
|
|
408
|
+
return {
|
|
409
|
+
type: "version_check",
|
|
410
|
+
data: {
|
|
411
|
+
resolved: kind.val.resolved,
|
|
412
|
+
latest: kind.val.latest,
|
|
413
|
+
},
|
|
414
|
+
};
|
|
399
415
|
default:
|
|
400
416
|
throw new Error(
|
|
401
417
|
`Unknown entry kind: ${(kind as { tag: string }).tag}`,
|
package/schemas/v1.bare
CHANGED
|
@@ -122,6 +122,14 @@ type RemovedEntry struct {
|
|
|
122
122
|
originalName: optional<str>
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
# MARK: Version Check Entry
|
|
126
|
+
type VersionCheckEntry struct {
|
|
127
|
+
# The version this instance resolved to at this location
|
|
128
|
+
resolved: u32
|
|
129
|
+
# The `latest` value seen when first resolved (diagnostics)
|
|
130
|
+
latest: u32
|
|
131
|
+
}
|
|
132
|
+
|
|
125
133
|
# MARK: Entry Kind
|
|
126
134
|
# Type-specific entry data
|
|
127
135
|
type EntryKind union {
|
|
@@ -132,7 +140,8 @@ type EntryKind union {
|
|
|
132
140
|
RollbackCheckpointEntry |
|
|
133
141
|
JoinEntry |
|
|
134
142
|
RaceEntry |
|
|
135
|
-
RemovedEntry
|
|
143
|
+
RemovedEntry |
|
|
144
|
+
VersionCheckEntry
|
|
136
145
|
}
|
|
137
146
|
|
|
138
147
|
# MARK: Entry
|
package/schemas/versioned.ts
CHANGED
package/src/context.ts
CHANGED
|
@@ -2581,4 +2581,97 @@ export class WorkflowContextImpl implements WorkflowContextInterface {
|
|
|
2581
2581
|
setEntry(this.storage, location, entry);
|
|
2582
2582
|
await this.flushStorage();
|
|
2583
2583
|
}
|
|
2584
|
+
|
|
2585
|
+
// === Version ===
|
|
2586
|
+
|
|
2587
|
+
async getVersion(name: string, latest: number): Promise<number> {
|
|
2588
|
+
this.assertNotInProgress();
|
|
2589
|
+
this.checkEvicted();
|
|
2590
|
+
|
|
2591
|
+
this.entryInProgress = true;
|
|
2592
|
+
try {
|
|
2593
|
+
return await this.executeGetVersion(name, latest);
|
|
2594
|
+
} finally {
|
|
2595
|
+
this.entryInProgress = false;
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
private async executeGetVersion(
|
|
2600
|
+
name: string,
|
|
2601
|
+
latest: number,
|
|
2602
|
+
): Promise<number> {
|
|
2603
|
+
if (!Number.isInteger(latest) || latest < 1) {
|
|
2604
|
+
throw new Error(
|
|
2605
|
+
`getVersion("${name}", ${latest}): latest must be an integer >= 1`,
|
|
2606
|
+
);
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2609
|
+
// Check for duplicate name in current execution
|
|
2610
|
+
this.checkDuplicateName(name);
|
|
2611
|
+
|
|
2612
|
+
const location = appendName(this.storage, this.currentLocation, name);
|
|
2613
|
+
const key = locationToKey(this.storage, location);
|
|
2614
|
+
const existing = this.storage.history.entries.get(key);
|
|
2615
|
+
|
|
2616
|
+
// Mark this entry as visited for validateComplete
|
|
2617
|
+
this.markVisited(key);
|
|
2618
|
+
|
|
2619
|
+
this.stopRollbackIfMissing(existing);
|
|
2620
|
+
|
|
2621
|
+
if (existing) {
|
|
2622
|
+
if (existing.kind.type !== "version_check") {
|
|
2623
|
+
throw new HistoryDivergedError(
|
|
2624
|
+
`Expected version_check at ${key}, found ${existing.kind.type}`,
|
|
2625
|
+
);
|
|
2626
|
+
}
|
|
2627
|
+
// Pure replay: this instance is already pinned at this location.
|
|
2628
|
+
return existing.kind.data.resolved;
|
|
2629
|
+
}
|
|
2630
|
+
|
|
2631
|
+
// No recorded version at this location. Decide whether this instance
|
|
2632
|
+
// already executed past this point under older code (old in-flight) or
|
|
2633
|
+
// is reaching it fresh at the live frontier.
|
|
2634
|
+
//
|
|
2635
|
+
// The discriminator is "is there any unvisited history entry under the
|
|
2636
|
+
// current scope?". Entries created earlier in this same run are already
|
|
2637
|
+
// marked visited, so the only unvisited entries under the scope are
|
|
2638
|
+
// leftovers from a prior run, which proves this scope already executed
|
|
2639
|
+
// under code that predates this gate. Such instances resolve to the
|
|
2640
|
+
// implicit floor version 1 (old branch); fresh instances resolve to
|
|
2641
|
+
// `latest`.
|
|
2642
|
+
const resolved = this.hasUnvisitedUnderCurrentScope(key) ? 1 : latest;
|
|
2643
|
+
|
|
2644
|
+
const entry = createEntry(location, {
|
|
2645
|
+
type: "version_check",
|
|
2646
|
+
data: { resolved, latest },
|
|
2647
|
+
});
|
|
2648
|
+
setEntry(this.storage, location, entry);
|
|
2649
|
+
await this.flushStorage();
|
|
2650
|
+
|
|
2651
|
+
return resolved;
|
|
2652
|
+
}
|
|
2653
|
+
|
|
2654
|
+
/**
|
|
2655
|
+
* Returns true if any history entry under the current location scope
|
|
2656
|
+
* (other than `excludeKey`) has not yet been visited this run. Used by
|
|
2657
|
+
* getVersion to detect an old in-flight instance whose scope was already
|
|
2658
|
+
* executed by code that predates a version gate.
|
|
2659
|
+
*/
|
|
2660
|
+
private hasUnvisitedUnderCurrentScope(excludeKey: string): boolean {
|
|
2661
|
+
const prefix = locationToKey(this.storage, this.currentLocation);
|
|
2662
|
+
|
|
2663
|
+
for (const key of this.storage.history.entries.keys()) {
|
|
2664
|
+
if (key === excludeKey) {
|
|
2665
|
+
continue;
|
|
2666
|
+
}
|
|
2667
|
+
const isUnderPrefix =
|
|
2668
|
+
prefix === ""
|
|
2669
|
+
? true
|
|
2670
|
+
: key.startsWith(`${prefix}/`) || key === prefix;
|
|
2671
|
+
if (isUnderPrefix && !this.visitedKeys.has(key)) {
|
|
2672
|
+
return true;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
return false;
|
|
2676
|
+
}
|
|
2584
2677
|
}
|
package/src/types.ts
CHANGED
|
@@ -135,6 +135,17 @@ export interface RemovedEntry {
|
|
|
135
135
|
originalName?: string;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Version check entry data - records which version of a code path this
|
|
140
|
+
* workflow instance is pinned to at a given location.
|
|
141
|
+
*/
|
|
142
|
+
export interface VersionCheckEntry {
|
|
143
|
+
/** The version this instance resolved to at this location. */
|
|
144
|
+
resolved: number;
|
|
145
|
+
/** The `latest` value seen when this entry was first resolved (diagnostics). */
|
|
146
|
+
latest: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
138
149
|
/**
|
|
139
150
|
* All possible entry kind types.
|
|
140
151
|
*/
|
|
@@ -146,7 +157,8 @@ export type EntryKindType =
|
|
|
146
157
|
| "rollback_checkpoint"
|
|
147
158
|
| "join"
|
|
148
159
|
| "race"
|
|
149
|
-
| "removed"
|
|
160
|
+
| "removed"
|
|
161
|
+
| "version_check";
|
|
150
162
|
|
|
151
163
|
/**
|
|
152
164
|
* Type-specific entry data.
|
|
@@ -159,7 +171,8 @@ export type EntryKind =
|
|
|
159
171
|
| { type: "rollback_checkpoint"; data: RollbackCheckpointEntry }
|
|
160
172
|
| { type: "join"; data: JoinEntry }
|
|
161
173
|
| { type: "race"; data: RaceEntry }
|
|
162
|
-
| { type: "removed"; data: RemovedEntry }
|
|
174
|
+
| { type: "removed"; data: RemovedEntry }
|
|
175
|
+
| { type: "version_check"; data: VersionCheckEntry };
|
|
163
176
|
|
|
164
177
|
/**
|
|
165
178
|
* An entry in the workflow history.
|
|
@@ -539,6 +552,8 @@ export interface WorkflowContextInterface {
|
|
|
539
552
|
|
|
540
553
|
removed(name: string, originalType: EntryKindType): Promise<void>;
|
|
541
554
|
|
|
555
|
+
getVersion(name: string, latest: number): Promise<number>;
|
|
556
|
+
|
|
542
557
|
isEvicted(): boolean;
|
|
543
558
|
}
|
|
544
559
|
|