@voltro/protocol 0.40.0 → 0.41.0

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,80 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.41.0] — 2026-08-17
43
+
44
+ ### ⚠ BREAKING
45
+
46
+ - **@voltro/web, @voltro/cli** — `middleware.ts` exports `defineMiddleware(...)` (from `@voltro/web/middleware`) instead of a bare function, and each export carries its own `match`. Several middlewares per file are allowed; **at most one may match a given route**.
47
+
48
+ Migration: the codemod wraps the existing default export. That is behaviour-preserving — no `match` means every server-rendered route, which is what an unwrapped middleware did — and its note explains how to replace a hand-written path gate with a `match`.
49
+
50
+ **Why it was worth a break.** A hand-written `if (!req.pathname.startsWith('/app')) return` is invisible: nothing can tell you a middleware runs nowhere, or that two of them claim one route. `match` puts it where both the boot and `voltro doctor` can read it.
51
+
52
+ **The matcher speaks ROUTES, not URL patterns** — `under` / `routes` / `except`, validated against the app's own route patterns. A path matching no route refuses the boot instead of silently never firing. This is the deliberate difference from the `'/((?!api|_next/static|…).*)'` shape: our hook runs after route matching, so an app has never needed to know its own asset layout, and non-page requests are reachable only by asking (`assets: true`) — where, note, there is no render, so only `setCookies` takes effect.
53
+
54
+ An overlap refuses the boot and names both middlewares plus the route. Declaration order is not a semantic, "most specific wins" silently drops the broader hook, and merging needs a per-field rule nobody remembers — so two hooks writing one `authorization` header is a refusal, not a resolution.
55
+
56
+ **The web bundle budget moved UP, and the split is worth stating** because only one half is a cost the framework imposes:
57
+
58
+ | measured | before | after | | --- | --- | --- | | first load | 184 955 B | 185 309 B (**+354**) | | lazy route chunks | 3 502 B | 4 415 B (+913) |
59
+
60
+ The **+354 B of first load is the real price** — one `serverContext` chunk, 0.2 KB gz, which every app now carries whether or not it declares a middleware. That is the number to argue with, and it leaves 6.7 KB of headroom under the ceiling.
61
+
62
+ The +913 B is NOT a per-route regression: the fixture gained four routes (`exact`, `exact/[id]`, `mw`, `mw/skip`) to exercise the feature end to end, at 0.1–0.2 KB gz each, which accounts for the growth without remainder. Re-pinned with `--update` rather than by hand, so the `slackFloor` keeps ratcheting — a ceiling nobody lowers again silently permits re-inflating to the old number.
63
+
64
+ ### Fixed
65
+
66
+ - **@voltro/data-transfer, @voltro/cli** — `voltro data export` could not export a table whose primary key is not named `id`, and one of its two failure modes reported success.
67
+
68
+ The keyset column was `columns.find(c => c.type === 'id')?.name ?? 'id'`, and `type: 'id'` is tagged only on a column that is BOTH the single-column primary key AND literally named `id` — identically in all four dialect introspectors. So any introspected table with another PK name was ordered by a column that does not exist. It now comes from the real primary key (the synthesised `<table>_pkey` index), with the declared `id()` column still winning where there is one.
69
+
70
+ **A composite or absent primary key is now REFUSED**, not silently ordered by the first column: keyset pagination on a non-unique order splits equal values across page boundaries, so rows are dropped or duplicated into a bundle that reports success. Bounded exports are recoverable; a quietly short backup is discovered at the restore.
71
+
72
+ **A requested table missing from the schema is refused too.** `scope: { kind: 'tables' }` used to drop unknown names, so a run that explicitly named a table wrote `"tables": []` and printed `export complete` with exit 0. `kind: 'all'` over an empty database is still a legal empty export — the asymmetry is deliberate: a named table is an expectation.
73
+
74
+ **Failure reasons survive.** `String(e?.message ?? e)` produced `"write table failed: "` with nothing after the colon — `??` falls back on null/undefined, and an Effect `TaggedError` carries an empty-string `message`. Every catch site in the exporter now reports tag, message or cause.
75
+
76
+ **New: `voltro data export --exclude a,b`** — everything except these, resolved against the live table list. It is the escape hatch the refusals above require; without it a single unkeyable table would block a whole-database export. An unknown name is refused for the same reason. Direct target only (the expansion needs the live table list), and it expands to an explicit `tables` scope, so the manifest records what was actually exported.
77
+
78
+ Reported with a reduced repro, a four-way variation over PK TYPES that ruled type out, and two disproved hypotheses. The affected tables include `@effect/cluster`'s own (`cluster_locks`, `cluster_migrations`), so no app running workflows could take a whole-database export.
79
+ - **@voltro/database, @voltro/runtime, @voltro/cli** — `.encrypted()` had three writers and two encodings. The store wrote `encrypt(JSON.stringify(v))`; `encryptField` — the documented raw-SQL escape hatch — and `voltro db encrypt-column` wrote `encrypt(v)`. All three produce the same `enc:v1:` envelope and nothing distinguished them, so a value written by one and read by another either threw with the wrong diagnosis or came back subtly wrong (`decryptField` handed back the JSON encoding verbatim, quotes and all, raising nothing).
80
+
81
+ There is one encoding for every WRITE now, and every READ resolves BOTH forms — so **no data has to be rewritten and nothing is blocked**. That second half is the point: the old form is already on staging and production disks, and a fix that needs the rows rewritten before the app works is an outage with a migration attached.
82
+
83
+ Reading two forms is deterministic, not a heuristic. After decrypting, a parse failure is the raw form; a parse to a STRING is the JSON form; a parse to a non-string depends on the column's declared type (a text column cannot hold a number, so `12345` is a raw string that parsed by accident). The one case nothing can separate — a raw secret whose literal text is `"abc"`, quotes included — is stated in the code rather than hidden.
84
+
85
+ **`voltro db encrypt-column` verified itself against the wrong decoder.** It wrote the raw form and checked it with `cipher.decrypt` — a decoder nothing reads these columns with — so it reported success over columns the app could not read. It round-trips through `decodeFieldValue` now, the same function the store calls. A self-check against a decoder the runtime does not use is not a weaker check; it is a second opinion from the same mistake.
86
+
87
+ The command also NORMALISES rows in the old encoding as it goes (reported separately from the ones it encrypts), so an operator does not write a script per column. It skips anything ambiguous and anything it cannot decrypt.
88
+
89
+ **The width pre-flight measured the wrong thing after the encoding changed.** It sized the ciphertext from the PLAINTEXT's byte length while the cipher is handed the JSON encoding — two characters more at minimum, and more for every escape. Measured on a real MariaDB: a 63-byte value in a `varchar(135)` passed the check and the UPDATE answered `ER_DATA_TOO_LONG`, which is the failure that check exists to prevent, mid-column with the rest already converted. It measures the encoded length now, and the refusal says "encodes to" rather than "is" so an operator measuring their own column finds the number it names.
90
+
91
+ **Two dialect defects, both found by running the command against real servers.** SQL Server reports `-1` for `NVARCHAR(MAX)` — its spelling of unbounded — and the pre-flight read it as a one-character column, so it refused the widest column the dialect has and printed `declared as -1` at the operator. And SQLITE has no `information_schema` at all: the shared catalog query died there with `Failed to prepare statement` and no statement attached, on a dialect the command claims to support. It uses `pragma_table_info` now, reporting no length because sqlite enforces none.
92
+
93
+ Measured end to end on postgres, mysql, mariadb, mssql and sqlite: a table holding plaintext, the old encoding and the current encoding side by side converts, every row decodes back to its original value, a re-run writes nothing, and a wrong key refuses with exit 1.
94
+
95
+ **Backups and restores were never affected and now say so.** `voltro data export` reads through the raw dialect store, so ciphertext travels verbatim in either encoding — pinned by a test, because a future change that wrapped that store would put plaintext credentials in a bundle.
96
+ - **@voltro/protocol, @voltro/cli** — Three findings from one consumer round, all of the same shape: something the framework knows and does not say.
97
+
98
+ **A decode failure on a GUARDED procedure now says the guard did not run.** The payload decodes before the handler, so a guard on a procedure with a malformed payload never gets the chance to refuse. A consumer auditing a guard called one with an incomplete payload, got a decode error instead of a `ScopeError`, and concluded the guard was not applied — the wrong conclusion in the dangerous direction. The title now carries `(guarded — the guard did NOT run: the payload failed to decode first, so this says nothing about access)`. It discloses nothing new: that a procedure is guarded is already visible to anyone who sends a VALID payload. An `openAccess:` declaration is not an enforced guard and gets no such sentence — `hasEnforcedGuard` is the one predicate, read by both the label and the wire error union, because two copies of that rule would disagree invisibly.
99
+
100
+ **`middleware.ts`'s `httpOnly` default is documented at the field, and warned about.** It defaults to `HttpOnly`, which is wrong for a session cookie a browser SDK reads back: Supabase's `createBrowserClient` reads `document.cookie`, so a forgotten `httpOnly: false` gives the browser a session it cannot see — the SSR render is perfect and the user is signed out at the first client-side call. The consumer only avoided shipping it because their probes already set the flag. `voltro dev` warns once per cookie when a session-shaped name is written with no `httpOnly` decision; an explicit decision either way silences it, because warning on a decision is how a diagnostic becomes noise.
101
+
102
+ **`voltro dev` restarts when `middleware.ts` changes.** It is loaded once per boot, that is documented, and a consumer read it and still lost an afternoon: they sabotaged the middleware, saw no change, and concluded it was not wired — in an environment where everything else hot-reloads. It now restarts through the same respawn a hard-restart field in `app.config.ts` uses, extracted so there is one copy of the `execArgv` inheritance and the signal forwarding.
103
+ - **@voltro/cli** — `middleware.ts` now produces ONE view of the request that every downstream reader takes. Previously only `buildLoaderQuery` saw the hook's result, while the loader context (`ctx.headers`), the SSR request snapshot (`useServerRequest()`) and the locale resolver kept reading the raw request — four readers, two answers, within eighty lines of one function.
104
+
105
+ The consequence was worse than an inconsistency: a hook that renews purely via `setCookies` — no `headers` at all, which is the normal shape for a cookie-session IdP and the reason the response half exists — moved nothing for the render that ran it. The rpc call still sent the old `Cookie` header, because a renewed cookie only reached the browser.
106
+
107
+ `setCookies` is applied to the cookie jar before the render, the `Cookie` header is rebuilt from that jar (an explicit `cookie` in the hook's own `headers` still wins), and `maxAge <= 0` deletes, so a hook that signs someone out renders them signed out. Both SSR boot paths shadow the raw headers out of scope after the hook runs, so a new reader added below is correct without knowing any of this.
108
+ - **@voltro/cli** — `voltro start` dropped `middleware.ts`'s `Set-Cookie` on **streamed** responses — which is the arm a plain `renderMode: 'ssr'` page takes, so it was the common case. The hook renewed the session server-side, the render used the fresh value, and the browser kept the consumed one. Against an IdP that rotates refresh tokens and detects reuse, that is worse than not renewing at all.
109
+
110
+ The cause is worth stating because it read as handled: a streamed response hands the socket to `stream(res)` and the caller never looks at the returned `headers`, so the `withCookies(...)` wrapper on that arm was dead code — sitting under a comment promising the cookies were written on every arm. The cookies now travel with the headers `streamSsrResponse` itself writes, and the dead wrapper is gone.
111
+
112
+ Found by booting real `voltro dev` and `voltro start` servers against a fixture and reading the response. Every unit test was green throughout, and the render's own HTML was correct — only the wire was wrong.
113
+
114
+ ---
115
+
42
116
  ## [0.40.0] — 2026-08-16
43
117
 
44
118
  ### ⚠ BREAKING
package/dist/index.d.ts CHANGED
@@ -2052,6 +2052,22 @@ export declare const hasCallbackRoutes: (s: AuthStrategy) => s is AuthStrategyWi
2052
2052
  * `hasScope` anywhere rbac roles should count. */
2053
2053
  export declare const hasEffectiveScope: (subject: Subject, scope: string) => boolean;
2054
2054
 
2055
+ /**
2056
+ * Does this procedure carry a guard that can actually REFUSE?
2057
+ *
2058
+ * A declared `openAccess:` is an access decision, not a check — nothing can fail
2059
+ * with a `ScopeError`, so it neither widens the wire union nor makes a decode
2060
+ * failure worth annotating.
2061
+ *
2062
+ * One function because it now has two readers, and this repo's per-seam scar is
2063
+ * exactly a predicate that got copied: `withGuardError` decides the wire error
2064
+ * union, and `strictInput`'s label decides whether a decode failure says the
2065
+ * guard did not run. Two copies would eventually disagree about `openAccess:`,
2066
+ * and the disagreement would surface as a procedure that says "guarded" while
2067
+ * declaring no `ScopeError` — or the reverse.
2068
+ */
2069
+ export declare const hasEnforcedGuard: (guards: DeclaredAccess | undefined) => boolean;
2070
+
2055
2071
  /** True if the subject holds `scope` (or the `admin:full` bypass). Checks only
2056
2072
  * the RAW subject scopes — use `hasEffectiveScope` to include rbac roles. */
2057
2073
  export declare const hasScope: (subject: Subject, scope: string) => boolean;
@@ -2186,6 +2202,33 @@ export declare const idToPath: (id: RowId) => string;
2186
2202
  */
2187
2203
  export declare const IMPERSONATION_METADATA_KEY: "impersonation";
2188
2204
 
2205
+ /**
2206
+ * The title a decode failure carries — and, on a GUARDED procedure, the one
2207
+ * sentence that stops the failure being read as "the guard did not fire".
2208
+ *
2209
+ * ── Why this sentence exists ───────────────────────────────────────────────
2210
+ *
2211
+ * The ordering above is not fixable at this seam: the payload decodes before
2212
+ * the handler, so a guard on a procedure with a malformed payload never runs. A
2213
+ * consumer checking a guard on 2026-08-16 called such a procedure with an
2214
+ * incomplete payload, got a decode error instead of a `ScopeError`, and
2215
+ * concluded the guard was not being applied. It was; it never got the chance.
2216
+ * With a complete payload the picture flipped immediately.
2217
+ *
2218
+ * That is the more dangerous of the two misreadings — it says "unprotected"
2219
+ * about something protected — and it is the one the ordering makes easy. So a
2220
+ * guarded procedure says so in the title:
2221
+ *
2222
+ * workAreas.create input (guarded — the guard did NOT run: the payload
2223
+ * failed to decode first, so this says nothing about access)
2224
+ * └─ ["type"] └─ is missing
2225
+ *
2226
+ * It discloses nothing new. That a procedure is guarded is already visible to
2227
+ * any caller who sends a VALID payload and receives `ScopeError`, and the
2228
+ * message names no scope, no resource and no field type.
2229
+ */
2230
+ export declare const inputLabel: (procedure?: string, guarded?: boolean) => string;
2231
+
2189
2232
  export declare interface InsertTarget<Input = unknown, Row = unknown, Item = Record<string, unknown>> extends NestedTargetFields<Input> {
2190
2233
  readonly table: string;
2191
2234
  readonly op: 'insert';
@@ -4085,7 +4128,7 @@ export declare const streamToRpc: <Name extends string, Input extends Schema.Sch
4085
4128
  * non-struct payload (`Schema.Void`, a scalar) is unaffected — there is no
4086
4129
  * excess property for it to have.
4087
4130
  */
4088
- export declare const strictInput: <S extends Schema.Schema.Any>(input: S, procedure?: string) => S;
4131
+ export declare const strictInput: <S extends Schema.Schema.Any>(input: S, procedure?: string, guarded?: boolean) => S;
4089
4132
 
4090
4133
  export declare const Subject: Schema.Union<[Schema.Struct<{
4091
4134
  type: Schema.Literal<["user"]>;
package/dist/index.js CHANGED
@@ -76,16 +76,19 @@ var We = d.Union(d.String, d.Number), p = d.Record({
76
76
  _tag: d.Literal("error"),
77
77
  error: d.Unknown,
78
78
  revision: d.optional(d.Number)
79
- })), Xe = (e) => {
79
+ })), y = (e, t) => {
80
+ let n = `${e ?? "this procedure"} input`;
81
+ return t === !0 ? `${n} (guarded — the guard did NOT run: the payload failed to decode first, so this says nothing about access)` : n;
82
+ }, Xe = (e) => {
80
83
  let t = e.ast;
81
84
  return t._tag === "TypeLiteral" && (t.propertySignatures?.length ?? 0) === 0 && (t.indexSignatures?.length ?? 0) === 0;
82
- }, y = (e, t) => {
83
- let n = e.annotations({
85
+ }, b = (e, t, n) => {
86
+ let r = y(t, n), i = e.annotations({
84
87
  parseOptions: { onExcessProperty: "error" },
85
- parseIssueTitle: () => `${t ?? "this procedure"} input`
88
+ parseIssueTitle: () => r
86
89
  });
87
- return Xe(e) ? d.filter((e) => typeof e == "object" && e && Object.keys(e).length > 0 ? `this procedure declares no input; received: ${Object.keys(e).join(", ")}` : void 0)(n).annotations({ identifier: `${t ?? "this procedure"} input` }) : n;
88
- }, b = class extends d.TaggedError()("BusinessRuleViolation", {
90
+ return Xe(e) ? d.filter((e) => typeof e == "object" && e && Object.keys(e).length > 0 ? `this procedure declares no input; received: ${Object.keys(e).join(", ")}` : void 0)(i).annotations({ identifier: r }) : i;
91
+ }, x = class extends d.TaggedError()("BusinessRuleViolation", {
89
92
  rule: d.String,
90
93
  params: d.optional(d.Record({
91
94
  key: d.String,
@@ -94,34 +97,34 @@ var We = d.Union(d.String, d.Number), p = d.Record({
94
97
  field: d.optional(d.String),
95
98
  message: d.optional(d.String),
96
99
  severity: d.Literal("error", "warning")
97
- }) {}, x = class extends d.TaggedError()("ApprovalRequired", {
100
+ }) {}, S = class extends d.TaggedError()("ApprovalRequired", {
98
101
  approvalId: d.String,
99
102
  procedure: d.String,
100
103
  expiresAt: d.String,
101
104
  requiredScopes: d.Array(d.String),
102
105
  reason: d.NullOr(d.String),
103
106
  created: d.Boolean
104
- }) {}, S = class extends d.TaggedError()("ApprovalNotFound", { approvalId: d.String }) {}, C = class extends d.TaggedError()("ApprovalSelfApproval", {
107
+ }) {}, C = class extends d.TaggedError()("ApprovalNotFound", { approvalId: d.String }) {}, w = class extends d.TaggedError()("ApprovalSelfApproval", {
105
108
  approvalId: d.String,
106
109
  subjectId: d.NullOr(d.String)
107
- }) {}, w = class extends d.TaggedError()("ApprovalRejected", {
110
+ }) {}, T = class extends d.TaggedError()("ApprovalRejected", {
108
111
  approvalId: d.String,
109
112
  decidedBy: d.NullOr(d.String),
110
113
  note: d.NullOr(d.String)
111
- }) {}, T = class extends d.TaggedError()("ApprovalExpired", {
114
+ }) {}, E = class extends d.TaggedError()("ApprovalExpired", {
112
115
  approvalId: d.String,
113
116
  expiredAt: d.String
114
- }) {}, E = class extends d.TaggedError()("ApprovalNotPending", {
117
+ }) {}, D = class extends d.TaggedError()("ApprovalNotPending", {
115
118
  approvalId: d.String,
116
119
  status: d.String
117
- }) {}, D = class extends d.TaggedError()("ApprovalForbidden", {
120
+ }) {}, O = class extends d.TaggedError()("ApprovalForbidden", {
118
121
  approvalId: d.String,
119
122
  required: d.NullOr(d.String),
120
123
  message: d.String
121
- }) {}, O = class extends d.TaggedError()("ApprovalUnavailable", {
124
+ }) {}, k = class extends d.TaggedError()("ApprovalUnavailable", {
122
125
  procedure: d.String,
123
126
  message: d.String
124
- }) {}, k = d.Union(x, w, T, O), A = d.Union(S, C, T, E, D, O), j = d.Struct({
127
+ }) {}, A = d.Union(S, T, E, k), j = d.Union(C, w, E, D, O, k), M = d.Struct({
125
128
  id: d.String,
126
129
  procedure: d.String,
127
130
  kind: d.Literal("mutation", "action"),
@@ -135,28 +138,28 @@ var We = d.Union(d.String, d.Number), p = d.Record({
135
138
  decidedAt: d.NullOr(d.String),
136
139
  note: d.NullOr(d.String),
137
140
  relation: d.Literal("to-decide", "requested")
138
- }), M = "channel:", N = Symbol.for("@voltro/protocol/reactivityChannels"), P = globalThis, F = P[N] ?? (P[N] = /* @__PURE__ */ new Map()), Ze = /^[a-z][a-z0-9-]*(\.[a-z0-9-]+)*$/, Qe = (e) => {
139
- if (!Ze.test(e)) throw Error(`reactivityChannel('${e}'): invalid channel name.\n Use lowercase kebab segments separated by dots — \`presence\`, \`job-queue\`,
141
+ }), N = "channel:", Ze = Symbol.for("@voltro/protocol/reactivityChannels"), Qe = globalThis, P = Qe[Ze] ?? (Qe[Ze] = /* @__PURE__ */ new Map()), $e = /^[a-z][a-z0-9-]*(\.[a-z0-9-]+)*$/, et = (e) => {
142
+ if (!$e.test(e)) throw Error(`reactivityChannel('${e}'): invalid channel name.\n Use lowercase kebab segments separated by dots — \`presence\`, \`job-queue\`,
140
143
  \`billing.usage\`. A \`:\` is refused because it is the namespace separator in
141
- the routing key (\`${M}<name>\`), and an uppercase letter is\n refused because a key that differs only by case reads as one channel and
144
+ the routing key (\`${N}<name>\`), and an uppercase letter is\n refused because a key that differs only by case reads as one channel and
142
145
  routes as two.`);
143
- let t = F.get(e);
146
+ let t = P.get(e);
144
147
  if (t !== void 0) return t;
145
- let n = `${M}${e}`, r = Object.freeze({
148
+ let n = `${N}${e}`, r = Object.freeze({
146
149
  kind: "reactivity-channel",
147
150
  name: e,
148
151
  key: n,
149
152
  toString: () => n
150
153
  });
151
- return F.set(e, r), r;
152
- }, I = (e) => typeof e == "object" && !!e && e.kind === "reactivity-channel", $e = (e) => e.startsWith(M), et = () => new Set([...F.values()].map((e) => e.key)), L = (e) => I(e) ? e.key : e, tt = (e) => e === void 0 ? [] : Array.isArray(e) ? e.map(L) : [L(e)], nt = (e) => e === void 0 ? void 0 : Array.isArray(e) ? e.map(L) : L(e), rt = (e) => {
153
- let t = et(), n = [];
154
- for (let r of e) for (let e of tt(r.source)) !$e(e) || t.has(e) || n.push({
154
+ return P.set(e, r), r;
155
+ }, tt = (e) => typeof e == "object" && !!e && e.kind === "reactivity-channel", nt = (e) => e.startsWith(N), rt = () => new Set([...P.values()].map((e) => e.key)), F = (e) => tt(e) ? e.key : e, it = (e) => e === void 0 ? [] : Array.isArray(e) ? e.map(F) : [F(e)], at = (e) => e === void 0 ? void 0 : Array.isArray(e) ? e.map(F) : F(e), ot = (e) => {
156
+ let t = rt(), n = [];
157
+ for (let r of e) for (let e of it(r.source)) !nt(e) || t.has(e) || n.push({
155
158
  procedure: r.name,
156
159
  key: e
157
160
  });
158
161
  return n;
159
- }, it = (e, t) => {
162
+ }, st = (e, t) => {
160
163
  let n = e?.injectExternalChange;
161
164
  return n !== void 0 && (n.call(e, {
162
165
  table: t.key,
@@ -165,8 +168,8 @@ var We = d.Union(d.String, d.Number), p = d.Record({
165
168
  old: {},
166
169
  origin: "inline"
167
170
  }), !0);
168
- }, at = (e) => e.internal !== !0, R = (e) => {
169
- if (ot(e), Array.isArray(e.guards) && e.guards.length === 0) throw Error(`${e.name}: \`guards: []\` is empty, so it enforces nothing — but it reads\n at the call site as if this procedure were protected. Omit the field for an
171
+ }, ct = (e) => e.internal !== !0, I = (e) => {
172
+ if (lt(e), Array.isArray(e.guards) && e.guards.length === 0) throw Error(`${e.name}: \`guards: []\` is empty, so it enforces nothing — but it reads\n at the call site as if this procedure were protected. Omit the field for an
170
173
  unguarded procedure, or list the scopes required to call it.`);
171
174
  let t = typeof e.source == "string" ? [e.source] : Array.isArray(e.source) ? e.source : void 0;
172
175
  if (t !== void 0 && (t.length === 0 || t.some((e) => String(e).trim() === ""))) throw Error(`${e.name}: \`source\` is empty, so this query declares reactivity and\n subscribes to nothing — it serves one snapshot and never updates again,
@@ -183,7 +186,7 @@ var We = d.Union(d.String, d.Number), p = d.Record({
183
186
  let n = [e.publicApi === void 0 ? void 0 : "publicApi", e.exposeAsTool === void 0 ? void 0 : "exposeAsTool"].filter((e) => e !== void 0);
184
187
  if (n.length === 0) return e;
185
188
  throw Error(`${e.name}: \`internal: true\` cannot be combined with ${n.map((e) => `\`${e}\``).join(" or ")}. \`internal\` takes the procedure OFF the wire; those put it back ON a different one (${n.includes("publicApi") ? "a REST route" : "an agent tool"}), and that surface is projected without consulting the flag — so the procedure would be unreachable from your client and reachable from the internet. Drop \`internal: true\` if the wider surface is intended, or remove the ${n.join(" / ")} annotation if it is not.`);
186
- }, ot = (e) => {
189
+ }, lt = (e) => {
187
190
  let t = e.requiresApproval;
188
191
  if (t !== void 0) {
189
192
  if (!Array.isArray(t.approvers) || t.approvers.length === 0) throw Error(`${e.name}: \`requiresApproval.approvers\` is empty, so NOBODY can approve this\n procedure and every call would park forever. Name the scope(s) an approver must hold
@@ -193,48 +196,48 @@ var We = d.Union(d.String, d.Number), p = d.Record({
193
196
  human from every other, and the second-pair-of-eyes control would enforce nothing.
194
197
  Give the procedure a \`guards:\` decision, or drop \`requiresApproval\`.`);
195
198
  }
196
- }, st = (e) => e.action !== void 0 && e.resourceType !== void 0, ct = (e) => e.openAccess !== void 0 || e.guards !== void 0 && e.guards.length > 0, z = (e, t, n) => {
199
+ }, ut = (e) => e.action !== void 0 && e.resourceType !== void 0, dt = (e) => e.openAccess !== void 0 || e.guards !== void 0 && e.guards.length > 0, L = (e, t, n) => {
197
200
  if (n === void 0) return t;
198
201
  if (n.trim() === "") throw Error(`${e}: \`openAccess\` needs a REASON, not an empty string. It is the sentence a\n reviewer reads to decide whether this procedure should really be callable
199
202
  without an authorization check — e.g. \`openAccess: 'public pricing, no caller data'\`.`);
200
203
  if (t !== void 0 && t.length > 0) throw Error(`${e}: \`openAccess\` and \`guards\` are two different access decisions, so\n declaring both says the procedure is protected AND open. Keep the guards if a
201
204
  caller must hold a scope; drop them if anyone may call it.`);
202
205
  return [u(n.trim())];
203
- }, B = (e) => R({
206
+ }, R = (e) => I({
204
207
  kind: "query",
205
208
  name: e.name,
206
209
  input: e.input,
207
210
  output: e.output,
208
211
  error: e.error ?? d.Never,
209
- source: nt(e.source),
212
+ source: at(e.source),
210
213
  cache: e.cache,
211
- guards: z(e.name, e.guards, e.openAccess),
214
+ guards: L(e.name, e.guards, e.openAccess),
212
215
  openAccess: e.openAccess,
213
216
  publicApi: e.publicApi,
214
217
  exposeAsTool: e.exposeAsTool,
215
218
  internal: e.internal,
216
219
  overridesPlugin: e.overridesPlugin
217
- }), V = (e) => R({
220
+ }), z = (e) => I({
218
221
  kind: "mutation",
219
222
  name: e.name,
220
223
  input: e.input,
221
224
  output: e.output,
222
225
  error: e.error ?? d.Never,
223
226
  target: e.target,
224
- guards: z(e.name, e.guards, e.openAccess),
227
+ guards: L(e.name, e.guards, e.openAccess),
225
228
  openAccess: e.openAccess,
226
229
  publicApi: e.publicApi,
227
230
  exposeAsTool: e.exposeAsTool,
228
231
  requiresApproval: e.requiresApproval,
229
232
  internal: e.internal,
230
233
  overridesPlugin: e.overridesPlugin
231
- }), H = (e) => R({
234
+ }), B = (e) => I({
232
235
  kind: "action",
233
236
  name: e.name,
234
237
  input: e.input,
235
238
  output: e.output,
236
239
  error: e.error ?? d.Never,
237
- guards: z(e.name, e.guards, e.openAccess),
240
+ guards: L(e.name, e.guards, e.openAccess),
238
241
  openAccess: e.openAccess,
239
242
  source: e.source,
240
243
  target: e.target,
@@ -243,7 +246,7 @@ var We = d.Union(d.String, d.Number), p = d.Record({
243
246
  requiresApproval: e.requiresApproval,
244
247
  internal: e.internal,
245
248
  overridesPlugin: e.overridesPlugin
246
- }), lt = (e) => R({
249
+ }), ft = (e) => I({
247
250
  kind: "stream",
248
251
  name: e.name,
249
252
  input: e.input,
@@ -251,42 +254,42 @@ var We = d.Union(d.String, d.Number), p = d.Record({
251
254
  error: e.error ?? d.Never,
252
255
  internal: e.internal,
253
256
  overridesPlugin: e.overridesPlugin,
254
- guards: z(e.name, e.guards, e.openAccess),
257
+ guards: L(e.name, e.guards, e.openAccess),
255
258
  openAccess: e.openAccess
256
- }), U = (e, t) => t && t.length > 0 ? d.Union(e, ...t) : e, ut = (e, t) => t && t.some((e) => !l(e)) ? d.Union(e, c, s) : e, dt = (e) => d.Union(e, b), ft = (e, t) => t === void 0 ? e : d.Union(e, k), W = (e, t) => {
259
+ }), V = (e, t) => t && t.length > 0 ? d.Union(e, ...t) : e, H = (e) => e !== void 0 && e.some((e) => !l(e)), pt = (e, t) => H(t) ? d.Union(e, c, s) : e, mt = (e) => d.Union(e, x), ht = (e, t) => t === void 0 ? e : d.Union(e, A), U = (e, t) => {
257
260
  if (t === "stream") return e.error;
258
- let n = ut(e.error, e.guards);
259
- return t === "query" ? n : ft(t === "mutation" ? dt(n) : n, e.requiresApproval);
260
- }, pt = (e, t) => f.make(e.name, {
261
- payload: y(e.input, e.name),
261
+ let n = pt(e.error, e.guards);
262
+ return t === "query" ? n : ht(t === "mutation" ? mt(n) : n, e.requiresApproval);
263
+ }, gt = (e, t) => f.make(e.name, {
264
+ payload: b(e.input, e.name, H(e.guards)),
262
265
  success: v(e.output),
263
- error: U(W(e, "query"), t),
266
+ error: V(U(e, "query"), t),
264
267
  stream: !0
265
- }), mt = (e, t) => f.make(e.name, {
266
- payload: y(e.input, e.name),
268
+ }), _t = (e, t) => f.make(e.name, {
269
+ payload: b(e.input, e.name, H(e.guards)),
267
270
  success: e.output,
268
- error: U(W(e, "mutation"), t)
269
- }), ht = (e, t) => f.make(e.name, {
270
- payload: y(e.input, e.name),
271
+ error: V(U(e, "mutation"), t)
272
+ }), vt = (e, t) => f.make(e.name, {
273
+ payload: b(e.input, e.name, H(e.guards)),
271
274
  success: e.output,
272
- error: U(W(e, "action"), t)
273
- }), gt = (e, t) => f.make(e.name, {
274
- payload: y(e.input, e.name),
275
+ error: V(U(e, "action"), t)
276
+ }), yt = (e, t) => f.make(e.name, {
277
+ payload: b(e.input, e.name, H(e.guards)),
275
278
  success: e.element,
276
- error: U(e.error, t),
279
+ error: V(e.error, t),
277
280
  stream: !0
278
- }), _t = (e) => {
281
+ }), bt = (e) => {
279
282
  if (typeof e != "object" || !e) return;
280
283
  let t = e._tag;
281
284
  return typeof t == "string" ? t : void 0;
282
- }, vt = (e) => {
285
+ }, xt = (e) => {
283
286
  switch (e.kind) {
284
- case "query": return pt(e);
285
- case "mutation": return mt(e);
286
- case "action": return ht(e);
287
- case "stream": return gt(e);
287
+ case "query": return gt(e);
288
+ case "mutation": return _t(e);
289
+ case "action": return vt(e);
290
+ case "stream": return yt(e);
288
291
  }
289
- }, yt = (e) => {
292
+ }, St = (e) => {
290
293
  let t = e.input, n = t === void 0 ? {} : { input: t }, r = e.output, i = r === void 0 ? {} : { output: r };
291
294
  if (e.kind === "query") return {
292
295
  kind: "query",
@@ -327,7 +330,7 @@ var We = d.Union(d.String, d.Number), p = d.Record({
327
330
  ..."shapeItem" in e && e.shapeItem !== void 0 ? { shapeItem: e.shapeItem } : {}
328
331
  }))
329
332
  };
330
- }, bt = 7500, xt = (e) => typeof e == "object" && !!e && e.kind === "event", St = (e) => {
333
+ }, Ct = 7500, wt = (e) => typeof e == "object" && !!e && e.kind === "event", Tt = (e) => {
331
334
  if (e.name.length === 0) throw Error("defineEvent: `name` must not be empty");
332
335
  if (/\s/.test(e.name)) throw Error(`defineEvent("${e.name}"): \`name\` must not contain whitespace.\n The name is used as a broker subject segment. NATS refuses a subject with
333
336
  whitespace and delivers nothing — silently, and only on that broker, so an
@@ -362,69 +365,69 @@ var We = d.Union(d.String, d.Number), p = d.Record({
362
365
  webhook: e.webhook,
363
366
  delivery: e.delivery
364
367
  };
365
- }, Ct = (e) => d.Struct({
368
+ }, Et = (e) => d.Struct({
366
369
  _tag: d.Literal("event"),
367
370
  origin: d.String,
368
371
  n: d.Number,
369
372
  emittedAt: d.Number,
370
373
  payload: e
371
- }), wt = d.Struct({
374
+ }), Dt = d.Struct({
372
375
  _tag: d.Literal("gap"),
373
376
  missed: d.Number,
374
377
  reason: d.Literal("buffer", "resume")
375
- }), Tt = d.Struct({ _tag: d.Literal("attached") }), Et = (e) => d.Union(Tt, Ct(e), wt), Dt = d.Struct({
378
+ }), Ot = d.Struct({ _tag: d.Literal("attached") }), kt = (e) => d.Union(Ot, Et(e), Dt), At = d.Struct({
376
379
  origin: d.String,
377
380
  n: d.Number
378
- }), Ot = (e) => d.Struct({
381
+ }), jt = (e) => d.Struct({
379
382
  key: e,
380
- resume: d.optional(d.Array(Dt))
381
- }), kt = (e, t) => {
383
+ resume: d.optional(d.Array(At))
384
+ }), Mt = (e, t) => {
382
385
  let n = e.guards !== void 0 && e.guards.some((e) => !l(e)) ? d.Union(c, s) : d.Never, r = t && t.length > 0 ? d.Union(n, ...t) : n;
383
386
  return f.make(e.name, {
384
- payload: y(Ot(e.key), e.name),
385
- success: Et(e.payload),
387
+ payload: b(jt(e.key), e.name, H(e.guards)),
388
+ success: kt(e.payload),
386
389
  error: r,
387
390
  stream: !0
388
391
  });
389
- }, At = class extends d.TaggedError()("EventPayloadInvalid", {
392
+ }, Nt = class extends d.TaggedError()("EventPayloadInvalid", {
390
393
  event: d.String,
391
394
  message: d.String
392
- }) {}, jt = class extends d.TaggedError()("EventKeyInvalid", {
395
+ }) {}, Pt = class extends d.TaggedError()("EventKeyInvalid", {
393
396
  event: d.String,
394
397
  message: d.String
395
- }) {}, Mt = class extends d.TaggedError()("EventPayloadTooLarge", {
398
+ }) {}, Ft = class extends d.TaggedError()("EventPayloadTooLarge", {
396
399
  event: d.String,
397
400
  bytes: d.Number,
398
401
  limit: d.Number
399
- }) {}, Nt = (e) => {
402
+ }) {}, It = (e) => {
400
403
  if (typeof e != "object" || !e) return JSON.stringify(e) ?? "null";
401
404
  let t = Object.entries(e).filter(([, e]) => e !== void 0).sort(([e], [t]) => e < t ? -1 : +(e > t));
402
405
  return JSON.stringify(t);
403
- }, Pt = "\0", Ft = (e, t, n) => [
406
+ }, Lt = "\0", Rt = (e, t, n) => [
404
407
  e ?? "~",
405
408
  t,
406
- Nt(n)
407
- ].join("\0"), It = (e) => {
409
+ It(n)
410
+ ].join("\0"), zt = (e) => {
408
411
  let [t = "~", n = "", r = ""] = e.split("\0");
409
412
  return {
410
413
  tenantId: t === "~" ? null : t,
411
414
  event: n,
412
415
  key: r
413
416
  };
414
- }, Lt = (e) => e.split("\0").join(" · "), Rt = (e) => e, zt = (e) => e, Bt = (e) => {
417
+ }, Bt = (e) => e.split("\0").join(" · "), Vt = (e) => e, Ht = (e) => e, Ut = (e) => {
415
418
  let t = e.alias?.trim(), n = e.instance?.trim(), r = t !== void 0 && t !== "" ? t : e.base;
416
419
  return n !== void 0 && n !== "" ? `${r}#${n}` : r;
417
- }, Vt = (e) => {
420
+ }, Wt = (e) => {
418
421
  if (e.length !== 0) return (t, n) => e.reduceRight((e, t) => t(e, n), t);
419
- }, Ht = (e, t) => {
422
+ }, Gt = (e, t) => {
420
423
  let n = He.GenericTag(e);
421
424
  return {
422
425
  Tag: n,
423
426
  Live: Ue.succeed(n, t)
424
427
  };
425
- }, Ut = (e, t, n) => {
428
+ }, Kt = (e, t, n) => {
426
429
  if (!t) return { ok: !0 };
427
- let r = G(n);
430
+ let r = W(n);
428
431
  if (!r) return {
429
432
  ok: !1,
430
433
  reason: `cannot parse runningVersion "${n}"`
@@ -432,12 +435,12 @@ var We = d.Union(d.String, d.Number), p = d.Record({
432
435
  let i = t.trim();
433
436
  if (i === "*" || i === "") return { ok: !0 };
434
437
  let a = i.split(/\s+/).filter((e) => e.length > 0);
435
- for (let i of a) if (!Wt(i, r)) return {
438
+ for (let i of a) if (!qt(i, r)) return {
436
439
  ok: !1,
437
440
  reason: `plugin "${e}" requires framework ${t}, running ${n}`
438
441
  };
439
442
  return { ok: !0 };
440
- }, G = (e) => {
443
+ }, W = (e) => {
441
444
  let t = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9a-zA-Z.-]+))?$/.exec(e.trim());
442
445
  return t ? {
443
446
  major: Number(t[1]),
@@ -445,44 +448,44 @@ var We = d.Union(d.String, d.Number), p = d.Record({
445
448
  patch: Number(t[3]),
446
449
  pre: t[4] ?? ""
447
450
  } : null;
448
- }, K = (e, t) => e.major === t.major ? e.minor === t.minor ? e.patch === t.patch ? e.pre && !t.pre ? -1 : !e.pre && t.pre ? 1 : e.pre && t.pre ? e.pre < t.pre ? -1 : +(e.pre > t.pre) : 0 : e.patch - t.patch : e.minor - t.minor : e.major - t.major, Wt = (e, t) => {
451
+ }, G = (e, t) => e.major === t.major ? e.minor === t.minor ? e.patch === t.patch ? e.pre && !t.pre ? -1 : !e.pre && t.pre ? 1 : e.pre && t.pre ? e.pre < t.pre ? -1 : +(e.pre > t.pre) : 0 : e.patch - t.patch : e.minor - t.minor : e.major - t.major, qt = (e, t) => {
449
452
  if (e === "*") return !0;
450
453
  if (e.startsWith("^")) {
451
- let n = G(e.slice(1));
452
- return !n || t.major !== n.major ? !1 : K(t, n) >= 0;
454
+ let n = W(e.slice(1));
455
+ return !n || t.major !== n.major ? !1 : G(t, n) >= 0;
453
456
  }
454
457
  if (e.startsWith("~")) {
455
- let n = G(e.slice(1));
456
- return !n || t.major !== n.major || t.minor !== n.minor ? !1 : K(t, n) >= 0;
458
+ let n = W(e.slice(1));
459
+ return !n || t.major !== n.major || t.minor !== n.minor ? !1 : G(t, n) >= 0;
457
460
  }
458
461
  let n = /^(>=|<=|>|<)(.+)$/.exec(e);
459
462
  if (n) {
460
- let e = n[1], r = G(n[2]);
463
+ let e = n[1], r = W(n[2]);
461
464
  if (!r) return !1;
462
- let i = K(t, r);
465
+ let i = G(t, r);
463
466
  if (e === ">=") return i >= 0;
464
467
  if (e === "<=") return i <= 0;
465
468
  if (e === ">") return i > 0;
466
469
  if (e === "<") return i < 0;
467
470
  }
468
- let r = G(e);
469
- return r ? K(t, r) === 0 : !1;
470
- }, q = d.Literal("running", "succeeded", "failed", "cancelled", "suspended"), Gt = d.Literal("cancel", "terminate", "abandon"), Kt = d.Struct({
471
+ let r = W(e);
472
+ return r ? G(t, r) === 0 : !1;
473
+ }, K = d.Literal("running", "succeeded", "failed", "cancelled", "suspended"), Jt = d.Literal("cancel", "terminate", "abandon"), Yt = d.Struct({
471
474
  mode: d.String,
472
475
  dueAt: d.NullOr(d.Number),
473
476
  retryAfterMs: d.NullOr(d.Number),
474
477
  intentId: d.NullOr(d.String)
475
- }), qt = d.Struct({
478
+ }), Xt = d.Struct({
476
479
  id: d.String,
477
480
  workflowName: d.String,
478
481
  executionId: d.NullOr(d.String),
479
482
  status: d.Literal("running", "queued", "dropped", "skipped"),
480
- deferral: d.optional(Kt)
481
- }), J = d.Struct({
483
+ deferral: d.optional(Yt)
484
+ }), q = d.Struct({
482
485
  id: d.String,
483
486
  tag: d.String,
484
487
  executionId: d.String,
485
- status: q,
488
+ status: K,
486
489
  payload: d.Unknown,
487
490
  workflowVersion: d.NullOr(d.String),
488
491
  workflowPatches: d.NullOr(d.Unknown),
@@ -497,12 +500,12 @@ var We = d.Union(d.String, d.Number), p = d.Record({
497
500
  durationMs: d.NullOr(d.Number),
498
501
  traceId: d.NullOr(d.String),
499
502
  parentExecutionId: d.NullOr(d.String),
500
- parentClosePolicy: d.NullOr(Gt)
501
- }), Jt = d.Struct({
503
+ parentClosePolicy: d.NullOr(Jt)
504
+ }), Zt = d.Struct({
502
505
  tag: d.optional(d.String),
503
- status: d.optional(q),
506
+ status: d.optional(K),
504
507
  limit: d.optional(d.Number)
505
- }), Yt = d.Struct({
508
+ }), Qt = d.Struct({
506
509
  id: d.String,
507
510
  runId: d.String,
508
511
  stepName: d.String,
@@ -517,7 +520,7 @@ var We = d.Union(d.String, d.Number), p = d.Record({
517
520
  startedAt: d.Date,
518
521
  completedAt: d.NullOr(d.Date),
519
522
  durationMs: d.NullOr(d.Number)
520
- }), Xt = d.Struct({
523
+ }), $t = d.Struct({
521
524
  id: d.String,
522
525
  runId: d.String,
523
526
  eventType: d.String,
@@ -525,7 +528,7 @@ var We = d.Union(d.String, d.Number), p = d.Record({
525
528
  occurredAt: d.Date,
526
529
  stepName: d.NullOr(d.String),
527
530
  attempt: d.NullOr(d.Number)
528
- }), Zt = d.Struct({
531
+ }), en = d.Struct({
529
532
  id: d.String,
530
533
  name: d.String,
531
534
  payload: d.Unknown,
@@ -533,7 +536,7 @@ var We = d.Union(d.String, d.Number), p = d.Record({
533
536
  subject: d.NullOr(d.Unknown),
534
537
  traceId: d.NullOr(d.String),
535
538
  occurredAt: d.Date
536
- }), Qt = d.Struct({
539
+ }), tn = d.Struct({
537
540
  id: d.String,
538
541
  eventId: d.String,
539
542
  eventName: d.String,
@@ -546,108 +549,108 @@ var We = d.Union(d.String, d.Number), p = d.Record({
546
549
  errorMessage: d.NullOr(d.String),
547
550
  createdAt: d.Date,
548
551
  completedAt: d.NullOr(d.Date)
549
- }), $t = d.Struct({ id: d.String }), Y = d.Struct({ runId: d.String }), en = d.Struct({
552
+ }), nn = d.Struct({ id: d.String }), J = d.Struct({ runId: d.String }), rn = d.Struct({
550
553
  name: d.optional(d.String),
551
554
  limit: d.optional(d.Number)
552
- }), tn = d.Struct({ eventId: d.String }), X = d.Struct({
555
+ }), an = d.Struct({ eventId: d.String }), Y = d.Struct({
553
556
  workflowName: d.String,
554
557
  executionId: d.String
555
- }), nn = d.Struct({
558
+ }), on = d.Struct({
556
559
  id: d.String,
557
560
  signalName: d.String,
558
561
  payload: d.optional(d.Unknown)
559
- }), rn = d.Struct({
562
+ }), sn = d.Struct({
560
563
  id: d.String,
561
564
  updateName: d.String,
562
565
  payload: d.optional(d.Unknown),
563
566
  timeoutMs: d.optional(d.Number)
564
- }), an = d.Struct({
567
+ }), cn = d.Struct({
565
568
  eventId: d.String,
566
569
  updateId: d.String,
567
570
  completedEventId: d.String,
568
571
  result: d.Unknown
569
- }), on = B({
572
+ }), ln = R({
570
573
  name: "__voltro.workflow.run",
571
574
  source: "_voltro_workflow_runs",
572
- input: $t,
573
- output: d.Array(J)
574
- }), sn = B({
575
+ input: nn,
576
+ output: d.Array(q)
577
+ }), un = R({
575
578
  name: "__voltro.workflow.runs",
576
579
  source: "_voltro_workflow_runs",
577
- input: Jt,
578
- output: d.Array(J)
579
- }), cn = B({
580
+ input: Zt,
581
+ output: d.Array(q)
582
+ }), dn = R({
580
583
  name: "__voltro.workflow.run.steps",
581
584
  source: "_voltro_workflow_run_steps",
582
- input: Y,
583
- output: d.Array(Yt)
584
- }), ln = B({
585
+ input: J,
586
+ output: d.Array(Qt)
587
+ }), fn = R({
585
588
  name: "__voltro.workflow.run.events",
586
589
  source: "_voltro_workflow_run_events",
587
- input: Y,
588
- output: d.Array(Xt)
589
- }), un = B({
590
+ input: J,
591
+ output: d.Array($t)
592
+ }), pn = R({
590
593
  name: "__voltro.workflow.domainEvents",
591
594
  source: "_voltro_workflow_events",
592
- input: en,
593
- output: d.Array(Zt)
594
- }), dn = B({
595
+ input: rn,
596
+ output: d.Array(en)
597
+ }), mn = R({
595
598
  name: "__voltro.workflow.event.deliveries",
596
599
  source: "_voltro_workflow_event_deliveries",
597
- input: tn,
598
- output: d.Array(Qt)
599
- }), fn = H({
600
+ input: an,
601
+ output: d.Array(tn)
602
+ }), hn = B({
600
603
  name: "__voltro.workflow.cancel",
601
- input: X,
604
+ input: Y,
602
605
  output: d.Struct({ ok: d.Boolean })
603
- }), pn = H({
606
+ }), gn = B({
604
607
  name: "__voltro.workflow.resume",
605
- input: X,
608
+ input: Y,
606
609
  output: d.Struct({ ok: d.Boolean })
607
- }), mn = H({
610
+ }), _n = B({
608
611
  name: "__voltro.workflow.signal",
609
- input: nn,
612
+ input: on,
610
613
  output: d.Struct({ eventId: d.String })
611
- }), hn = H({
614
+ }), vn = B({
612
615
  name: "__voltro.workflow.update",
613
- input: rn,
614
- output: an
615
- }), gn = "__voltro.undo.log", Z = "__voltro.undo.apply", _n = "__voltro.undo.redo", vn = d.Struct({
616
+ input: sn,
617
+ output: cn
618
+ }), yn = "__voltro.undo.log", bn = "__voltro.undo.apply", xn = "__voltro.undo.redo", Sn = d.Struct({
616
619
  id: d.String,
617
620
  tag: d.String,
618
621
  label: d.NullOr(d.String),
619
622
  undone: d.Boolean,
620
623
  crossesAction: d.Boolean,
621
624
  createdAt: d.String
622
- }), yn = class extends d.TaggedError()("UndoNotFound", { invocationId: d.String }) {}, bn = class extends d.TaggedError()("UndoForbidden", { invocationId: d.String }) {}, xn = class extends d.TaggedError()("UndoConflict", {
625
+ }), Cn = class extends d.TaggedError()("UndoNotFound", { invocationId: d.String }) {}, wn = class extends d.TaggedError()("UndoForbidden", { invocationId: d.String }) {}, X = class extends d.TaggedError()("UndoConflict", {
623
626
  invocationId: d.String,
624
627
  reason: d.Literal("conflict", "action")
625
- }) {}, Sn = d.Union(yn, bn, xn), Cn = B({
626
- name: gn,
628
+ }) {}, Tn = d.Union(Cn, wn, X), En = R({
629
+ name: yn,
627
630
  source: "_voltro_undo_log",
628
631
  input: d.Struct({ limit: d.optional(d.Number) }),
629
- output: d.Array(vn),
632
+ output: d.Array(Sn),
630
633
  openAccess: "subject-scoped by construction: lists only the calling subject's own undoable actions"
631
- }), wn = V({
632
- name: Z,
634
+ }), Dn = z({
635
+ name: bn,
633
636
  input: d.Struct({ invocationId: d.String }),
634
637
  output: d.Struct({ ok: d.Boolean }),
635
- error: Sn,
638
+ error: Tn,
636
639
  openAccess: "subject-scoped by construction: undo is per-actor — another subject's invocation fails typed with UndoForbidden"
637
- }), Tn = V({
638
- name: _n,
640
+ }), On = z({
641
+ name: xn,
639
642
  input: d.Struct({ invocationId: d.String }),
640
643
  output: d.Struct({ ok: d.Boolean }),
641
- error: Sn,
644
+ error: Tn,
642
645
  openAccess: "subject-scoped by construction: redo is per-actor — another subject's invocation fails typed with UndoForbidden"
643
- }), En = "__voltro.approvals.pending", Dn = "__voltro.approvals.decide", On = B({
644
- name: En,
646
+ }), kn = "__voltro.approvals.pending", An = "__voltro.approvals.decide", jn = R({
647
+ name: kn,
645
648
  source: "_voltro_approvals",
646
649
  input: d.Struct({ limit: d.optional(d.Number) }),
647
- output: d.Array(j),
650
+ output: d.Array(M),
648
651
  openAccess: "the answer is scoped to the caller — a row appears only if they requested it or hold its recorded approver scopes, so an anonymous caller sees nothing"
649
- }), kn = V({
650
- name: Dn,
652
+ }), Mn = z({
653
+ name: An,
651
654
  input: d.Struct({
652
655
  approvalId: d.String,
653
656
  decision: d.Literal("approve", "reject"),
@@ -657,35 +660,35 @@ var We = d.Union(d.String, d.Number), p = d.Record({
657
660
  approvalId: d.String,
658
661
  status: d.Literal("approved", "rejected")
659
662
  }),
660
- error: A,
663
+ error: j,
661
664
  openAccess: "the approver authority is the PENDING ROW's own recorded scopes (plus an unconditional self-approval refusal), checked in-handler — a descriptor-level scope would have to be the union of every approval-requiring procedure in the app"
662
- }), An = "__voltro.connections.list", jn = "__voltro.connections.start", Mn = "__voltro.connections.submitToken", Nn = "__voltro.connections.disconnect", Q = d.Literal("oauth2", "pat"), Pn = d.Literal("disconnected", "connected", "expired", "revoked", "error"), Fn = d.Struct({
665
+ }), Nn = "__voltro.connections.list", Pn = "__voltro.connections.start", Fn = "__voltro.connections.submitToken", In = "__voltro.connections.disconnect", Z = d.Literal("oauth2", "pat"), Ln = d.Literal("disconnected", "connected", "expired", "revoked", "error"), Rn = d.Struct({
663
666
  connectionId: d.String,
664
- kind: Q,
667
+ kind: Z,
665
668
  label: d.String,
666
- status: Pn,
669
+ status: Ln,
667
670
  accountId: d.NullOr(d.String),
668
671
  accountLabel: d.NullOr(d.String),
669
672
  scopes: d.Array(d.String),
670
673
  expiresAt: d.NullOr(d.String),
671
674
  lastError: d.NullOr(d.String),
672
675
  connectedAt: d.NullOr(d.String)
673
- }), In = class extends d.TaggedError()("ConnectionNotDeclared", { connectionId: d.String }) {}, Ln = class extends d.TaggedError()("ConnectionSubjectRequired", { connectionId: d.String }) {}, Rn = class extends d.TaggedError()("ConnectionKindMismatch", {
676
+ }), zn = class extends d.TaggedError()("ConnectionNotDeclared", { connectionId: d.String }) {}, Bn = class extends d.TaggedError()("ConnectionSubjectRequired", { connectionId: d.String }) {}, Vn = class extends d.TaggedError()("ConnectionKindMismatch", {
674
677
  connectionId: d.String,
675
- expected: Q,
676
- actual: Q
677
- }) {}, zn = class extends d.TaggedError()("ConnectionHandshakeFailed", {
678
+ expected: Z,
679
+ actual: Z
680
+ }) {}, Q = class extends d.TaggedError()("ConnectionHandshakeFailed", {
678
681
  connectionId: d.String,
679
682
  reason: d.String,
680
683
  transient: d.Boolean
681
- }) {}, $ = d.Union(In, Ln, Rn, zn), Bn = B({
682
- name: An,
684
+ }) {}, $ = d.Union(zn, Bn, Vn, Q), Hn = R({
685
+ name: Nn,
683
686
  source: "_voltro_connections",
684
687
  input: d.Struct({}),
685
- output: d.Array(Fn),
688
+ output: d.Array(Rn),
686
689
  openAccess: "self-scoped read: projects the declared connections for the calling subject only (its own connect/disconnect state)"
687
- }), Vn = H({
688
- name: jn,
690
+ }), Un = B({
691
+ name: Pn,
689
692
  input: d.Struct({
690
693
  connectionId: d.String,
691
694
  redirectTo: d.optional(d.String)
@@ -696,8 +699,8 @@ var We = d.Union(d.String, d.Number), p = d.Record({
696
699
  }),
697
700
  error: $,
698
701
  openAccess: "self-service: begins an oauth handshake that stores a credential for the calling subject only; anonymous callers fail typed with ConnectionSubjectRequired"
699
- }), Hn = V({
700
- name: Mn,
702
+ }), Wn = z({
703
+ name: Fn,
701
704
  input: d.Struct({
702
705
  connectionId: d.String,
703
706
  token: d.String
@@ -705,15 +708,15 @@ var We = d.Union(d.String, d.Number), p = d.Record({
705
708
  output: d.Struct({ ok: d.Boolean }),
706
709
  error: $,
707
710
  openAccess: "self-service: stores a pasted token as the calling subject's own credential; anonymous callers fail typed with ConnectionSubjectRequired"
708
- }), Un = V({
709
- name: Nn,
711
+ }), Gn = z({
712
+ name: In,
710
713
  input: d.Struct({ connectionId: d.String }),
711
714
  output: d.Struct({ ok: d.Boolean }),
712
715
  error: $,
713
716
  openAccess: "self-service: deletes only the calling subject's own credential row for this connection"
714
- }), Wn = (e) => {
717
+ }), Kn = (e) => {
715
718
  let t = e instanceof Date ? e.getTime() : typeof e == "number" ? e : typeof e == "string" ? new Date(e).getTime() : 0;
716
719
  return Number.isNaN(t) ? 0 : t;
717
- }, Gn = 1;
720
+ }, qn = 1;
718
721
  //#endregion
719
- export { Oe as ADMIN_SCOPE, Ce as APIKEY_ISSUE_ORG_SCOPE, Ee as APIKEY_ISSUE_OTHER_SCOPE, ve as APIKEY_ISSUE_SELF_SCOPE, Dn as APPROVALS_DECIDE_TAG, En as APPROVALS_PENDING_TAG, A as ApprovalDecisionErrors, k as ApprovalErrors, T as ApprovalExpired, D as ApprovalForbidden, S as ApprovalNotFound, E as ApprovalNotPending, w as ApprovalRejected, x as ApprovalRequired, C as ApprovalSelfApproval, O as ApprovalUnavailable, Ae as AuthMiddleware, b as BusinessRuleViolation, An as CONNECTIONS_LIST_TAG, Nn as CONNECTION_DISCONNECT_TAG, jn as CONNECTION_START_TAG, Mn as CONNECTION_SUBMIT_TOKEN_TAG, zn as ConnectionHandshakeFailed, ce as ConnectionInfo, i as ConnectionInfoMiddleware, Q as ConnectionKind, Rn as ConnectionKindMismatch, In as ConnectionNotDeclared, Fn as ConnectionState, Pn as ConnectionStatus, Ln as ConnectionSubjectRequired, r as DEFAULT_SCOPE_CACHE_MAX_ENTRIES, ie as DEFAULT_SCOPE_CACHE_TTL_MS, Pt as EVENT_ROUTE_SEP, jt as EventKeyInvalid, At as EventPayloadInvalid, Mt as EventPayloadTooLarge, be as IMPERSONATION_METADATA_KEY, bt as MAX_EVENT_ENVELOPE_BYTES, Gn as PROTOCOL_VERSION, j as PendingApproval, M as REACTIVITY_CHANNEL_PREFIX, c as ScopeError, e as Subject, ye as SubjectIdentity, re as SubjectService, Z as UNDO_APPLY_TAG, gn as UNDO_LOG_TAG, _n as UNDO_REDO_TAG, s as Unauthenticated, xn as UndoConflict, bn as UndoForbidden, vn as UndoLogEntry, yn as UndoNotFound, X as WorkflowControlInputSchema, Zt as WorkflowDomainEventRowSchema, en as WorkflowDomainEventsInputSchema, tn as WorkflowEventDeliveriesInputSchema, Qt as WorkflowEventDeliveryRowSchema, Gt as WorkflowParentClosePolicySchema, Xt as WorkflowRunEventRowSchema, qt as WorkflowRunHandleSchema, $t as WorkflowRunRefSchema, J as WorkflowRunRowSchema, q as WorkflowRunStatusSchema, Yt as WorkflowRunStepRowSchema, Y as WorkflowRunTableRefSchema, Jt as WorkflowRunsInputSchema, nn as WorkflowSignalInputSchema, Kt as WorkflowStartDeferralSchema, rn as WorkflowUpdateInputSchema, an as WorkflowUpdateResultSchema, ht as actionToRpc, we as advisoryResourceGuardWarning, ae as anonymousSubject, Ye as applyRowPatch, a as applyScopeDecision, kn as approvalsDecideDescriptor, On as approvalsPendingQueryDescriptor, te as assertAuthenticated, ze as beginIdempotent, Ut as checkFrameworkCompat, De as checkGuards, me as checkGuardsEffect, ne as composeAuthStrategies, Vt as composeRpcInterceptors, Un as connectionDisconnectDescriptor, Vn as connectionStartDescriptor, Hn as connectionSubmitTokenDescriptor, Bn as connectionsListQueryDescriptor, et as declaredReactivityChannelKeys, H as defineAction, St as defineEvent, V as defineMutation, Rt as definePlugin, zt as definePluginRoute, Ht as definePluginService, B as defineQuery, lt as defineStream, qe as diffRows, xe as effectiveScopes, Nt as encodeEventKey, _t as errorTag, Tt as eventAttached, Ct as eventEnvelope, wt as eventGap, Dt as eventResumePoint, Ft as eventRoute, Et as eventStreamEvent, Ot as eventSubscribeInput, kt as eventToRpc, Ie as failIdempotent, ke as findAdvisoryResourceGuards, Pe as finishIdempotent, Lt as formatEventRoute, he as getPolicyGuardResolver, ge as getResourceScopeResolver, ct as hasAccessDecision, oe as hasCallbackRoutes, Te as hasEffectiveScope, Se as hasScope, g as idToPath, Re as idempotencyScope, xt as isEventDescriptor, Je as isIdKeyed, l as isOpenAccess, _e as isPolicyCheck, st as isPolicyGuard, I as isReactivityChannel, $e as isReactivityChannelKey, Ne as isSystemSubject, at as isWireReachable, t as makeScopeCache, Be as memoryIdempotencyStore, fe as missingAccessDecision, mt as mutationToRpc, yt as normalizeDescriptor, nt as normalizeSource, u as openAccessSpec, It as parseEventRoute, Ge as pathToId, Bt as pluginInstanceName, it as publishReactivity, Ve as publishServerError, pt as queryToRpc, ue as rawImpersonationMark, Qe as reactivityChannel, Me as requireScope, m as rowPatchOpSchema, h as rowPatchSchema, ee as scopeCacheKey, pe as setEffectiveScopes, je as setPolicyGuardResolver, se as setResourceScopeResolver, tt as sourceKeys, gt as streamToRpc, y as strictInput, le as subjectIdentity, n as subjectScopes, Le as subscribeServerErrors, v as subscriptionEvent, de as systemSubject, o as tenantScopedSubject, vt as toRpc, Wn as tsMs, rt as undeclaredChannelKeys, wn as undoApplyDescriptor, Cn as undoLogQueryDescriptor, Tn as undoRedoDescriptor, W as wireErrorUnion, fn as workflowCancelDescriptor, un as workflowDomainEventsQueryDescriptor, dn as workflowEventDeliveriesQueryDescriptor, pn as workflowResumeDescriptor, ln as workflowRunEventsQueryDescriptor, on as workflowRunQueryDescriptor, cn as workflowRunStepsQueryDescriptor, sn as workflowRunsQueryDescriptor, mn as workflowSignalDescriptor, hn as workflowUpdateDescriptor, Fe as wsMutationIdempotencyScope };
722
+ export { Oe as ADMIN_SCOPE, Ce as APIKEY_ISSUE_ORG_SCOPE, Ee as APIKEY_ISSUE_OTHER_SCOPE, ve as APIKEY_ISSUE_SELF_SCOPE, An as APPROVALS_DECIDE_TAG, kn as APPROVALS_PENDING_TAG, j as ApprovalDecisionErrors, A as ApprovalErrors, E as ApprovalExpired, O as ApprovalForbidden, C as ApprovalNotFound, D as ApprovalNotPending, T as ApprovalRejected, S as ApprovalRequired, w as ApprovalSelfApproval, k as ApprovalUnavailable, Ae as AuthMiddleware, x as BusinessRuleViolation, Nn as CONNECTIONS_LIST_TAG, In as CONNECTION_DISCONNECT_TAG, Pn as CONNECTION_START_TAG, Fn as CONNECTION_SUBMIT_TOKEN_TAG, Q as ConnectionHandshakeFailed, ce as ConnectionInfo, i as ConnectionInfoMiddleware, Z as ConnectionKind, Vn as ConnectionKindMismatch, zn as ConnectionNotDeclared, Rn as ConnectionState, Ln as ConnectionStatus, Bn as ConnectionSubjectRequired, r as DEFAULT_SCOPE_CACHE_MAX_ENTRIES, ie as DEFAULT_SCOPE_CACHE_TTL_MS, Lt as EVENT_ROUTE_SEP, Pt as EventKeyInvalid, Nt as EventPayloadInvalid, Ft as EventPayloadTooLarge, be as IMPERSONATION_METADATA_KEY, Ct as MAX_EVENT_ENVELOPE_BYTES, qn as PROTOCOL_VERSION, M as PendingApproval, N as REACTIVITY_CHANNEL_PREFIX, c as ScopeError, e as Subject, ye as SubjectIdentity, re as SubjectService, bn as UNDO_APPLY_TAG, yn as UNDO_LOG_TAG, xn as UNDO_REDO_TAG, s as Unauthenticated, X as UndoConflict, wn as UndoForbidden, Sn as UndoLogEntry, Cn as UndoNotFound, Y as WorkflowControlInputSchema, en as WorkflowDomainEventRowSchema, rn as WorkflowDomainEventsInputSchema, an as WorkflowEventDeliveriesInputSchema, tn as WorkflowEventDeliveryRowSchema, Jt as WorkflowParentClosePolicySchema, $t as WorkflowRunEventRowSchema, Xt as WorkflowRunHandleSchema, nn as WorkflowRunRefSchema, q as WorkflowRunRowSchema, K as WorkflowRunStatusSchema, Qt as WorkflowRunStepRowSchema, J as WorkflowRunTableRefSchema, Zt as WorkflowRunsInputSchema, on as WorkflowSignalInputSchema, Yt as WorkflowStartDeferralSchema, sn as WorkflowUpdateInputSchema, cn as WorkflowUpdateResultSchema, vt as actionToRpc, we as advisoryResourceGuardWarning, ae as anonymousSubject, Ye as applyRowPatch, a as applyScopeDecision, Mn as approvalsDecideDescriptor, jn as approvalsPendingQueryDescriptor, te as assertAuthenticated, ze as beginIdempotent, Kt as checkFrameworkCompat, De as checkGuards, me as checkGuardsEffect, ne as composeAuthStrategies, Wt as composeRpcInterceptors, Gn as connectionDisconnectDescriptor, Un as connectionStartDescriptor, Wn as connectionSubmitTokenDescriptor, Hn as connectionsListQueryDescriptor, rt as declaredReactivityChannelKeys, B as defineAction, Tt as defineEvent, z as defineMutation, Vt as definePlugin, Ht as definePluginRoute, Gt as definePluginService, R as defineQuery, ft as defineStream, qe as diffRows, xe as effectiveScopes, It as encodeEventKey, bt as errorTag, Ot as eventAttached, Et as eventEnvelope, Dt as eventGap, At as eventResumePoint, Rt as eventRoute, kt as eventStreamEvent, jt as eventSubscribeInput, Mt as eventToRpc, Ie as failIdempotent, ke as findAdvisoryResourceGuards, Pe as finishIdempotent, Bt as formatEventRoute, he as getPolicyGuardResolver, ge as getResourceScopeResolver, dt as hasAccessDecision, oe as hasCallbackRoutes, Te as hasEffectiveScope, H as hasEnforcedGuard, Se as hasScope, g as idToPath, Re as idempotencyScope, y as inputLabel, wt as isEventDescriptor, Je as isIdKeyed, l as isOpenAccess, _e as isPolicyCheck, ut as isPolicyGuard, tt as isReactivityChannel, nt as isReactivityChannelKey, Ne as isSystemSubject, ct as isWireReachable, t as makeScopeCache, Be as memoryIdempotencyStore, fe as missingAccessDecision, _t as mutationToRpc, St as normalizeDescriptor, at as normalizeSource, u as openAccessSpec, zt as parseEventRoute, Ge as pathToId, Ut as pluginInstanceName, st as publishReactivity, Ve as publishServerError, gt as queryToRpc, ue as rawImpersonationMark, et as reactivityChannel, Me as requireScope, m as rowPatchOpSchema, h as rowPatchSchema, ee as scopeCacheKey, pe as setEffectiveScopes, je as setPolicyGuardResolver, se as setResourceScopeResolver, it as sourceKeys, yt as streamToRpc, b as strictInput, le as subjectIdentity, n as subjectScopes, Le as subscribeServerErrors, v as subscriptionEvent, de as systemSubject, o as tenantScopedSubject, xt as toRpc, Kn as tsMs, ot as undeclaredChannelKeys, Dn as undoApplyDescriptor, En as undoLogQueryDescriptor, On as undoRedoDescriptor, U as wireErrorUnion, hn as workflowCancelDescriptor, pn as workflowDomainEventsQueryDescriptor, mn as workflowEventDeliveriesQueryDescriptor, gn as workflowResumeDescriptor, fn as workflowRunEventsQueryDescriptor, ln as workflowRunQueryDescriptor, dn as workflowRunStepsQueryDescriptor, un as workflowRunsQueryDescriptor, _n as workflowSignalDescriptor, vn as workflowUpdateDescriptor, Fe as wsMutationIdempotencyScope };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/protocol",
3
- "version": "0.40.0",
3
+ "version": "0.41.0",
4
4
  "description": "The Voltro wire + plugin contract — defineQuery/Mutation/Action/Stream, definePlugin, sessions / JWT / API-keys, and the RPC protocol.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -54,8 +54,8 @@
54
54
  },
55
55
  "dependencies": {
56
56
  "@effect/sql": "^0.52.0",
57
- "@voltro/database": "0.40.0",
58
- "@voltro/logger": "0.40.0",
57
+ "@voltro/database": "0.41.0",
58
+ "@voltro/logger": "0.41.0",
59
59
  "jose": "^6.2.8"
60
60
  },
61
61
  "peerDependencies": {