@workflow/world-local 4.1.0-beta.42 → 4.1.0-beta.44
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/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/fs.d.ts +34 -0
- package/dist/fs.d.ts.map +1 -1
- package/dist/fs.js +88 -0
- package/dist/fs.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -5
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +6 -1
- package/dist/init.js.map +1 -1
- package/dist/queue.d.ts +1 -28
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +55 -89
- package/dist/queue.js.map +1 -1
- package/dist/storage/events-storage.d.ts +1 -1
- package/dist/storage/events-storage.d.ts.map +1 -1
- package/dist/storage/events-storage.js +51 -73
- package/dist/storage/events-storage.js.map +1 -1
- package/dist/storage/filters.d.ts +2 -6
- package/dist/storage/filters.d.ts.map +1 -1
- package/dist/storage/filters.js +1 -11
- package/dist/storage/filters.js.map +1 -1
- package/dist/storage/helpers.d.ts +4 -0
- package/dist/storage/helpers.d.ts.map +1 -1
- package/dist/storage/helpers.js +14 -4
- package/dist/storage/helpers.js.map +1 -1
- package/dist/storage/hooks-storage.d.ts +1 -1
- package/dist/storage/hooks-storage.d.ts.map +1 -1
- package/dist/storage/hooks-storage.js +7 -4
- package/dist/storage/hooks-storage.js.map +1 -1
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +5 -5
- package/dist/storage/index.js.map +1 -1
- package/dist/storage/legacy.js +2 -2
- package/dist/storage/legacy.js.map +1 -1
- package/dist/storage/runs-storage.d.ts +1 -1
- package/dist/storage/runs-storage.d.ts.map +1 -1
- package/dist/storage/runs-storage.js +3 -4
- package/dist/storage/runs-storage.js.map +1 -1
- package/dist/storage/steps-storage.d.ts +1 -1
- package/dist/storage/steps-storage.d.ts.map +1 -1
- package/dist/storage/steps-storage.js +5 -6
- package/dist/storage/steps-storage.js.map +1 -1
- package/dist/streamer.d.ts +1 -1
- package/dist/streamer.d.ts.map +1 -1
- package/dist/streamer.js +37 -15
- package/dist/streamer.js.map +1 -1
- package/package.json +3 -3
package/dist/queue.js
CHANGED
|
@@ -30,7 +30,7 @@ function getQueueRoute(queueName) {
|
|
|
30
30
|
}
|
|
31
31
|
throw new Error('Unknown queue name prefix');
|
|
32
32
|
}
|
|
33
|
-
export function
|
|
33
|
+
export function createQueue(config) {
|
|
34
34
|
// Create a custom agent optimized for high-concurrency local workflows:
|
|
35
35
|
// - headersTimeout: 0 allows long-running steps
|
|
36
36
|
// - connections: 1000 allows many parallel connections to the same host
|
|
@@ -41,70 +41,6 @@ export function createQueueExecutor(config) {
|
|
|
41
41
|
connections: 1000,
|
|
42
42
|
keepAliveTimeout: 30_000,
|
|
43
43
|
});
|
|
44
|
-
/** Direct in-process handlers by queue prefix, bypassing HTTP when set. */
|
|
45
|
-
const directHandlers = new Map();
|
|
46
|
-
const executeMessage = async ({ queueName, messageId, attempt, body, headers: extraHeaders, }) => {
|
|
47
|
-
const { pathname, prefix } = getQueueRoute(queueName);
|
|
48
|
-
const headers = {
|
|
49
|
-
...extraHeaders,
|
|
50
|
-
'content-type': 'application/json',
|
|
51
|
-
'x-vqs-queue-name': queueName,
|
|
52
|
-
'x-vqs-message-id': messageId,
|
|
53
|
-
'x-vqs-message-attempt': String(attempt),
|
|
54
|
-
};
|
|
55
|
-
const directHandler = directHandlers.get(prefix);
|
|
56
|
-
let response;
|
|
57
|
-
if (directHandler) {
|
|
58
|
-
// Wrap direct handlers in a Request so local execution and HTTP execution
|
|
59
|
-
// share the same contract and response parsing.
|
|
60
|
-
const req = new Request('http://localhost/.well-known/workflow/v1/' + pathname, {
|
|
61
|
-
method: 'POST',
|
|
62
|
-
headers,
|
|
63
|
-
body,
|
|
64
|
-
});
|
|
65
|
-
response = await directHandler(req);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
const baseUrl = await resolveBaseUrl(config);
|
|
69
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- undici v7 dispatcher types don't match @types/node's RequestInit
|
|
70
|
-
response = await fetch(`${baseUrl}/.well-known/workflow/v1/${pathname}`, {
|
|
71
|
-
method: 'POST',
|
|
72
|
-
duplex: 'half',
|
|
73
|
-
dispatcher: httpAgent,
|
|
74
|
-
headers,
|
|
75
|
-
body,
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
const text = await response.text();
|
|
79
|
-
if (!response.ok) {
|
|
80
|
-
return {
|
|
81
|
-
type: 'error',
|
|
82
|
-
status: response.status,
|
|
83
|
-
text,
|
|
84
|
-
headers: Object.fromEntries(response.headers.entries()),
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
const timeoutSeconds = Number(JSON.parse(text).timeoutSeconds);
|
|
89
|
-
if (Number.isFinite(timeoutSeconds) && timeoutSeconds >= 0) {
|
|
90
|
-
return { type: 'reschedule', timeoutSeconds };
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
catch { }
|
|
94
|
-
return { type: 'completed' };
|
|
95
|
-
};
|
|
96
|
-
return {
|
|
97
|
-
executeMessage,
|
|
98
|
-
registerHandler(prefix, handler) {
|
|
99
|
-
directHandlers.set(prefix, handler);
|
|
100
|
-
},
|
|
101
|
-
async close() {
|
|
102
|
-
await httpAgent.close();
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
export function createQueue(config) {
|
|
107
|
-
const executor = createQueueExecutor(config);
|
|
108
44
|
const transport = new JsonTransport();
|
|
109
45
|
const generateId = monotonicFactory();
|
|
110
46
|
const semaphore = new Sema(WORKFLOW_LOCAL_QUEUE_CONCURRENCY);
|
|
@@ -113,6 +49,8 @@ export function createQueue(config) {
|
|
|
113
49
|
* that we don't queue the same message multiple times
|
|
114
50
|
*/
|
|
115
51
|
const inflightMessages = new Map();
|
|
52
|
+
/** Direct in-process handlers by queue prefix, bypassing HTTP when set. */
|
|
53
|
+
const directHandlers = new Map();
|
|
116
54
|
const queue = async (queueName, message, opts) => {
|
|
117
55
|
const cleanup = [];
|
|
118
56
|
if (opts?.idempotencyKey) {
|
|
@@ -122,7 +60,7 @@ export function createQueue(config) {
|
|
|
122
60
|
}
|
|
123
61
|
}
|
|
124
62
|
const body = transport.serialize(message);
|
|
125
|
-
getQueueRoute(queueName);
|
|
63
|
+
const { pathname, prefix } = getQueueRoute(queueName);
|
|
126
64
|
const messageId = MessageId.parse(`msg_${generateId()}`);
|
|
127
65
|
if (opts?.idempotencyKey) {
|
|
128
66
|
const key = opts.idempotencyKey;
|
|
@@ -141,32 +79,58 @@ export function createQueue(config) {
|
|
|
141
79
|
let defaultRetriesLeft = 3;
|
|
142
80
|
for (let attempt = 0; defaultRetriesLeft > 0; attempt++) {
|
|
143
81
|
defaultRetriesLeft--;
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
82
|
+
const headers = {
|
|
83
|
+
...opts?.headers,
|
|
84
|
+
'content-type': 'application/json',
|
|
85
|
+
'x-vqs-queue-name': queueName,
|
|
86
|
+
'x-vqs-message-id': messageId,
|
|
87
|
+
'x-vqs-message-attempt': String(attempt + 1),
|
|
88
|
+
};
|
|
89
|
+
const directHandler = directHandlers.get(prefix);
|
|
90
|
+
let response;
|
|
91
|
+
if (directHandler) {
|
|
92
|
+
const req = new Request(`http://localhost/.well-known/workflow/v1/${pathname}`, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers,
|
|
95
|
+
body,
|
|
96
|
+
});
|
|
97
|
+
response = await directHandler(req);
|
|
153
98
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
99
|
+
else {
|
|
100
|
+
const baseUrl = await resolveBaseUrl(config);
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- undici v7 dispatcher types don't match @types/node's RequestInit
|
|
102
|
+
response = await fetch(`${baseUrl}/.well-known/workflow/v1/${pathname}`, {
|
|
103
|
+
method: 'POST',
|
|
104
|
+
duplex: 'half',
|
|
105
|
+
dispatcher: httpAgent,
|
|
106
|
+
headers,
|
|
107
|
+
body,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const text = await response.text();
|
|
111
|
+
if (response.ok) {
|
|
112
|
+
try {
|
|
113
|
+
const timeoutSeconds = Number(JSON.parse(text).timeoutSeconds);
|
|
114
|
+
if (Number.isFinite(timeoutSeconds) && timeoutSeconds >= 0) {
|
|
115
|
+
// Clamp to MAX_SAFE_TIMEOUT_MS to avoid Node.js setTimeout overflow warning.
|
|
116
|
+
// When this fires early, the handler recalculates remaining time from
|
|
117
|
+
// persistent state and returns another timeoutSeconds if needed.
|
|
118
|
+
if (timeoutSeconds > 0) {
|
|
119
|
+
const timeoutMs = Math.min(timeoutSeconds * 1000, MAX_SAFE_TIMEOUT_MS);
|
|
120
|
+
await setTimeout(timeoutMs);
|
|
121
|
+
}
|
|
122
|
+
defaultRetriesLeft++;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
161
125
|
}
|
|
162
|
-
|
|
163
|
-
|
|
126
|
+
catch { }
|
|
127
|
+
return;
|
|
164
128
|
}
|
|
165
129
|
console.error(`[local world] Failed to queue message`, {
|
|
166
130
|
queueName,
|
|
167
|
-
text
|
|
168
|
-
status:
|
|
169
|
-
headers:
|
|
131
|
+
text,
|
|
132
|
+
status: response.status,
|
|
133
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
170
134
|
body: body.toString(),
|
|
171
135
|
});
|
|
172
136
|
}
|
|
@@ -237,9 +201,11 @@ export function createQueue(config) {
|
|
|
237
201
|
queue,
|
|
238
202
|
createQueueHandler,
|
|
239
203
|
getDeploymentId,
|
|
240
|
-
registerHandler
|
|
204
|
+
registerHandler(prefix, handler) {
|
|
205
|
+
directHandlers.set(prefix, handler);
|
|
206
|
+
},
|
|
241
207
|
async close() {
|
|
242
|
-
await
|
|
208
|
+
await httpAgent.close();
|
|
243
209
|
},
|
|
244
210
|
};
|
|
245
211
|
}
|
package/dist/queue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAc,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAE3C,mFAAmF;AACnF,+FAA+F;AAC/F,MAAM,0BAA0B,GAC9B,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAI,GAAG,EAAE,EAAE,CAAC;IACpE,QAAQ,CAAC;AAEX,oFAAoF;AACpF,4FAA4F;AAC5F,mFAAmF;AACnF,gEAAgE;AAChE,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAEvC,mEAAmE;AACnE,oEAAoE;AACpE,MAAM,yBAAyB,GAAG,IAAI,CAAC;AACvC,MAAM,gCAAgC,GACpC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gCAAgC,IAAI,GAAG,EAAE,EAAE,CAAC;IACjE,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"queue.js","sourceRoot":"","sources":["../src/queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,5 +3,5 @@ import type { Storage } from '@workflow/world';
|
|
|
3
3
|
* Creates the events storage implementation using the filesystem.
|
|
4
4
|
* Implements the Storage['events'] interface with create, list, and listByCorrelationId operations.
|
|
5
5
|
*/
|
|
6
|
-
export declare function createEventsStorage(basedir: string): Storage['events'];
|
|
6
|
+
export declare function createEventsStorage(basedir: string, tag?: string): Storage['events'];
|
|
7
7
|
//# sourceMappingURL=events-storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events-storage.d.ts","sourceRoot":"","sources":["../../src/storage/events-storage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAMV,OAAO,EAGR,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"events-storage.d.ts","sourceRoot":"","sources":["../../src/storage/events-storage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAMV,OAAO,EAGR,MAAM,iBAAiB,CAAC;AAgDzB;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,QAAQ,CAAC,CA6wBnB"}
|
|
@@ -2,9 +2,9 @@ import path from 'node:path';
|
|
|
2
2
|
import { RunNotSupportedError, WorkflowAPIError } from '@workflow/errors';
|
|
3
3
|
import { EventSchema, HookSchema, isLegacySpecVersion, requiresNewerWorld, SPEC_VERSION_CURRENT, StepSchema, validateUlidTimestamp, WaitSchema, WorkflowRunSchema, } from '@workflow/world';
|
|
4
4
|
import { DEFAULT_RESOLVE_DATA_OPTION } from '../config.js';
|
|
5
|
-
import { deleteJSON, listJSONFiles, paginatedFileSystemQuery,
|
|
6
|
-
import {
|
|
7
|
-
import { getObjectCreatedAt, monotonicUlid } from './helpers.js';
|
|
5
|
+
import { deleteJSON, listJSONFiles, paginatedFileSystemQuery, readJSONWithFallback, taggedPath, writeExclusive, writeJSON, } from '../fs.js';
|
|
6
|
+
import { stripEventDataRefs } from './filters.js';
|
|
7
|
+
import { getObjectCreatedAt, hashToken, monotonicUlid } from './helpers.js';
|
|
8
8
|
import { deleteAllHooksForRun } from './hooks-storage.js';
|
|
9
9
|
import { handleLegacyEvent } from './legacy.js';
|
|
10
10
|
/**
|
|
@@ -15,6 +15,8 @@ async function deleteAllWaitsForRun(basedir, runId) {
|
|
|
15
15
|
const waitsDir = path.join(basedir, 'waits');
|
|
16
16
|
const files = await listJSONFiles(waitsDir);
|
|
17
17
|
for (const file of files) {
|
|
18
|
+
// fileIds may contain tag suffixes (e.g., "wrun_ABC-corrId.vitest-0")
|
|
19
|
+
// but startsWith still matches correctly since the tag is a suffix.
|
|
18
20
|
if (file.startsWith(`${runId}-`)) {
|
|
19
21
|
const waitPath = path.join(waitsDir, `${file}.json`);
|
|
20
22
|
await deleteJSON(waitPath);
|
|
@@ -25,7 +27,7 @@ async function deleteAllWaitsForRun(basedir, runId) {
|
|
|
25
27
|
* Creates the events storage implementation using the filesystem.
|
|
26
28
|
* Implements the Storage['events'] interface with create, list, and listByCorrelationId operations.
|
|
27
29
|
*/
|
|
28
|
-
export function createEventsStorage(basedir) {
|
|
30
|
+
export function createEventsStorage(basedir, tag) {
|
|
29
31
|
return {
|
|
30
32
|
async create(runId, data, params) {
|
|
31
33
|
const eventId = `evnt_${monotonicUlid()}`;
|
|
@@ -62,8 +64,7 @@ export function createEventsStorage(basedir) {
|
|
|
62
64
|
const skipRunValidationEvents = ['step_completed', 'step_retrying'];
|
|
63
65
|
if (data.eventType !== 'run_created' &&
|
|
64
66
|
!skipRunValidationEvents.includes(data.eventType)) {
|
|
65
|
-
|
|
66
|
-
currentRun = await readJSON(runPath, WorkflowRunSchema);
|
|
67
|
+
currentRun = await readJSONWithFallback(basedir, 'runs', effectiveRunId, WorkflowRunSchema, tag);
|
|
67
68
|
}
|
|
68
69
|
// ============================================================
|
|
69
70
|
// VERSION COMPATIBILITY: Check run spec version
|
|
@@ -102,11 +103,10 @@ export function createEventsStorage(basedir) {
|
|
|
102
103
|
specVersion: effectiveSpecVersion,
|
|
103
104
|
};
|
|
104
105
|
const compositeKey = `${effectiveRunId}-${eventId}`;
|
|
105
|
-
|
|
106
|
-
await writeJSON(eventPath, event);
|
|
106
|
+
await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), event);
|
|
107
107
|
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
108
108
|
return {
|
|
109
|
-
event:
|
|
109
|
+
event: stripEventDataRefs(event, resolveData),
|
|
110
110
|
run: currentRun,
|
|
111
111
|
};
|
|
112
112
|
}
|
|
@@ -133,8 +133,7 @@ export function createEventsStorage(basedir) {
|
|
|
133
133
|
];
|
|
134
134
|
if (stepEvents.includes(data.eventType) && data.correlationId) {
|
|
135
135
|
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
136
|
-
|
|
137
|
-
validatedStep = await readJSON(stepPath, StepSchema);
|
|
136
|
+
validatedStep = await readJSONWithFallback(basedir, 'steps', stepCompositeKey, StepSchema, tag);
|
|
138
137
|
// Event ordering: step must exist before these events
|
|
139
138
|
if (!validatedStep) {
|
|
140
139
|
throw new WorkflowAPIError(`Step "${data.correlationId}" not found`, {
|
|
@@ -156,8 +155,7 @@ export function createEventsStorage(basedir) {
|
|
|
156
155
|
const hookEventsRequiringExistence = ['hook_disposed', 'hook_received'];
|
|
157
156
|
if (hookEventsRequiringExistence.includes(data.eventType) &&
|
|
158
157
|
data.correlationId) {
|
|
159
|
-
const
|
|
160
|
-
const existingHook = await readJSON(hookPath, HookSchema);
|
|
158
|
+
const existingHook = await readJSONWithFallback(basedir, 'hooks', data.correlationId, HookSchema, tag);
|
|
161
159
|
if (!existingHook) {
|
|
162
160
|
throw new WorkflowAPIError(`Hook "${data.correlationId}" not found`, {
|
|
163
161
|
status: 404,
|
|
@@ -196,13 +194,11 @@ export function createEventsStorage(basedir) {
|
|
|
196
194
|
createdAt: now,
|
|
197
195
|
updatedAt: now,
|
|
198
196
|
};
|
|
199
|
-
|
|
200
|
-
await writeJSON(runPath, run);
|
|
197
|
+
await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run);
|
|
201
198
|
}
|
|
202
199
|
else if (data.eventType === 'run_started') {
|
|
203
200
|
// Reuse currentRun from validation (already read above)
|
|
204
201
|
if (currentRun) {
|
|
205
|
-
const runPath = path.join(basedir, 'runs', `${effectiveRunId}.json`);
|
|
206
202
|
run = {
|
|
207
203
|
runId: currentRun.runId,
|
|
208
204
|
deploymentId: currentRun.deploymentId,
|
|
@@ -219,14 +215,13 @@ export function createEventsStorage(basedir) {
|
|
|
219
215
|
startedAt: currentRun.startedAt ?? now,
|
|
220
216
|
updatedAt: now,
|
|
221
217
|
};
|
|
222
|
-
await writeJSON(
|
|
218
|
+
await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
|
|
223
219
|
}
|
|
224
220
|
}
|
|
225
221
|
else if (data.eventType === 'run_completed' && 'eventData' in data) {
|
|
226
222
|
const completedData = data.eventData;
|
|
227
223
|
// Reuse currentRun from validation (already read above)
|
|
228
224
|
if (currentRun) {
|
|
229
|
-
const runPath = path.join(basedir, 'runs', `${effectiveRunId}.json`);
|
|
230
225
|
run = {
|
|
231
226
|
runId: currentRun.runId,
|
|
232
227
|
deploymentId: currentRun.deploymentId,
|
|
@@ -243,7 +238,7 @@ export function createEventsStorage(basedir) {
|
|
|
243
238
|
completedAt: now,
|
|
244
239
|
updatedAt: now,
|
|
245
240
|
};
|
|
246
|
-
await writeJSON(
|
|
241
|
+
await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
|
|
247
242
|
await Promise.all([
|
|
248
243
|
deleteAllHooksForRun(basedir, effectiveRunId),
|
|
249
244
|
deleteAllWaitsForRun(basedir, effectiveRunId),
|
|
@@ -254,7 +249,6 @@ export function createEventsStorage(basedir) {
|
|
|
254
249
|
const failedData = data.eventData;
|
|
255
250
|
// Reuse currentRun from validation (already read above)
|
|
256
251
|
if (currentRun) {
|
|
257
|
-
const runPath = path.join(basedir, 'runs', `${effectiveRunId}.json`);
|
|
258
252
|
run = {
|
|
259
253
|
runId: currentRun.runId,
|
|
260
254
|
deploymentId: currentRun.deploymentId,
|
|
@@ -277,7 +271,7 @@ export function createEventsStorage(basedir) {
|
|
|
277
271
|
completedAt: now,
|
|
278
272
|
updatedAt: now,
|
|
279
273
|
};
|
|
280
|
-
await writeJSON(
|
|
274
|
+
await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
|
|
281
275
|
await Promise.all([
|
|
282
276
|
deleteAllHooksForRun(basedir, effectiveRunId),
|
|
283
277
|
deleteAllWaitsForRun(basedir, effectiveRunId),
|
|
@@ -287,7 +281,6 @@ export function createEventsStorage(basedir) {
|
|
|
287
281
|
else if (data.eventType === 'run_cancelled') {
|
|
288
282
|
// Reuse currentRun from validation (already read above)
|
|
289
283
|
if (currentRun) {
|
|
290
|
-
const runPath = path.join(basedir, 'runs', `${effectiveRunId}.json`);
|
|
291
284
|
run = {
|
|
292
285
|
runId: currentRun.runId,
|
|
293
286
|
deploymentId: currentRun.deploymentId,
|
|
@@ -304,7 +297,7 @@ export function createEventsStorage(basedir) {
|
|
|
304
297
|
completedAt: now,
|
|
305
298
|
updatedAt: now,
|
|
306
299
|
};
|
|
307
|
-
await writeJSON(
|
|
300
|
+
await writeJSON(taggedPath(basedir, 'runs', effectiveRunId, tag), run, { overwrite: true });
|
|
308
301
|
await Promise.all([
|
|
309
302
|
deleteAllHooksForRun(basedir, effectiveRunId),
|
|
310
303
|
deleteAllWaitsForRun(basedir, effectiveRunId),
|
|
@@ -334,8 +327,7 @@ export function createEventsStorage(basedir) {
|
|
|
334
327
|
specVersion: effectiveSpecVersion,
|
|
335
328
|
};
|
|
336
329
|
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
337
|
-
|
|
338
|
-
await writeJSON(stepPath, step);
|
|
330
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step);
|
|
339
331
|
}
|
|
340
332
|
else if (data.eventType === 'step_started') {
|
|
341
333
|
// step_started: Increments attempt, sets status to 'running'
|
|
@@ -354,7 +346,6 @@ export function createEventsStorage(basedir) {
|
|
|
354
346
|
throw err;
|
|
355
347
|
}
|
|
356
348
|
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
357
|
-
const stepPath = path.join(basedir, 'steps', `${stepCompositeKey}.json`);
|
|
358
349
|
step = {
|
|
359
350
|
...validatedStep,
|
|
360
351
|
status: 'running',
|
|
@@ -366,7 +357,7 @@ export function createEventsStorage(basedir) {
|
|
|
366
357
|
retryAfter: undefined,
|
|
367
358
|
updatedAt: now,
|
|
368
359
|
};
|
|
369
|
-
await writeJSON(
|
|
360
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
|
|
370
361
|
}
|
|
371
362
|
}
|
|
372
363
|
else if (data.eventType === 'step_completed' && 'eventData' in data) {
|
|
@@ -375,7 +366,6 @@ export function createEventsStorage(basedir) {
|
|
|
375
366
|
const completedData = data.eventData;
|
|
376
367
|
if (validatedStep) {
|
|
377
368
|
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
378
|
-
const stepPath = path.join(basedir, 'steps', `${stepCompositeKey}.json`);
|
|
379
369
|
step = {
|
|
380
370
|
...validatedStep,
|
|
381
371
|
status: 'completed',
|
|
@@ -383,7 +373,7 @@ export function createEventsStorage(basedir) {
|
|
|
383
373
|
completedAt: now,
|
|
384
374
|
updatedAt: now,
|
|
385
375
|
};
|
|
386
|
-
await writeJSON(
|
|
376
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
|
|
387
377
|
}
|
|
388
378
|
}
|
|
389
379
|
else if (data.eventType === 'step_failed' && 'eventData' in data) {
|
|
@@ -392,7 +382,6 @@ export function createEventsStorage(basedir) {
|
|
|
392
382
|
const failedData = data.eventData;
|
|
393
383
|
if (validatedStep) {
|
|
394
384
|
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
395
|
-
const stepPath = path.join(basedir, 'steps', `${stepCompositeKey}.json`);
|
|
396
385
|
const error = {
|
|
397
386
|
message: typeof failedData.error === 'string'
|
|
398
387
|
? failedData.error
|
|
@@ -406,7 +395,7 @@ export function createEventsStorage(basedir) {
|
|
|
406
395
|
completedAt: now,
|
|
407
396
|
updatedAt: now,
|
|
408
397
|
};
|
|
409
|
-
await writeJSON(
|
|
398
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
|
|
410
399
|
}
|
|
411
400
|
}
|
|
412
401
|
else if (data.eventType === 'step_retrying' && 'eventData' in data) {
|
|
@@ -415,7 +404,6 @@ export function createEventsStorage(basedir) {
|
|
|
415
404
|
const retryData = data.eventData;
|
|
416
405
|
if (validatedStep) {
|
|
417
406
|
const stepCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
418
|
-
const stepPath = path.join(basedir, 'steps', `${stepCompositeKey}.json`);
|
|
419
407
|
step = {
|
|
420
408
|
...validatedStep,
|
|
421
409
|
status: 'pending',
|
|
@@ -428,7 +416,7 @@ export function createEventsStorage(basedir) {
|
|
|
428
416
|
retryAfter: retryData.retryAfter,
|
|
429
417
|
updatedAt: now,
|
|
430
418
|
};
|
|
431
|
-
await writeJSON(
|
|
419
|
+
await writeJSON(taggedPath(basedir, 'steps', stepCompositeKey, tag), step, { overwrite: true });
|
|
432
420
|
}
|
|
433
421
|
}
|
|
434
422
|
else if (
|
|
@@ -436,19 +424,15 @@ export function createEventsStorage(basedir) {
|
|
|
436
424
|
data.eventType === 'hook_created' &&
|
|
437
425
|
'eventData' in data) {
|
|
438
426
|
const hookData = data.eventData;
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
break;
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
if (hasConflict) {
|
|
427
|
+
// Atomically claim the token using an exclusive-create constraint file.
|
|
428
|
+
// This avoids the TOCTOU race of the previous read-all-then-check approach.
|
|
429
|
+
const constraintPath = path.join(basedir, 'hooks', 'tokens', `${hashToken(hookData.token)}.json`);
|
|
430
|
+
const tokenClaimed = await writeExclusive(constraintPath, JSON.stringify({
|
|
431
|
+
token: hookData.token,
|
|
432
|
+
hookId: data.correlationId,
|
|
433
|
+
runId: effectiveRunId,
|
|
434
|
+
}));
|
|
435
|
+
if (!tokenClaimed) {
|
|
452
436
|
// Create hook_conflict event instead of hook_created
|
|
453
437
|
// This allows the workflow to continue and fail gracefully when the hook is awaited
|
|
454
438
|
const conflictEvent = {
|
|
@@ -464,10 +448,9 @@ export function createEventsStorage(basedir) {
|
|
|
464
448
|
};
|
|
465
449
|
// Store the conflict event
|
|
466
450
|
const compositeKey = `${effectiveRunId}-${eventId}`;
|
|
467
|
-
|
|
468
|
-
await writeJSON(eventPath, conflictEvent);
|
|
451
|
+
await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), conflictEvent);
|
|
469
452
|
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
470
|
-
const filteredEvent =
|
|
453
|
+
const filteredEvent = stripEventDataRefs(conflictEvent, resolveData);
|
|
471
454
|
// Return EventResult with conflict event (no hook entity created)
|
|
472
455
|
return {
|
|
473
456
|
event: filteredEvent,
|
|
@@ -489,20 +472,24 @@ export function createEventsStorage(basedir) {
|
|
|
489
472
|
specVersion: effectiveSpecVersion,
|
|
490
473
|
isWebhook: hookData.isWebhook ?? false,
|
|
491
474
|
};
|
|
492
|
-
|
|
493
|
-
await writeJSON(hookPath, hook);
|
|
475
|
+
await writeJSON(taggedPath(basedir, 'hooks', data.correlationId, tag), hook);
|
|
494
476
|
}
|
|
495
477
|
else if (data.eventType === 'hook_disposed') {
|
|
496
|
-
//
|
|
497
|
-
const hookPath =
|
|
478
|
+
// Read the hook to get its token before deleting
|
|
479
|
+
const hookPath = taggedPath(basedir, 'hooks', data.correlationId, tag);
|
|
480
|
+
const existingHook = await readJSONWithFallback(basedir, 'hooks', data.correlationId, HookSchema, tag);
|
|
481
|
+
if (existingHook) {
|
|
482
|
+
// Delete the token constraint file to free up the token for reuse
|
|
483
|
+
const disposedConstraintPath = path.join(basedir, 'hooks', 'tokens', `${hashToken(existingHook.token)}.json`);
|
|
484
|
+
await deleteJSON(disposedConstraintPath);
|
|
485
|
+
}
|
|
498
486
|
await deleteJSON(hookPath);
|
|
499
487
|
}
|
|
500
488
|
else if (data.eventType === 'wait_created' && 'eventData' in data) {
|
|
501
489
|
// wait_created: Creates wait entity with status 'waiting'
|
|
502
490
|
const waitData = data.eventData;
|
|
503
491
|
const waitCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
504
|
-
const
|
|
505
|
-
const existingWait = await readJSON(waitPath, WaitSchema);
|
|
492
|
+
const existingWait = await readJSONWithFallback(basedir, 'waits', waitCompositeKey, WaitSchema, tag);
|
|
506
493
|
if (existingWait) {
|
|
507
494
|
throw new WorkflowAPIError(`Wait "${data.correlationId}" already exists`, { status: 409 });
|
|
508
495
|
}
|
|
@@ -516,13 +503,12 @@ export function createEventsStorage(basedir) {
|
|
|
516
503
|
updatedAt: now,
|
|
517
504
|
specVersion: effectiveSpecVersion,
|
|
518
505
|
};
|
|
519
|
-
await writeJSON(
|
|
506
|
+
await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait);
|
|
520
507
|
}
|
|
521
508
|
else if (data.eventType === 'wait_completed') {
|
|
522
509
|
// wait_completed: Transitions wait to 'completed', rejects duplicates
|
|
523
510
|
const waitCompositeKey = `${effectiveRunId}-${data.correlationId}`;
|
|
524
|
-
const
|
|
525
|
-
const existingWait = await readJSON(waitPath, WaitSchema);
|
|
511
|
+
const existingWait = await readJSONWithFallback(basedir, 'waits', waitCompositeKey, WaitSchema, tag);
|
|
526
512
|
if (!existingWait) {
|
|
527
513
|
throw new WorkflowAPIError(`Wait "${data.correlationId}" not found`, {
|
|
528
514
|
status: 404,
|
|
@@ -537,16 +523,15 @@ export function createEventsStorage(basedir) {
|
|
|
537
523
|
completedAt: now,
|
|
538
524
|
updatedAt: now,
|
|
539
525
|
};
|
|
540
|
-
await writeJSON(
|
|
526
|
+
await writeJSON(taggedPath(basedir, 'waits', waitCompositeKey, tag), wait, { overwrite: true });
|
|
541
527
|
}
|
|
542
528
|
// Note: hook_received events are stored in the event log but don't
|
|
543
529
|
// modify the Hook entity (which doesn't have a payload field)
|
|
544
530
|
// Store event using composite key {runId}-{eventId}
|
|
545
531
|
const compositeKey = `${effectiveRunId}-${eventId}`;
|
|
546
|
-
|
|
547
|
-
await writeJSON(eventPath, event);
|
|
532
|
+
await writeJSON(taggedPath(basedir, 'events', compositeKey, tag), event);
|
|
548
533
|
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
549
|
-
const filteredEvent =
|
|
534
|
+
const filteredEvent = stripEventDataRefs(event, resolveData);
|
|
550
535
|
// Return EventResult with event and any created/updated entity
|
|
551
536
|
return {
|
|
552
537
|
event: filteredEvent,
|
|
@@ -558,13 +543,12 @@ export function createEventsStorage(basedir) {
|
|
|
558
543
|
},
|
|
559
544
|
async get(runId, eventId, params) {
|
|
560
545
|
const compositeKey = `${runId}-${eventId}`;
|
|
561
|
-
const
|
|
562
|
-
const event = await readJSON(eventPath, EventSchema);
|
|
546
|
+
const event = await readJSONWithFallback(basedir, 'events', compositeKey, EventSchema, tag);
|
|
563
547
|
if (!event) {
|
|
564
548
|
throw new Error(`Event ${eventId} in run ${runId} not found`);
|
|
565
549
|
}
|
|
566
550
|
const resolveData = params?.resolveData ?? DEFAULT_RESOLVE_DATA_OPTION;
|
|
567
|
-
return
|
|
551
|
+
return stripEventDataRefs(event, resolveData);
|
|
568
552
|
},
|
|
569
553
|
async list(params) {
|
|
570
554
|
const { runId } = params;
|
|
@@ -585,10 +569,7 @@ export function createEventsStorage(basedir) {
|
|
|
585
569
|
if (resolveData === 'none') {
|
|
586
570
|
return {
|
|
587
571
|
...result,
|
|
588
|
-
data: result.data.map((event) =>
|
|
589
|
-
const { eventData: _eventData, ...rest } = event;
|
|
590
|
-
return rest;
|
|
591
|
-
}),
|
|
572
|
+
data: result.data.map((event) => stripEventDataRefs(event, resolveData)),
|
|
592
573
|
};
|
|
593
574
|
}
|
|
594
575
|
return result;
|
|
@@ -613,10 +594,7 @@ export function createEventsStorage(basedir) {
|
|
|
613
594
|
if (resolveData === 'none') {
|
|
614
595
|
return {
|
|
615
596
|
...result,
|
|
616
|
-
data: result.data.map((event) =>
|
|
617
|
-
const { eventData: _eventData, ...rest } = event;
|
|
618
|
-
return rest;
|
|
619
|
-
}),
|
|
597
|
+
data: result.data.map((event) => stripEventDataRefs(event, resolveData)),
|
|
620
598
|
};
|
|
621
599
|
}
|
|
622
600
|
return result;
|