@workflow/world-postgres 4.1.0-beta.45 → 4.1.0-beta.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -7
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +7 -6
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +10 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/drizzle/index.d.ts +3 -3
- package/dist/drizzle/index.d.ts.map +1 -1
- package/dist/drizzle/index.js +3 -3
- package/dist/drizzle/index.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -16
- package/dist/index.js.map +1 -1
- package/dist/queue.d.ts +2 -2
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +112 -41
- package/dist/queue.js.map +1 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +127 -75
- package/dist/storage.js.map +1 -1
- package/dist/streamer.d.ts +9 -2
- package/dist/streamer.d.ts.map +1 -1
- package/dist/streamer.js +140 -9
- package/dist/streamer.js.map +1 -1
- package/package.json +10 -8
package/dist/storage.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { HookNotFoundError, RunNotSupportedError,
|
|
2
|
-
import { EventSchema, HookSchema, isLegacySpecVersion, requiresNewerWorld, SPEC_VERSION_CURRENT, StepSchema, validateUlidTimestamp, WorkflowRunSchema, } from '@workflow/world';
|
|
1
|
+
import { EntityConflictError, HookNotFoundError, RunExpiredError, RunNotSupportedError, TooEarlyError, WorkflowWorldError, WorkflowRunNotFoundError, } from '@workflow/errors';
|
|
2
|
+
import { EventSchema, HookSchema, isLegacySpecVersion, requiresNewerWorld, SPEC_VERSION_CURRENT, StepSchema, stripEventDataRefs, validateUlidTimestamp, WorkflowRunSchema, } from '@workflow/world';
|
|
3
3
|
import { and, asc, desc, eq, gt, lt, notInArray, sql } from 'drizzle-orm';
|
|
4
4
|
import { monotonicFactory } from 'ulid';
|
|
5
5
|
import { Schema } from './drizzle/index.js';
|
|
@@ -73,7 +73,7 @@ export function createRunsStorage(drizzle) {
|
|
|
73
73
|
get: (async (id, params) => {
|
|
74
74
|
const [value] = await get.execute({ id });
|
|
75
75
|
if (!value) {
|
|
76
|
-
throw new
|
|
76
|
+
throw new WorkflowRunNotFoundError(id);
|
|
77
77
|
}
|
|
78
78
|
value.output ||= value.outputJson;
|
|
79
79
|
value.input ||= value.inputJson;
|
|
@@ -179,7 +179,7 @@ async function handleLegacyEventPostgres(drizzle, runId, eventId, data, currentR
|
|
|
179
179
|
runId,
|
|
180
180
|
eventId,
|
|
181
181
|
});
|
|
182
|
-
return { event:
|
|
182
|
+
return { event: stripEventDataRefs(event, resolveData) };
|
|
183
183
|
}
|
|
184
184
|
default:
|
|
185
185
|
throw new Error(`Event type '${data.eventType}' not supported for legacy runs ` +
|
|
@@ -242,7 +242,7 @@ export function createEventsStorage(drizzle) {
|
|
|
242
242
|
if (data.eventType === 'run_created' && runId && runId !== '') {
|
|
243
243
|
const validationError = validateUlidTimestamp(effectiveRunId, 'wrun_');
|
|
244
244
|
if (validationError) {
|
|
245
|
-
throw new
|
|
245
|
+
throw new WorkflowWorldError(validationError);
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
// specVersion is always sent by the runtime, but we provide a fallback for safety
|
|
@@ -256,7 +256,11 @@ export function createEventsStorage(drizzle) {
|
|
|
256
256
|
// Helper to check if run is in terminal state
|
|
257
257
|
const isRunTerminal = (status) => ['completed', 'failed', 'cancelled'].includes(status);
|
|
258
258
|
// Helper to check if step is in terminal state
|
|
259
|
-
const isStepTerminal = (status) => ['completed', 'failed'].includes(status);
|
|
259
|
+
const isStepTerminal = (status) => ['completed', 'failed', 'cancelled'].includes(status);
|
|
260
|
+
// Terminal step statuses for use in SQL WHERE clauses (atomic guard).
|
|
261
|
+
// Must match the Vercel world's conditional expressions:
|
|
262
|
+
// ne(status, 'completed') AND ne(status, 'failed') AND ne(status, 'cancelled')
|
|
263
|
+
const terminalStepStatuses = ['completed', 'failed', 'cancelled'];
|
|
260
264
|
// ============================================================
|
|
261
265
|
// VALIDATION: Terminal state and event ordering checks
|
|
262
266
|
// ============================================================
|
|
@@ -321,20 +325,20 @@ export function createEventsStorage(drizzle) {
|
|
|
321
325
|
const parsed = EventSchema.parse(result);
|
|
322
326
|
const resolveData = params?.resolveData ?? 'all';
|
|
323
327
|
return {
|
|
324
|
-
event:
|
|
328
|
+
event: stripEventDataRefs(parsed, resolveData),
|
|
325
329
|
run: fullRun ? deserializeRunError(compact(fullRun)) : undefined,
|
|
326
330
|
};
|
|
327
331
|
}
|
|
328
332
|
// Run state transitions are not allowed on terminal runs
|
|
329
333
|
if (runTerminalEvents.includes(data.eventType) ||
|
|
330
334
|
data.eventType === 'run_cancelled') {
|
|
331
|
-
throw new
|
|
335
|
+
throw new EntityConflictError(`Cannot transition run from terminal state "${currentRun.status}"`);
|
|
332
336
|
}
|
|
333
337
|
// Creating new entities on terminal runs is not allowed
|
|
334
338
|
if (data.eventType === 'step_created' ||
|
|
335
339
|
data.eventType === 'hook_created' ||
|
|
336
340
|
data.eventType === 'wait_created') {
|
|
337
|
-
throw new
|
|
341
|
+
throw new EntityConflictError(`Cannot create new entities on run in terminal state "${currentRun.status}"`);
|
|
338
342
|
}
|
|
339
343
|
}
|
|
340
344
|
// Step-related event validation (ordering and terminal state)
|
|
@@ -352,18 +356,16 @@ export function createEventsStorage(drizzle) {
|
|
|
352
356
|
validatedStep = existingStep ?? null;
|
|
353
357
|
// Event ordering: step must exist before these events
|
|
354
358
|
if (!validatedStep) {
|
|
355
|
-
throw new
|
|
356
|
-
status: 404,
|
|
357
|
-
});
|
|
359
|
+
throw new WorkflowWorldError(`Step "${data.correlationId}" not found`);
|
|
358
360
|
}
|
|
359
361
|
// Step terminal state validation
|
|
360
362
|
if (isStepTerminal(validatedStep.status)) {
|
|
361
|
-
throw new
|
|
363
|
+
throw new EntityConflictError(`Cannot modify step in terminal state "${validatedStep.status}"`);
|
|
362
364
|
}
|
|
363
365
|
// On terminal runs: only allow completing/failing in-progress steps
|
|
364
366
|
if (currentRun && isRunTerminal(currentRun.status)) {
|
|
365
367
|
if (validatedStep.status !== 'running') {
|
|
366
|
-
throw new
|
|
368
|
+
throw new RunExpiredError(`Cannot modify non-running step on run in terminal state "${currentRun.status}"`);
|
|
367
369
|
}
|
|
368
370
|
}
|
|
369
371
|
}
|
|
@@ -377,9 +379,7 @@ export function createEventsStorage(drizzle) {
|
|
|
377
379
|
.where(eq(Schema.hooks.hookId, data.correlationId))
|
|
378
380
|
.limit(1);
|
|
379
381
|
if (!existingHook) {
|
|
380
|
-
throw new
|
|
381
|
-
status: 404,
|
|
382
|
-
});
|
|
382
|
+
throw new HookNotFoundError(data.correlationId);
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
385
|
// ============================================================
|
|
@@ -421,7 +421,12 @@ export function createEventsStorage(drizzle) {
|
|
|
421
421
|
run = deserializeRunError(compact(runValue));
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
|
+
// Terminal run statuses for use in SQL WHERE clauses (atomic guard).
|
|
425
|
+
// Must match the Vercel world's conditional expressions:
|
|
426
|
+
// ne(status, 'completed') AND ne(status, 'failed') AND ne(status, 'cancelled')
|
|
427
|
+
const terminalRunStatuses = ['completed', 'failed', 'cancelled'];
|
|
424
428
|
// Handle run_completed event: update run status and cleanup hooks
|
|
429
|
+
// Uses conditional UPDATE to prevent completing an already-terminal run.
|
|
425
430
|
if (data.eventType === 'run_completed') {
|
|
426
431
|
const eventData = data.eventData;
|
|
427
432
|
const [runValue] = await drizzle
|
|
@@ -432,11 +437,22 @@ export function createEventsStorage(drizzle) {
|
|
|
432
437
|
completedAt: now,
|
|
433
438
|
updatedAt: now,
|
|
434
439
|
})
|
|
435
|
-
.where(eq(Schema.runs.runId, effectiveRunId))
|
|
440
|
+
.where(and(eq(Schema.runs.runId, effectiveRunId), notInArray(Schema.runs.status, terminalRunStatuses)))
|
|
436
441
|
.returning();
|
|
437
442
|
if (runValue) {
|
|
438
443
|
run = deserializeRunError(compact(runValue));
|
|
439
444
|
}
|
|
445
|
+
else {
|
|
446
|
+
const [existing] = await getRunForValidation.execute({
|
|
447
|
+
runId: effectiveRunId,
|
|
448
|
+
});
|
|
449
|
+
if (!existing) {
|
|
450
|
+
throw new WorkflowRunNotFoundError(effectiveRunId);
|
|
451
|
+
}
|
|
452
|
+
if (isRunTerminal(existing.status)) {
|
|
453
|
+
throw new EntityConflictError(`Cannot transition run from terminal state "${existing.status}"`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
440
456
|
// Delete all hooks and waits for this run to allow token reuse
|
|
441
457
|
await Promise.all([
|
|
442
458
|
drizzle
|
|
@@ -448,6 +464,7 @@ export function createEventsStorage(drizzle) {
|
|
|
448
464
|
]);
|
|
449
465
|
}
|
|
450
466
|
// Handle run_failed event: update run status and cleanup hooks
|
|
467
|
+
// Uses conditional UPDATE to prevent failing an already-terminal run.
|
|
451
468
|
if (data.eventType === 'run_failed') {
|
|
452
469
|
const eventData = data.eventData;
|
|
453
470
|
const errorMessage = typeof eventData.error === 'string'
|
|
@@ -465,11 +482,22 @@ export function createEventsStorage(drizzle) {
|
|
|
465
482
|
completedAt: now,
|
|
466
483
|
updatedAt: now,
|
|
467
484
|
})
|
|
468
|
-
.where(eq(Schema.runs.runId, effectiveRunId))
|
|
485
|
+
.where(and(eq(Schema.runs.runId, effectiveRunId), notInArray(Schema.runs.status, terminalRunStatuses)))
|
|
469
486
|
.returning();
|
|
470
487
|
if (runValue) {
|
|
471
488
|
run = deserializeRunError(compact(runValue));
|
|
472
489
|
}
|
|
490
|
+
else {
|
|
491
|
+
const [existing] = await getRunForValidation.execute({
|
|
492
|
+
runId: effectiveRunId,
|
|
493
|
+
});
|
|
494
|
+
if (!existing) {
|
|
495
|
+
throw new WorkflowRunNotFoundError(effectiveRunId);
|
|
496
|
+
}
|
|
497
|
+
if (isRunTerminal(existing.status)) {
|
|
498
|
+
throw new EntityConflictError(`Cannot transition run from terminal state "${existing.status}"`);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
473
501
|
// Delete all hooks and waits for this run to allow token reuse
|
|
474
502
|
await Promise.all([
|
|
475
503
|
drizzle
|
|
@@ -481,6 +509,9 @@ export function createEventsStorage(drizzle) {
|
|
|
481
509
|
]);
|
|
482
510
|
}
|
|
483
511
|
// Handle run_cancelled event: update run status and cleanup hooks
|
|
512
|
+
// Uses conditional UPDATE to prevent cancelling an already-terminal run.
|
|
513
|
+
// Note: idempotent run_cancelled on already-cancelled runs is handled
|
|
514
|
+
// earlier in the pre-validation block (creates event and returns early).
|
|
484
515
|
if (data.eventType === 'run_cancelled') {
|
|
485
516
|
const [runValue] = await drizzle
|
|
486
517
|
.update(Schema.runs)
|
|
@@ -489,11 +520,22 @@ export function createEventsStorage(drizzle) {
|
|
|
489
520
|
completedAt: now,
|
|
490
521
|
updatedAt: now,
|
|
491
522
|
})
|
|
492
|
-
.where(eq(Schema.runs.runId, effectiveRunId))
|
|
523
|
+
.where(and(eq(Schema.runs.runId, effectiveRunId), notInArray(Schema.runs.status, terminalRunStatuses)))
|
|
493
524
|
.returning();
|
|
494
525
|
if (runValue) {
|
|
495
526
|
run = deserializeRunError(compact(runValue));
|
|
496
527
|
}
|
|
528
|
+
else {
|
|
529
|
+
const [existing] = await getRunForValidation.execute({
|
|
530
|
+
runId: effectiveRunId,
|
|
531
|
+
});
|
|
532
|
+
if (!existing) {
|
|
533
|
+
throw new WorkflowRunNotFoundError(effectiveRunId);
|
|
534
|
+
}
|
|
535
|
+
if (isRunTerminal(existing.status)) {
|
|
536
|
+
throw new EntityConflictError(`Cannot transition run from terminal state "${existing.status}"`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
497
539
|
// Delete all hooks and waits for this run to allow token reuse
|
|
498
540
|
await Promise.all([
|
|
499
541
|
drizzle
|
|
@@ -527,40 +569,53 @@ export function createEventsStorage(drizzle) {
|
|
|
527
569
|
}
|
|
528
570
|
// Handle step_started event: increment attempt, set status to 'running'
|
|
529
571
|
// Sets startedAt (maps to startedAt) only on first start
|
|
530
|
-
//
|
|
572
|
+
// Uses conditional UPDATE to prevent re-starting a step that has already
|
|
573
|
+
// reached a terminal state (completed/failed). Without this guard a
|
|
574
|
+
// concurrent step_started could revert a completed step back to 'running',
|
|
575
|
+
// allowing a duplicate execution that corrupts the event log.
|
|
531
576
|
if (data.eventType === 'step_started') {
|
|
532
577
|
// Check if retryAfter timestamp hasn't been reached yet
|
|
533
578
|
if (validatedStep?.retryAfter &&
|
|
534
579
|
validatedStep.retryAfter.getTime() > Date.now()) {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
stepId: data.correlationId,
|
|
539
|
-
retryAfter: validatedStep.retryAfter.toISOString(),
|
|
540
|
-
};
|
|
541
|
-
throw err;
|
|
580
|
+
throw new TooEarlyError(`Cannot start step "${data.correlationId}": retryAfter timestamp has not been reached yet`, {
|
|
581
|
+
retryAfter: Math.ceil((validatedStep.retryAfter.getTime() - Date.now()) / 1000),
|
|
582
|
+
});
|
|
542
583
|
}
|
|
543
|
-
const isFirstStart = !validatedStep?.startedAt;
|
|
544
|
-
const hadRetryAfter = !!validatedStep?.retryAfter;
|
|
545
584
|
const [stepValue] = await drizzle
|
|
546
585
|
.update(Schema.steps)
|
|
547
586
|
.set({
|
|
548
587
|
status: 'running',
|
|
549
588
|
// Increment attempt counter using SQL
|
|
550
589
|
attempt: sql `${Schema.steps.attempt} + 1`,
|
|
551
|
-
// Only set startedAt on first start
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
590
|
+
// Only set startedAt on first start — use COALESCE so concurrent
|
|
591
|
+
// step_started calls can't clobber the original timestamp.
|
|
592
|
+
startedAt: sql `COALESCE(${Schema.steps.startedAt}, ${now.toISOString()})`,
|
|
593
|
+
// Always clear retryAfter now that the step has started
|
|
594
|
+
retryAfter: null,
|
|
555
595
|
})
|
|
556
|
-
.where(and(eq(Schema.steps.runId, effectiveRunId), eq(Schema.steps.stepId, data.correlationId)
|
|
596
|
+
.where(and(eq(Schema.steps.runId, effectiveRunId), eq(Schema.steps.stepId, data.correlationId),
|
|
597
|
+
// Only update if not already in terminal state (prevents TOCTOU race)
|
|
598
|
+
notInArray(Schema.steps.status, terminalStepStatuses)))
|
|
557
599
|
.returning();
|
|
558
600
|
if (stepValue) {
|
|
559
601
|
step = deserializeStepError(compact(stepValue));
|
|
560
602
|
}
|
|
603
|
+
else {
|
|
604
|
+
// Step not updated - check if it exists and why
|
|
605
|
+
const [existing] = await getStepForValidation.execute({
|
|
606
|
+
runId: effectiveRunId,
|
|
607
|
+
stepId: data.correlationId,
|
|
608
|
+
});
|
|
609
|
+
if (!existing) {
|
|
610
|
+
throw new WorkflowWorldError(`Step "${data.correlationId}" not found`);
|
|
611
|
+
}
|
|
612
|
+
if (isStepTerminal(existing.status)) {
|
|
613
|
+
throw new EntityConflictError(`Cannot modify step in terminal state "${existing.status}"`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
561
616
|
}
|
|
562
617
|
// Handle step_completed event: update step status
|
|
563
|
-
// Uses conditional UPDATE to
|
|
618
|
+
// Uses conditional UPDATE to prevent completing an already-terminal step.
|
|
564
619
|
if (data.eventType === 'step_completed') {
|
|
565
620
|
const eventData = data.eventData;
|
|
566
621
|
const [stepValue] = await drizzle
|
|
@@ -570,9 +625,7 @@ export function createEventsStorage(drizzle) {
|
|
|
570
625
|
output: eventData.result,
|
|
571
626
|
completedAt: now,
|
|
572
627
|
})
|
|
573
|
-
.where(and(eq(Schema.steps.runId, effectiveRunId), eq(Schema.steps.stepId, data.correlationId),
|
|
574
|
-
// Only update if not already in terminal state (validation in WHERE clause)
|
|
575
|
-
notInArray(Schema.steps.status, ['completed', 'failed'])))
|
|
628
|
+
.where(and(eq(Schema.steps.runId, effectiveRunId), eq(Schema.steps.stepId, data.correlationId), notInArray(Schema.steps.status, terminalStepStatuses)))
|
|
576
629
|
.returning();
|
|
577
630
|
if (stepValue) {
|
|
578
631
|
step = deserializeStepError(compact(stepValue));
|
|
@@ -584,15 +637,15 @@ export function createEventsStorage(drizzle) {
|
|
|
584
637
|
stepId: data.correlationId,
|
|
585
638
|
});
|
|
586
639
|
if (!existing) {
|
|
587
|
-
throw new
|
|
640
|
+
throw new WorkflowWorldError(`Step "${data.correlationId}" not found`);
|
|
588
641
|
}
|
|
589
|
-
if (
|
|
590
|
-
throw new
|
|
642
|
+
if (isStepTerminal(existing.status)) {
|
|
643
|
+
throw new EntityConflictError(`Cannot modify step in terminal state "${existing.status}"`);
|
|
591
644
|
}
|
|
592
645
|
}
|
|
593
646
|
}
|
|
594
647
|
// Handle step_failed event: terminal state with error
|
|
595
|
-
// Uses conditional UPDATE to
|
|
648
|
+
// Uses conditional UPDATE to prevent failing an already-terminal step.
|
|
596
649
|
if (data.eventType === 'step_failed') {
|
|
597
650
|
const eventData = data.eventData;
|
|
598
651
|
const errorMessage = typeof eventData.error === 'string'
|
|
@@ -608,9 +661,7 @@ export function createEventsStorage(drizzle) {
|
|
|
608
661
|
},
|
|
609
662
|
completedAt: now,
|
|
610
663
|
})
|
|
611
|
-
.where(and(eq(Schema.steps.runId, effectiveRunId), eq(Schema.steps.stepId, data.correlationId),
|
|
612
|
-
// Only update if not already in terminal state (validation in WHERE clause)
|
|
613
|
-
notInArray(Schema.steps.status, ['completed', 'failed'])))
|
|
664
|
+
.where(and(eq(Schema.steps.runId, effectiveRunId), eq(Schema.steps.stepId, data.correlationId), notInArray(Schema.steps.status, terminalStepStatuses)))
|
|
614
665
|
.returning();
|
|
615
666
|
if (stepValue) {
|
|
616
667
|
step = deserializeStepError(compact(stepValue));
|
|
@@ -622,14 +673,15 @@ export function createEventsStorage(drizzle) {
|
|
|
622
673
|
stepId: data.correlationId,
|
|
623
674
|
});
|
|
624
675
|
if (!existing) {
|
|
625
|
-
throw new
|
|
676
|
+
throw new WorkflowWorldError(`Step "${data.correlationId}" not found`);
|
|
626
677
|
}
|
|
627
|
-
if (
|
|
628
|
-
throw new
|
|
678
|
+
if (isStepTerminal(existing.status)) {
|
|
679
|
+
throw new EntityConflictError(`Cannot modify step in terminal state "${existing.status}"`);
|
|
629
680
|
}
|
|
630
681
|
}
|
|
631
682
|
}
|
|
632
683
|
// Handle step_retrying event: sets status back to 'pending', records error
|
|
684
|
+
// Uses conditional UPDATE to prevent retrying an already-terminal step.
|
|
633
685
|
if (data.eventType === 'step_retrying') {
|
|
634
686
|
const eventData = data.eventData;
|
|
635
687
|
const errorMessage = typeof eventData.error === 'string'
|
|
@@ -645,11 +697,24 @@ export function createEventsStorage(drizzle) {
|
|
|
645
697
|
},
|
|
646
698
|
retryAfter: eventData.retryAfter,
|
|
647
699
|
})
|
|
648
|
-
.where(and(eq(Schema.steps.runId, effectiveRunId), eq(Schema.steps.stepId, data.correlationId)))
|
|
700
|
+
.where(and(eq(Schema.steps.runId, effectiveRunId), eq(Schema.steps.stepId, data.correlationId), notInArray(Schema.steps.status, terminalStepStatuses)))
|
|
649
701
|
.returning();
|
|
650
702
|
if (stepValue) {
|
|
651
703
|
step = deserializeStepError(compact(stepValue));
|
|
652
704
|
}
|
|
705
|
+
else {
|
|
706
|
+
// Step not updated - check if it exists and why
|
|
707
|
+
const [existing] = await getStepForValidation.execute({
|
|
708
|
+
runId: effectiveRunId,
|
|
709
|
+
stepId: data.correlationId,
|
|
710
|
+
});
|
|
711
|
+
if (!existing) {
|
|
712
|
+
throw new WorkflowWorldError(`Step "${data.correlationId}" not found`);
|
|
713
|
+
}
|
|
714
|
+
if (isStepTerminal(existing.status)) {
|
|
715
|
+
throw new EntityConflictError(`Cannot modify step in terminal state "${existing.status}"`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
653
718
|
}
|
|
654
719
|
// Handle hook_created event: create hook entity
|
|
655
720
|
// Uses prepared statement for token uniqueness check (performance optimization)
|
|
@@ -677,7 +742,7 @@ export function createEventsStorage(drizzle) {
|
|
|
677
742
|
})
|
|
678
743
|
.returning({ createdAt: events.createdAt });
|
|
679
744
|
if (!conflictValue) {
|
|
680
|
-
throw new
|
|
745
|
+
throw new EntityConflictError(`Event ${eventId} could not be created`);
|
|
681
746
|
}
|
|
682
747
|
const conflictResult = {
|
|
683
748
|
eventType: 'hook_conflict',
|
|
@@ -690,7 +755,7 @@ export function createEventsStorage(drizzle) {
|
|
|
690
755
|
const parsedConflict = EventSchema.parse(conflictResult);
|
|
691
756
|
const resolveData = params?.resolveData ?? 'all';
|
|
692
757
|
return {
|
|
693
|
-
event:
|
|
758
|
+
event: stripEventDataRefs(parsedConflict, resolveData),
|
|
694
759
|
run,
|
|
695
760
|
step,
|
|
696
761
|
hook: undefined,
|
|
@@ -751,7 +816,7 @@ export function createEventsStorage(drizzle) {
|
|
|
751
816
|
};
|
|
752
817
|
}
|
|
753
818
|
else {
|
|
754
|
-
throw new
|
|
819
|
+
throw new EntityConflictError(`Wait "${data.correlationId}" already exists`);
|
|
755
820
|
}
|
|
756
821
|
}
|
|
757
822
|
// Handle wait_completed event: transition wait to 'completed'
|
|
@@ -784,10 +849,10 @@ export function createEventsStorage(drizzle) {
|
|
|
784
849
|
waitId,
|
|
785
850
|
});
|
|
786
851
|
if (!existing) {
|
|
787
|
-
throw new
|
|
852
|
+
throw new WorkflowWorldError(`Wait "${data.correlationId}" not found`);
|
|
788
853
|
}
|
|
789
854
|
if (existing.status === 'completed') {
|
|
790
|
-
throw new
|
|
855
|
+
throw new EntityConflictError(`Wait "${data.correlationId}" already completed`);
|
|
791
856
|
}
|
|
792
857
|
}
|
|
793
858
|
}
|
|
@@ -803,15 +868,13 @@ export function createEventsStorage(drizzle) {
|
|
|
803
868
|
})
|
|
804
869
|
.returning({ createdAt: events.createdAt });
|
|
805
870
|
if (!value) {
|
|
806
|
-
throw new
|
|
807
|
-
status: 409,
|
|
808
|
-
});
|
|
871
|
+
throw new EntityConflictError(`Event ${eventId} could not be created`);
|
|
809
872
|
}
|
|
810
873
|
const result = { ...data, ...value, runId: effectiveRunId, eventId };
|
|
811
874
|
const parsed = EventSchema.parse(result);
|
|
812
875
|
const resolveData = params?.resolveData ?? 'all';
|
|
813
876
|
return {
|
|
814
|
-
event:
|
|
877
|
+
event: stripEventDataRefs(parsed, resolveData),
|
|
815
878
|
run,
|
|
816
879
|
step,
|
|
817
880
|
hook,
|
|
@@ -825,14 +888,12 @@ export function createEventsStorage(drizzle) {
|
|
|
825
888
|
.where(and(eq(events.runId, runId), eq(events.eventId, eventId)))
|
|
826
889
|
.limit(1);
|
|
827
890
|
if (!value) {
|
|
828
|
-
throw new
|
|
829
|
-
status: 404,
|
|
830
|
-
});
|
|
891
|
+
throw new WorkflowWorldError(`Event not found: ${eventId}`);
|
|
831
892
|
}
|
|
832
893
|
value.eventData ||= value.eventDataJson;
|
|
833
894
|
const parsed = EventSchema.parse(compact(value));
|
|
834
895
|
const resolveData = params?.resolveData ?? 'all';
|
|
835
|
-
return
|
|
896
|
+
return stripEventDataRefs(parsed, resolveData);
|
|
836
897
|
},
|
|
837
898
|
async list(params) {
|
|
838
899
|
const limit = params?.pagination?.limit ?? 100;
|
|
@@ -852,7 +913,7 @@ export function createEventsStorage(drizzle) {
|
|
|
852
913
|
data: values.map((v) => {
|
|
853
914
|
v.eventData ||= v.eventDataJson;
|
|
854
915
|
const parsed = EventSchema.parse(compact(v));
|
|
855
|
-
return
|
|
916
|
+
return stripEventDataRefs(parsed, resolveData);
|
|
856
917
|
}),
|
|
857
918
|
cursor: values.at(-1)?.eventId ?? null,
|
|
858
919
|
hasMore: all.length > limit,
|
|
@@ -876,7 +937,7 @@ export function createEventsStorage(drizzle) {
|
|
|
876
937
|
data: values.map((v) => {
|
|
877
938
|
v.eventData ||= v.eventDataJson;
|
|
878
939
|
const parsed = EventSchema.parse(compact(v));
|
|
879
|
-
return
|
|
940
|
+
return stripEventDataRefs(parsed, resolveData);
|
|
880
941
|
}),
|
|
881
942
|
cursor: values.at(-1)?.eventId ?? null,
|
|
882
943
|
hasMore: all.length > limit,
|
|
@@ -957,9 +1018,7 @@ export function createStepsStorage(drizzle) {
|
|
|
957
1018
|
.where(whereClause)
|
|
958
1019
|
.limit(1);
|
|
959
1020
|
if (!value) {
|
|
960
|
-
throw new
|
|
961
|
-
status: 404,
|
|
962
|
-
});
|
|
1021
|
+
throw new WorkflowWorldError(`Step not found: ${stepId}`);
|
|
963
1022
|
}
|
|
964
1023
|
value.output ||= value.outputJson;
|
|
965
1024
|
value.input ||= value.inputJson;
|
|
@@ -1017,11 +1076,4 @@ function filterHookData(hook, resolveData) {
|
|
|
1017
1076
|
}
|
|
1018
1077
|
return hook;
|
|
1019
1078
|
}
|
|
1020
|
-
function filterEventData(event, resolveData) {
|
|
1021
|
-
if (resolveData === 'none' && 'eventData' in event) {
|
|
1022
|
-
const { eventData: _, ...rest } = event;
|
|
1023
|
-
return rest;
|
|
1024
|
-
}
|
|
1025
|
-
return event;
|
|
1026
|
-
}
|
|
1027
1079
|
//# sourceMappingURL=storage.js.map
|