@voltro/workflow 0.62.1 → 0.62.3

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/CHANGELOG.md CHANGED
@@ -39,6 +39,35 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.62.3] — 2026-09-03
43
+
44
+ ### Added
45
+
46
+ - **@voltro/runtime** — `EffectStore` now exposes the complete Promise-store mutation surface plus transactions that roll back while preserving an inner Effect Cause. `defineSubscriber` accepts `eachReplica: true` so local per-replica work can state its topology explicitly; it cannot be combined with `once`.
47
+
48
+ ### Fixed
49
+
50
+ - **@voltro/cli** — `voltro build` no longer starts Vite's HMR WebSocket while pre-rendering. A `RedirectError` from a static loader is now emitted as redirect metadata and served by `voltro start` with its exact status and `Location`; unrelated loader errors still fail the build.
51
+ - **@voltro/cli, @voltro/database, @voltro/plugin-storage** — `voltro doctor` now resolves grouped event bindings and narrows its hand-roll, junction, ownership, nullable-write, singleton-read, and UI reachability checks to evidence they can actually prove. Ids owned by external systems can be declared with `.externalId()`; plugin references and non-FK asset references carry that metadata automatically.
52
+
53
+ ---
54
+
55
+ ## [0.62.2] — 2026-09-03
56
+
57
+ ### Added
58
+
59
+ - **@voltro/client, @voltro/web, @voltro/runtime, @voltro/cli, @voltro/devtools, @voltro/workflow** — Reconnect incidents now have one bounded, payload-free evidence path: browser diagnostics retain runtime/socket/protocol/subscription transitions across a reload, the subscriptions panel exposes recovery progress and stuck re-issues, and `voltro support collect` joins that export to inspect snapshots and exact browser-to-replica socket ids. The opt-in browser fault harness can reproduce a bounded transport cut without a proxy; logs and traces remain excluded from the support bundle unless explicitly requested.
60
+
61
+ Workflow admission rows now retain queue-time app/schema provenance. `voltro workflows flow validate` checks queued payloads against the current release without returning them, while `flow retry <intentId>` re-arms one retained dead letter after its fix. Support bundles also inventory project `conflictColumns` callsites, and `voltro support sentry <eventId>` delegates source-map resolution diagnosis to Sentry's own CLI.
62
+ - **@voltro/runtime** — Successful rpc WebSocket upgrades now emit structured open and close records carrying the serving `replicaId` and a stable per-socket `connectionId`, so connection cuts can be correlated to an api replica without debug traffic.
63
+
64
+ ### Fixed
65
+
66
+ - **@voltro/client, @voltro/web, @voltro/react-native** — Subscription recovery now re-issues transport-failed active subscriptions only after the rpc protocol has cleared the previous socket error. A longer outage could previously reopen the WebSocket, immediately reject every re-issued subscription against the stale error, and leave the screen degraded until a reload.
67
+ - **@voltro/workflow, @voltro/cli, @voltro/devtools-ui** — Queued workflow starts whose stored payload no longer decodes now signal `onFailure` once and remain visible as dead-letter intents instead of retrying forever. Inspect or remove them with `voltro workflows flow` and `voltro workflows flow discard <intentId>`; a valid later debounce arrival for the same key replaces the obsolete payload and resumes normal draining.
68
+
69
+ ---
70
+
42
71
  ## [0.62.1] — 2026-09-02
43
72
 
44
73
  ### Fixed
