@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.
Files changed (53) hide show
  1. package/dist/config.d.ts +7 -0
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js.map +1 -1
  4. package/dist/fs.d.ts +34 -0
  5. package/dist/fs.d.ts.map +1 -1
  6. package/dist/fs.js +88 -0
  7. package/dist/fs.js.map +1 -1
  8. package/dist/index.d.ts +3 -1
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +50 -5
  11. package/dist/index.js.map +1 -1
  12. package/dist/init.d.ts.map +1 -1
  13. package/dist/init.js +6 -1
  14. package/dist/init.js.map +1 -1
  15. package/dist/queue.d.ts +1 -28
  16. package/dist/queue.d.ts.map +1 -1
  17. package/dist/queue.js +55 -89
  18. package/dist/queue.js.map +1 -1
  19. package/dist/storage/events-storage.d.ts +1 -1
  20. package/dist/storage/events-storage.d.ts.map +1 -1
  21. package/dist/storage/events-storage.js +51 -73
  22. package/dist/storage/events-storage.js.map +1 -1
  23. package/dist/storage/filters.d.ts +2 -6
  24. package/dist/storage/filters.d.ts.map +1 -1
  25. package/dist/storage/filters.js +1 -11
  26. package/dist/storage/filters.js.map +1 -1
  27. package/dist/storage/helpers.d.ts +4 -0
  28. package/dist/storage/helpers.d.ts.map +1 -1
  29. package/dist/storage/helpers.js +14 -4
  30. package/dist/storage/helpers.js.map +1 -1
  31. package/dist/storage/hooks-storage.d.ts +1 -1
  32. package/dist/storage/hooks-storage.d.ts.map +1 -1
  33. package/dist/storage/hooks-storage.js +7 -4
  34. package/dist/storage/hooks-storage.js.map +1 -1
  35. package/dist/storage/index.d.ts +1 -1
  36. package/dist/storage/index.d.ts.map +1 -1
  37. package/dist/storage/index.js +5 -5
  38. package/dist/storage/index.js.map +1 -1
  39. package/dist/storage/legacy.js +2 -2
  40. package/dist/storage/legacy.js.map +1 -1
  41. package/dist/storage/runs-storage.d.ts +1 -1
  42. package/dist/storage/runs-storage.d.ts.map +1 -1
  43. package/dist/storage/runs-storage.js +3 -4
  44. package/dist/storage/runs-storage.js.map +1 -1
  45. package/dist/storage/steps-storage.d.ts +1 -1
  46. package/dist/storage/steps-storage.d.ts.map +1 -1
  47. package/dist/storage/steps-storage.js +5 -6
  48. package/dist/storage/steps-storage.js.map +1 -1
  49. package/dist/streamer.d.ts +1 -1
  50. package/dist/streamer.d.ts.map +1 -1
  51. package/dist/streamer.js +37 -15
  52. package/dist/streamer.js.map +1 -1
  53. 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 createQueueExecutor(config) {
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 result = await executor.executeMessage({
145
- queueName,
146
- messageId,
147
- attempt: attempt + 1,
148
- body,
149
- headers: opts?.headers,
150
- });
151
- if (result.type === 'completed') {
152
- return;
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
- if (result.type === 'reschedule') {
155
- // Clamp to MAX_SAFE_TIMEOUT_MS to avoid Node.js setTimeout overflow warning.
156
- // When this fires early, the handler recalculates remaining time from
157
- // persistent state and returns another timeoutSeconds if needed.
158
- if (result.timeoutSeconds > 0) {
159
- const timeoutMs = Math.min(result.timeoutSeconds * 1000, MAX_SAFE_TIMEOUT_MS);
160
- await setTimeout(timeoutMs);
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
- defaultRetriesLeft++;
163
- continue;
126
+ catch { }
127
+ return;
164
128
  }
165
129
  console.error(`[local world] Failed to queue message`, {
166
130
  queueName,
167
- text: result.text,
168
- status: result.status,
169
- headers: result.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: executor.registerHandler,
204
+ registerHandler(prefix, handler) {
205
+ directHandlers.set(prefix, handler);
206
+ },
241
207
  async close() {
242
- await executor.close();
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":"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"}
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;AAc5B,SAAS,aAAa,CAAC,SAAyB;IAI9C,IAAI,SAAS,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IACrD,CAAC;IACD,IAAI,SAAS,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;IACzD,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,MAAuB;IACjD,wEAAwE;IACxE,gDAAgD;IAChD,wEAAwE;IACxE,uDAAuD;IACvD,0EAA0E;IAC1E,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC;QAC1B,cAAc,EAAE,CAAC;QACjB,WAAW,EAAE,IAAI;QACjB,gBAAgB,EAAE,MAAM;KACzB,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,IAAI,aAAa,EAAE,CAAC;IACtC,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC;IACtC,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,gCAAgC,CAAC,CAAC;IAE7D;;;OAGG;IACH,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAqB,CAAC;IACtD,2EAA2E;IAC3E,MAAM,cAAc,GAAG,IAAI,GAAG,EAAyB,CAAC;IAExD,MAAM,KAAK,GAAmB,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC/D,MAAM,OAAO,GAAG,EAAoB,CAAC;QAErC,IAAI,IAAI,EAAE,cAAc,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC3D,IAAI,QAAQ,EAAE,CAAC;gBACb,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;YACjC,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC1C,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,OAAO,UAAU,EAAE,EAAE,CAAC,CAAC;QAEzD,IAAI,IAAI,EAAE,cAAc,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC;YAChC,gBAAgB,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YACrC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE;gBAChB,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC/B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,CAAC,KAAK,IAAI,EAAE;YACV,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,CAAC,IAAI,CACV,qCAAqC,gCAAgC,yCAAyC,CAC/G,CAAC;gBACF,MAAM,SAAS,CAAC,OAAO,EAAE,CAAC;YAC5B,CAAC;YACD,IAAI,CAAC;gBACH,IAAI,kBAAkB,GAAG,CAAC,CAAC;gBAC3B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,kBAAkB,GAAG,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC;oBACxD,kBAAkB,EAAE,CAAC;oBAErB,MAAM,OAAO,GAA2B;wBACtC,GAAG,IAAI,EAAE,OAAO;wBAChB,cAAc,EAAE,kBAAkB;wBAClC,kBAAkB,EAAE,SAAS;wBAC7B,kBAAkB,EAAE,SAAS;wBAC7B,uBAAuB,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC,CAAC;qBAC7C,CAAC;oBACF,MAAM,aAAa,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;oBACjD,IAAI,QAAkB,CAAC;oBAEvB,IAAI,aAAa,EAAE,CAAC;wBAClB,MAAM,GAAG,GAAG,IAAI,OAAO,CACrB,4CAA4C,QAAQ,EAAE,EACtD;4BACE,MAAM,EAAE,MAAM;4BACd,OAAO;4BACP,IAAI;yBACL,CACF,CAAC;wBACF,QAAQ,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;oBACtC,CAAC;yBAAM,CAAC;wBACN,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,CAAC;wBAC7C,kIAAkI;wBAClI,QAAQ,GAAG,MAAM,KAAK,CACpB,GAAG,OAAO,4BAA4B,QAAQ,EAAE,EAChD;4BACE,MAAM,EAAE,MAAM;4BACd,MAAM,EAAE,MAAM;4BACd,UAAU,EAAE,SAAS;4BACrB,OAAO;4BACP,IAAI;yBACE,CACT,CAAC;oBACJ,CAAC;oBAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;oBAEnC,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;wBAChB,IAAI,CAAC;4BACH,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,CAAC;4BAC/D,IAAI,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;gCAC3D,6EAA6E;gCAC7E,sEAAsE;gCACtE,iEAAiE;gCACjE,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;oCACvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACxB,cAAc,GAAG,IAAI,EACrB,mBAAmB,CACpB,CAAC;oCACF,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;gCAC9B,CAAC;gCACD,kBAAkB,EAAE,CAAC;gCACrB,SAAS;4BACX,CAAC;wBACH,CAAC;wBAAC,MAAM,CAAC,CAAA,CAAC;wBACV,OAAO;oBACT,CAAC;oBAED,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE;wBACrD,SAAS;wBACT,IAAI;wBACJ,MAAM,EAAE,QAAQ,CAAC,MAAM;wBACvB,OAAO,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;wBACvD,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;qBACtB,CAAC,CAAC;gBACL,CAAC;gBAED,OAAO,CAAC,KAAK,CACX,uEAAuE,CACxE,CAAC;YACJ,CAAC;oBAAS,CAAC;gBACT,SAAS,CAAC,OAAO,EAAE,CAAC;YACtB,CAAC;QACH,CAAC,CAAC,EAAE;aACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACb,oFAAoF;YACpF,uEAAuE;YACvE,MAAM,YAAY,GAChB,GAAG,EAAE,IAAI,KAAK,YAAY,IAAI,GAAG,EAAE,IAAI,KAAK,iBAAiB,CAAC;YAChE,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC,CAAC;aACD,OAAO,CAAC,GAAG,EAAE;YACZ,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;gBACzB,EAAE,EAAE,CAAC;YACP,CAAC;QACH,CAAC,CAAC,CAAC;QAEL,OAAO,EAAE,SAAS,EAAE,CAAC;IACvB,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;QAC5B,kBAAkB,EAAE,cAAc;QAClC,kBAAkB,EAAE,SAAS;QAC7B,uBAAuB,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE;KAC3C,CAAC,CAAC;IAEH,MAAM,kBAAkB,GAAgC,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE;QAC1E,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;YACnB,MAAM,OAAO,GAAG,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;YAExE,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAClC,OAAO,QAAQ,CAAC,IAAI,CAClB;oBACE,KAAK,EAAE,CAAC,GAAG,CAAC,IAAI;wBACd,CAAC,CAAC,sBAAsB;wBACxB,CAAC,CAAC,0BAA0B;iBAC/B,EACD,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAC;YACJ,CAAC;YAED,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YACnD,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;YACnD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;YAEtD,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACtE,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,IAAI,aAAa,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7D,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;gBAEtE,IAAI,cAAc,GAAkB,IAAI,CAAC;gBACzC,IAAI,OAAO,MAAM,EAAE,cAAc,KAAK,QAAQ,EAAE,CAAC;oBAC/C,cAAc,GAAG,IAAI,CAAC,GAAG,CACvB,MAAM,CAAC,cAAc,EACrB,0BAA0B,CAC3B,CAAC;gBACJ,CAAC;gBAED,IAAI,cAAc,IAAI,IAAI,EAAE,CAAC;oBAC3B,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;gBAC3C,CAAC;gBAED,OAAO,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACvD,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,CAAC;IAEF,MAAM,eAAe,GAA6B,KAAK,IAAI,EAAE;QAC3D,MAAM,WAAW,GAAG,MAAM,cAAc,EAAE,CAAC;QAC3C,OAAO,aAAa,WAAW,CAAC,OAAO,EAAE,CAAC;IAC5C,CAAC,CAAC;IAEF,OAAO;QACL,KAAK;QACL,kBAAkB;QAClB,eAAe;QACf,eAAe,CACb,MAAyC,EACzC,OAAsB;YAEtB,cAAc,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACtC,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,SAAS,CAAC,KAAK,EAAE,CAAC;QAC1B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -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;AA4CzB;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2uBtE"}
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, readJSON, writeJSON, } from '../fs.js';
6
- import { filterEventData } from './filters.js';
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
- const runPath = path.join(basedir, 'runs', `${effectiveRunId}.json`);
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
- const eventPath = path.join(basedir, 'events', `${compositeKey}.json`);
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: filterEventData(event, resolveData),
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
- const stepPath = path.join(basedir, 'steps', `${stepCompositeKey}.json`);
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 hookPath = path.join(basedir, 'hooks', `${data.correlationId}.json`);
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
- const runPath = path.join(basedir, 'runs', `${effectiveRunId}.json`);
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(runPath, run, { overwrite: true });
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(runPath, run, { overwrite: true });
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(runPath, run, { overwrite: true });
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(runPath, run, { overwrite: true });
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
- const stepPath = path.join(basedir, 'steps', `${stepCompositeKey}.json`);
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(stepPath, step, { overwrite: true });
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(stepPath, step, { overwrite: true });
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(stepPath, step, { overwrite: true });
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(stepPath, step, { overwrite: true });
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
- // Check for duplicate token before creating hook
440
- const hooksDir = path.join(basedir, 'hooks');
441
- const hookFiles = await listJSONFiles(hooksDir);
442
- let hasConflict = false;
443
- for (const file of hookFiles) {
444
- const existingHookPath = path.join(hooksDir, `${file}.json`);
445
- const existingHook = await readJSON(existingHookPath, HookSchema);
446
- if (existingHook && existingHook.token === hookData.token) {
447
- hasConflict = true;
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
- const eventPath = path.join(basedir, 'events', `${compositeKey}.json`);
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 = filterEventData(conflictEvent, resolveData);
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
- const hookPath = path.join(basedir, 'hooks', `${data.correlationId}.json`);
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
- // Delete the hook when disposed
497
- const hookPath = path.join(basedir, 'hooks', `${data.correlationId}.json`);
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 waitPath = path.join(basedir, 'waits', `${waitCompositeKey}.json`);
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(waitPath, wait);
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 waitPath = path.join(basedir, 'waits', `${waitCompositeKey}.json`);
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(waitPath, wait, { overwrite: true });
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
- const eventPath = path.join(basedir, 'events', `${compositeKey}.json`);
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 = filterEventData(event, resolveData);
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 eventPath = path.join(basedir, 'events', `${compositeKey}.json`);
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 filterEventData(event, resolveData);
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;