@@ -46,6 +75,11 @@ _Changes staged for the next release accumulate here (rolled up from
46
75
  - **@voltro/client, @voltro/web, @voltro/react-native** — `useSubscriptionHealth` now clears failures when replacement subscriptions succeed after a reconnect; each api keeps one error bus across transport, auth-refresh, and subject-reconnect generations.
47
76
  - **@voltro/client, @voltro/web, @voltro/react-native** — Active subscriptions whose fibers ended in a transport failure are now re-issued when the rpc socket opens again, preserving their last rows and resume revision; `useConnectionStatus` stays degraded until every affected subscription delivers a later snapshot or delta.
48
77
 
78
+ **Correction:** In 0.62.1 that reissue runs on the raw WebSocket `open`, before
79
+ the rpc protocol clears the previous socket error. After a longer outage the
80
+ reissued fibers can therefore fail immediately and remain terminal. The next
81
+ patch moves recovery behind the protocol-ready `onOpen` effect.
82
+
49
83
  ### Internal (no consumer-facing effect)
50
84
 
51
85
  - **@voltro/local-first** — Release dependency floors now exclude the vulnerable `fast-uri` and `@xmldom/xmldom` ranges, and the local-first test toolchain uses the patched Tiptap release.
@@ -1,5 +1,5 @@
1
1
  import { I as e, P as t, S as n, g as r, v as i } from "./primitives-B-HlFCap.js";
2
- import { o as a } from "./src-B1XsZ6OO.js";
2
+ import { o as a } from "./src-CJ56XxpW.js";
3
3
  import { i as o, p as s } from "./cluster-OUXsLZ2x.js";
4
4
  import { Cron as c, Deferred as l, Effect as u, Fiber as d, Layer as f, Schema as p } from "effect";
5
5
  import { SqlClient as m } from "@effect/sql";
package/dist/index.d.ts CHANGED
@@ -125,6 +125,11 @@ export declare interface AdmissionDrainDeps {
125
125
  * lastError climb on the row) instead of a silent abandon.
126
126
  */
127
127
  readonly registeredWorkflows?: ReadonlySet<string>;
128
+ /** Classify a start failure that retrying the SAME stored payload cannot
129
+ * repair. The CLI wires `WorkflowPayloadError`; unknown engine/store errors
130
+ * stay on the retry path because abandoning transient work is worse than a
131
+ * visible retry. */
132
+ readonly isPermanentStartError?: (cause: unknown) => boolean;
128
133
  readonly log?: DrainLogger;
129
134
  readonly now?: () => number;
130
135
  }
@@ -243,6 +248,8 @@ export declare interface AdmitStartInput {
243
248
  readonly payload: unknown;
244
249
  readonly callerContext: unknown;
245
250
  readonly tenantId: string | null;
251
+ readonly queuedAppVersion?: string | null;
252
+ readonly queuedSchemaFingerprint?: string | null;
246
253
  readonly paused: boolean;
247
254
  readonly now: number;
248
255
  /**
@@ -760,6 +767,17 @@ export declare const CurrentWorkflowReplayShape: FiberRef.FiberRef<ReplayShapeTr
760
767
  * `Effect.locally(CurrentWorkflowRunId, runId)`. */
761
768
  export declare const CurrentWorkflowRunId: FiberRef.FiberRef<string | undefined>;
762
769
 
770
+ /**
771
+ * Stop retrying an intent whose exact payload can never start.
772
+ *
773
+ * The row deliberately remains in `_voltro_workflow_pending`: a deleted poison
774
+ * row and work that vanished are indistinguishable, while a retained row keeps
775
+ * the payload, caller context and error available to `voltro workflows flow`
776
+ * until an operator explicitly discards it. `pendingWindow` excludes it, so it
777
+ * cannot consume a drain slot or log again.
778
+ */
779
+ export declare const deadLetterIntent: (store: AdmissionDataStore, intent: PendingIntent, error: string, now: number) => Promise<boolean>;
780
+
763
781
  /**
764
782
  * Decide what happens to one start. Pure.
765
783
  *
@@ -935,6 +953,8 @@ export declare interface DrainOutcome {
935
953
  readonly expired: number;
936
954
  readonly evicted: number;
937
955
  readonly failed: number;
956
+ /** Pending intents moved out of the live drain set this tick. */
957
+ readonly deadLettered: number;
938
958
  /** Runs cancelled by `timeouts.finish` on this tick. */
939
959
  readonly timedOut: number;
940
960
  /**
@@ -1277,6 +1297,8 @@ export declare interface ParkDelayedStartInput {
1277
1297
  readonly payload: unknown;
1278
1298
  readonly callerContext: unknown;
1279
1299
  readonly tenantId: string | null;
1300
+ readonly queuedAppVersion?: string | null;
1301
+ readonly queuedSchemaFingerprint?: string | null;
1280
1302
  /** Epoch ms the start becomes an ARRIVAL. Must be in the future — the facade
1281
1303
  * starts a past-`at` immediately and never calls in here. */
1282
1304
  readonly at: number;
@@ -1367,6 +1389,8 @@ export declare interface PendingIntent {
1367
1389
  readonly mode: 'debounce' | 'batch' | 'throttle' | 'concurrency' | 'paused' | 'delayed';
1368
1390
  readonly payload: unknown;
1369
1391
  readonly callerContext: unknown;
1392
+ readonly queuedAppVersion: string | null;
1393
+ readonly queuedSchemaFingerprint: string | null;
1370
1394
  readonly priority: number;
1371
1395
  readonly dueAt: number;
1372
1396
  readonly deadlineAt: number | undefined;
@@ -1377,6 +1401,9 @@ export declare interface PendingIntent {
1377
1401
  * poison intent, and a poison intent that only ever manifested as "nothing
1378
1402
  * ran" is the failure this column removes. */
1379
1403
  readonly lastError: string | null;
1404
+ /** Present once this exact payload was classified as permanently unable to
1405
+ * start. Such rows remain inspectable but are not returned to the drainer. */
1406
+ readonly deadLetteredAt: number | undefined;
1380
1407
  }
1381
1408
 
1382
1409
  /**
@@ -1429,6 +1456,8 @@ declare interface PersistDeferInput {
1429
1456
  readonly payload: unknown;
1430
1457
  readonly callerContext: unknown;
1431
1458
  readonly tenantId: string | null;
1459
+ readonly queuedAppVersion?: string | null;
1460
+ readonly queuedSchemaFingerprint?: string | null;
1432
1461
  readonly now: number;
1433
1462
  /** Set when the drainer is re-deferring a row it already holds. */
1434
1463
  readonly existingId?: string;
@@ -1762,6 +1791,11 @@ export declare const resolveWorkflowMessageRun: (store: WorkflowMessageStore, ta
1762
1791
 
1763
1792
  export declare const resumeWorkflow: (store: AdmissionDataStore, workflowName: string) => Promise<boolean>;
1764
1793
 
1794
+ /** Re-arm one dead-lettered intent after an operator has deployed a schema or
1795
+ * handler fix. The payload and its queue-time provenance stay untouched; only
1796
+ * the terminal classification and retry history are reset. */
1797
+ export declare const retryDeadLetterIntent: (store: AdmissionDataStore, intentId: string, now: number) => Promise<boolean>;
1798
+
1765
1799
  export declare const sendWorkflowSignal: (input: {
1766
1800
  readonly store: WorkflowMessageStore;
1767
1801
  readonly recorder: WorkflowRunRecorderService;
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { A as e, B as t, C as n, D as r, E as i, G as a, H as o, I as s, L as c, M as l, N as u, O as d, P as f, R as p, S as m, T as h, U as g, V as _, W as v, _ as y, a as b, b as x, c as S, d as C, f as w, g as T, h as E, i as D, j as O, k, l as A, m as j, n as M, o as N, p as P, r as F, s as I, t as L, u as R, v as z, w as B, x as V, y as H, z as U } from "./primitives-B-HlFCap.js";
2
- import { $ as W, A as G, At as K, B as q, C as J, Ct as Y, D as X, Dt as Z, E as Q, Et as $, F as ee, Ft as te, G as ne, H as re, I as ie, It as ae, J as oe, K as se, L as ce, Lt as le, M as ue, Mt as de, N as fe, Nt as pe, O as me, Ot as he, P as ge, Pt as _e, Q as ve, R as ye, Rt as be, S as xe, St as Se, T as Ce, Tt as we, U as Te, V as Ee, W as De, X as Oe, Y as ke, Z as Ae, _ as je, _t as Me, a as Ne, at as Pe, b as Fe, bt as Ie, c as Le, ct as Re, d as ze, dt as Be, et as Ve, f as He, ft as Ue, g as We, gt as Ge, h as Ke, ht as qe, i as Je, it as Ye, j as Xe, jt as Ze, k as Qe, kt as $e, l as et, lt as tt, m as nt, mt as rt, n as it, nt as at, o as ot, ot as st, p as ct, pt as lt, q as ut, r as dt, rt as ft, s as pt, st as mt, t as ht, tt as gt, u as _t, ut as vt, v as yt, vt as bt, w as xt, wt as St, x as Ct, xt as wt, y as Tt, yt as Et, z as Dt, zt as Ot } from "./src-B1XsZ6OO.js";
3
- export { ft as ADMISSIONS_TABLE, ct as BUDGET_HOLDS_TABLE, nt as BudgetHoldExpired, De as CANCEL_ON_WATERMARK, l as CurrentWorkflowExecutionId, u as CurrentWorkflowPatches, B as CurrentWorkflowReplayShape, f as CurrentWorkflowRunId, Ke as DEFAULT_BUDGET_HOLD_TIMEOUT_MS, ne as DEFAULT_COLD_LOOKBACK_MS, ye as DEFAULT_DRAIN_BATCH, se as DEFAULT_EVENT_BATCH, Ye as DEFAULT_LEASE_MS, Je as DEFAULT_MAX_RUN_RECLAIMS, Ne as DEFAULT_REPLAY_SHAPE_LIMIT, q as DEFAULT_STALL_AFTER_MS, Ee as DEFAULT_STALL_EVENT_LOOKBACK, re as DEFAULT_STALL_RUN_PAGE, fe as DEFAULT_SUSPEND_HINT_MS, d as DEFAULT_WORKFLOW_SCHEDULE_MAX_RUNTIME_MS, t as DEFERRING_CONTROLS, ut as EVENTS_TABLE, St as FlowControlKeyError, Pe as PAUSES_TABLE, st as PENDING_TABLE, oe as WATERMARKS_TABLE, L as WorkflowFlowControlProperty, M as WorkflowMessagesProperty, s as WorkflowRunRecorder, k as WorkflowScheduleProperty, c as WorkflowStepInterceptorTag, F as WorkflowVersionTypeId, D as WorkflowWorkerLayerTypeId, We as _voltroBudgetHoldsTable, de as _voltroWorkflowAdmissionsTable, pe as _voltroWorkflowPausesTable, _e as _voltroWorkflowPendingTable, ae as _voltroWorkflowRunEventsTable, le as _voltroWorkflowRunStepsTable, be as _voltroWorkflowRunsTable, te as _voltroWorkflowStartContextsTable, Ve as _voltroWorkflowWatermarksTable, gt as admitStart, mt as allIntents, _ as assertConsistentPools, Xe as awaitEvent, ue as awaitSignal, xt as awaitSignalSuspending, He as awaitUpdate, je as budgetHoldGeneration, yt as budgetHoldKey, Tt as budgetHoldSignalName, dt as closeWorkflowChildrenForParent, we as comparePendingOrder, Ce as completeSuspendingSignal, $ as decideAdmission, o as deferringControlsOf, Re as deleteIntent, tt as discardIntent, Dt as drainTick, vt as dueIntents, b as durableClock, N as durableQueue, I as durableQueueModule, S as durableRateLimiterModule, Be as evaluateAdmission, p as getCurrentWorkflowExecutionId, U as getCurrentWorkflowRunId, A as getWorkflowFlowControl, e as getWorkflowSchedule, R as getWorkflowVersionMetadata, g as hasCancelOn, v as hasFlowControl, Fe as heldBudgets, Ot as inMemoryWorkflowEngineLayer, it as inspectWorkflow, Z as isFinishExpired, he as isStartExpired, C as isWorkflowWorkerLayer, at as linkExecution, ht as makeInMemoryRecorder, h as makeReplayShapeTracker, ot as makeWorkflowRunRecorder, X as makeWorkflowUpdateId, ge as maybeHintSuspendingSignal, ve as newestEventAt, $e as nextSlotAt, i as nondeterminismEventPayload, pt as normaliseWorkflowPatches, Ue as noteIntentAttempt, lt as parkDelayedStart, w as patch, rt as pauseWorkflow, Ct as pendingBudgetHolds, qe as pendingSlotId, Ge as pendingWindow, Me as persistDefer, W as planCancellations, K as pooledConcurrencyKey, P as processQueue, bt as pruneAdmissions, j as queueWorker, E as rateLimit, Et as readAdmissionState, Ie as readPausedWorkflows, ke as readWatermark, wt as recordAdmission, xe as releaseBudgetHolds, Se as releaseLease, r as reportNondeterminism, ee as resetSuspendHintsForTest, Ze as resolveAdmissionKeys, Le as resolveRunGuardTuning, ie as resolveSuspendHintMs, me as resolveWorkflowMessageRun, Y as resumeWorkflow, Qe as sendWorkflowSignal, G as sendWorkflowUpdate, et as serialiseWorkflowRowForWire, ce as setSuspendSignalHintMs, T as sleep, y as sleepUntil, z as step, H as stepIdempotencyKey, x as stepModule, J as suspendForBudget, Q as suspendingSignalDeferredName, Oe as sweepCancelOn, Te as sweepStalledRuns, _t as truncateWorkflowValue, a as validateFlowControl, O as validateWorkflowSchedule, V as withCompensation, m as workflow, n as workflowModule, ze as wrapWorkflowExecuteWithRunRecording, Ae as writeWatermark };
2
+ import { $ as W, A as G, At as K, B as q, Bt as J, C as Y, Ct as X, D as Z, Dt as Q, E as $, Et as ee, F as te, Ft as ne, G as re, H as ie, I as ae, It as oe, J as se, K as ce, L as le, Lt as ue, M as de, Mt as fe, N as pe, Nt as me, O as he, Ot as ge, P as _e, Pt as ve, Q as ye, R as be, Rt as xe, S as Se, St as Ce, T as we, Tt as Te, U as Ee, V as De, Vt as Oe, W as ke, X as Ae, Y as je, Z as Me, _ as Ne, _t as Pe, a as Fe, at as Ie, b as Le, bt as Re, c as ze, ct as Be, d as Ve, dt as He, et as Ue, f as We, ft as Ge, g as Ke, gt as qe, h as Je, ht as Ye, i as Xe, it as Ze, j as Qe, jt as $e, k as et, kt as tt, l as nt, lt as rt, m as it, mt as at, n as ot, nt as st, o as ct, ot as lt, p as ut, pt as dt, q as ft, r as pt, rt as mt, s as ht, st as gt, t as _t, tt as vt, u as yt, ut as bt, v as xt, vt as St, w as Ct, wt, x as Tt, xt as Et, y as Dt, yt as Ot, z as kt, zt as At } from "./src-CJ56XxpW.js";
3
+ export { mt as ADMISSIONS_TABLE, ut as BUDGET_HOLDS_TABLE, it as BudgetHoldExpired, ke as CANCEL_ON_WATERMARK, l as CurrentWorkflowExecutionId, u as CurrentWorkflowPatches, B as CurrentWorkflowReplayShape, f as CurrentWorkflowRunId, Je as DEFAULT_BUDGET_HOLD_TIMEOUT_MS, re as DEFAULT_COLD_LOOKBACK_MS, be as DEFAULT_DRAIN_BATCH, ce as DEFAULT_EVENT_BATCH, Ze as DEFAULT_LEASE_MS, Xe as DEFAULT_MAX_RUN_RECLAIMS, Fe as DEFAULT_REPLAY_SHAPE_LIMIT, q as DEFAULT_STALL_AFTER_MS, De as DEFAULT_STALL_EVENT_LOOKBACK, ie as DEFAULT_STALL_RUN_PAGE, pe as DEFAULT_SUSPEND_HINT_MS, d as DEFAULT_WORKFLOW_SCHEDULE_MAX_RUNTIME_MS, t as DEFERRING_CONTROLS, ft as EVENTS_TABLE, ee as FlowControlKeyError, Ie as PAUSES_TABLE, lt as PENDING_TABLE, se as WATERMARKS_TABLE, L as WorkflowFlowControlProperty, M as WorkflowMessagesProperty, s as WorkflowRunRecorder, k as WorkflowScheduleProperty, c as WorkflowStepInterceptorTag, F as WorkflowVersionTypeId, D as WorkflowWorkerLayerTypeId, Ke as _voltroBudgetHoldsTable, ve as _voltroWorkflowAdmissionsTable, ne as _voltroWorkflowPausesTable, oe as _voltroWorkflowPendingTable, xe as _voltroWorkflowRunEventsTable, At as _voltroWorkflowRunStepsTable, J as _voltroWorkflowRunsTable, ue as _voltroWorkflowStartContextsTable, Ue as _voltroWorkflowWatermarksTable, vt as admitStart, gt as allIntents, _ as assertConsistentPools, Qe as awaitEvent, de as awaitSignal, Ct as awaitSignalSuspending, We as awaitUpdate, Ne as budgetHoldGeneration, xt as budgetHoldKey, Dt as budgetHoldSignalName, pt as closeWorkflowChildrenForParent, Q as comparePendingOrder, we as completeSuspendingSignal, Be as deadLetterIntent, ge as decideAdmission, o as deferringControlsOf, rt as deleteIntent, bt as discardIntent, kt as drainTick, He as dueIntents, b as durableClock, N as durableQueue, I as durableQueueModule, S as durableRateLimiterModule, Ge as evaluateAdmission, p as getCurrentWorkflowExecutionId, U as getCurrentWorkflowRunId, A as getWorkflowFlowControl, e as getWorkflowSchedule, R as getWorkflowVersionMetadata, g as hasCancelOn, v as hasFlowControl, Le as heldBudgets, Oe as inMemoryWorkflowEngineLayer, ot as inspectWorkflow, tt as isFinishExpired, K as isStartExpired, C as isWorkflowWorkerLayer, st as linkExecution, _t as makeInMemoryRecorder, h as makeReplayShapeTracker, ct as makeWorkflowRunRecorder, Z as makeWorkflowUpdateId, _e as maybeHintSuspendingSignal, ye as newestEventAt, $e as nextSlotAt, i as nondeterminismEventPayload, ht as normaliseWorkflowPatches, dt as noteIntentAttempt, at as parkDelayedStart, w as patch, Ye as pauseWorkflow, Tt as pendingBudgetHolds, qe as pendingSlotId, Pe as pendingWindow, St as persistDefer, W as planCancellations, fe as pooledConcurrencyKey, P as processQueue, Ot as pruneAdmissions, j as queueWorker, E as rateLimit, Re as readAdmissionState, Et as readPausedWorkflows, je as readWatermark, Ce as recordAdmission, Se as releaseBudgetHolds, X as releaseLease, r as reportNondeterminism, te as resetSuspendHintsForTest, me as resolveAdmissionKeys, ze as resolveRunGuardTuning, ae as resolveSuspendHintMs, he as resolveWorkflowMessageRun, wt as resumeWorkflow, Te as retryDeadLetterIntent, et as sendWorkflowSignal, G as sendWorkflowUpdate, nt as serialiseWorkflowRowForWire, le as setSuspendSignalHintMs, T as sleep, y as sleepUntil, z as step, H as stepIdempotencyKey, x as stepModule, Y as suspendForBudget, $ as suspendingSignalDeferredName, Ae as sweepCancelOn, Ee as sweepStalledRuns, yt as truncateWorkflowValue, a as validateFlowControl, O as validateWorkflowSchedule, V as withCompensation, m as workflow, n as workflowModule, Ve as wrapWorkflowExecuteWithRunRecording, Me as writeWatermark };