@voltro/protocol 0.11.4 → 0.13.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/dist/index.d.ts CHANGED
@@ -39,6 +39,37 @@ export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec;
39
39
  /** A guard is either a scope check or a relationship check. */
40
40
  export declare type AnyGuardSpec<Input = unknown> = GuardSpec<Input> | PolicyGuardSpec<Input>;
41
41
 
42
+ export declare const APIKEY_ISSUE_ORG_SCOPE = "apikeys:issue:org";
43
+
44
+ export declare const APIKEY_ISSUE_OTHER_SCOPE = "apikeys:issue:other";
45
+
46
+ /**
47
+ * API-key issuance rights.
48
+ *
49
+ * One `admin:full` gate for all key minting was too coarse to express what
50
+ * products actually need: it means only a full admin can ever mint a key, so a
51
+ * normal user cannot create their own even narrowly-scoped credential, and an
52
+ * admin minting one FOR someone is indistinguishable from minting one for
53
+ * themselves.
54
+ *
55
+ * Three separable capabilities instead:
56
+ *
57
+ * `apikeys:issue:self` - mint a key that acts as ME. The common self-service
58
+ * case; the key can never exceed the holder's own
59
+ * scopes.
60
+ * `apikeys:issue:org` - mint an ORG key: acts as no person, belongs to the
61
+ * organization. A CI credential. Separate because
62
+ * "may create a personal token" and "may create a
63
+ * credential that outlives my account" are genuinely
64
+ * different levels of trust.
65
+ * `apikeys:issue:other` - mint a key ON BEHALF OF another user. Admin
66
+ * territory: it is the ability to act as someone
67
+ * else, so it is never implied by the other two.
68
+ *
69
+ * `admin:full` satisfies all three, as it does every scope.
70
+ */
71
+ export declare const APIKEY_ISSUE_SELF_SCOPE = "apikeys:issue:self";
72
+
42
73
  /**
43
74
  * Apply a `RowPatch` to `prev`, producing `next`. Exact inverse of
44
75
  * `diffRows`: `applyRowPatch(prev, diffRows(prev, next))` deep-equals
@@ -124,6 +155,24 @@ export declare interface AuthStrategy {
124
155
  * in logs + `Subject.metadata.provider`. */
125
156
  readonly id: string;
126
157
  readonly resolve: (input: AuthStrategyInput) => Promise<StrategyResolution> | StrategyResolution;
158
+ /**
159
+ * The bearer-token PREFIX this strategy claims, when it gates on one
160
+ * (`'sk_'`, `'awb_'`). Declared so a collision is DETECTABLE.
161
+ *
162
+ * Two strategies claiming the same prefix is not a harmless duplicate: the
163
+ * chain is first-match-wins, so whichever runs first decides the Subject —
164
+ * and if they resolve the same token to different authority, which one
165
+ * answered decides whether authorization works. A downstream app hit exactly
166
+ * this and had to pin a test asserting it never sets `apiKeys: true`, because
167
+ * doing so would append the framework strategy alongside its own on the same
168
+ * `sk_` prefix, with the framework one resolving without the app's team
169
+ * binding.
170
+ *
171
+ * Optional: a cookie or JWKS strategy claims no prefix and omits it. Only
172
+ * what is declared can be checked — a strategy that gates on a prefix without
173
+ * saying so is invisible to the boot check, exactly as before.
174
+ */
175
+ readonly claimsBearerPrefix?: string;
127
176
  }
128
177
 
129
178
  /** Per-call input the framework hands every strategy. */
@@ -243,18 +292,6 @@ export declare interface ClientTarget {
243
292
  readonly shapeItem?: ((input: Record<string, unknown>, currentOrOptimisticId: unknown) => Record<string, unknown>) | undefined;
244
293
  }
245
294
 
246
- /** Compose multiple strategies into a single resolver function. The
247
- * composer evaluates them in declaration order; first `matched`
248
- * wins; first `failed` short-circuits to anonymous (does NOT fall
249
- * through — see StrategyResolution doc).
250
- *
251
- * Apps wrap the return in `AuthMiddleware.of(...)` themselves so they
252
- * can inject the connection-override fast-path (`getConnectionSubject`)
253
- * ahead of the strategy chain — that path lives in `@voltro/runtime`
254
- * and the protocol package stays runtime-agnostic.
255
- *
256
- * Returns an async resolver `(input) => Promise<Subject>`.
257
- */
258
295
  export declare const composeAuthStrategies: (strategies: ReadonlyArray<AuthStrategy>, options?: {
259
296
  /** Fallback when no strategy matched. Default: returns the input's
260
297
  * tenant from `x-tenant` header or null. */
@@ -275,6 +312,42 @@ export declare const composeAuthStrategies: (strategies: ReadonlyArray<AuthStrat
275
312
  * `fallback` is supplied (the fallback owns that decision).
276
313
  */
277
314
  readonly anonymousTenantRequired?: boolean;
315
+ /**
316
+ * Add scopes to a resolved Subject from a source the strategy could not
317
+ * see — typically a role stored in the app's own database.
318
+ *
319
+ * **The gap this closes.** An app whose authorization is a DB ROLE
320
+ * (`requireCallerAdmin(ctx)` reading an `employees.role` column) is
321
+ * invisible to every static analysis the framework has: `voltro check`'s
322
+ * `rbac/unguarded-mutation` reports its writes as unguarded, and it is
323
+ * right to — nothing about that authorization is declared. But the
324
+ * declarative alternative was unusable for them: their subjects come from
325
+ * an external IdP's JWTs and carry no scopes, so `guards: [{ scope:
326
+ * 'employee:admin' }]` would lock out every real user. One app measured
327
+ * 1566 findings it had no way to act on.
328
+ *
329
+ * Lifting roles into `subject.scopes` here makes the SAME authorization
330
+ * declarable — `requireScope('employee:admin')` on the descriptor, visible
331
+ * in the manifest, checkable by CI. That is the framework's own
332
+ * scope-vs-filter argument one level up: only what is declared can be
333
+ * checked.
334
+ *
335
+ * **Deliberately narrow: scopes only.** It cannot return a Subject. A hook
336
+ * that could rewrite `id` or `tenantId` would be a forgery surface — the
337
+ * same shape as the api-key `metadata.userId` bug, where an app-supplied
338
+ * bag could overwrite the framework's claim about who a request was. The
339
+ * strategy owns identity; this owns authority.
340
+ *
341
+ * Returned scopes are UNIONED with whatever the strategy already set, so a
342
+ * resolver cannot silently remove a scope either.
343
+ *
344
+ * Runs only on a MATCHED subject — never for anonymous, where there is no
345
+ * identity to look a role up for. It is on the request path, so cache it
346
+ * (a per-connection or short-TTL map keyed by subject id); the framework
347
+ * deliberately does not cache for you, because only the app knows how
348
+ * quickly a role change must take effect.
349
+ */
350
+ readonly resolveScopes?: (subject: Subject, input: AuthStrategyInput) => Promise<ReadonlyArray<string>> | ReadonlyArray<string>;
278
351
  }) => ((input: AuthStrategyInput) => Promise<Subject>);
279
352
 
280
353
  /**
@@ -2222,6 +2295,25 @@ export declare type Target<Input = unknown, Row = unknown> = TargetSpec<Input, R
2222
2295
 
2223
2296
  export declare type TargetSpec<Input = unknown, Row = unknown> = InsertTarget<Input, Row> | UpdateTarget<Input, Row> | DeleteTarget<Input>;
2224
2297
 
2298
+ /**
2299
+ * The subject a `storeForTenant(id)` view runs as — the caller's identity,
2300
+ * re-pointed at ONE explicit tenant.
2301
+ *
2302
+ * Lives here, next to the other subject constructors, because BOTH the serve
2303
+ * context builder (`@voltro/cli`) and the test harness (`@voltro/testing`)
2304
+ * must produce the identical subject. When it lived in the CLI, the harness
2305
+ * could not reach it, and `ctx.storeForTenant` was simply absent under test —
2306
+ * a handler that used it had no way to be tested at all.
2307
+ *
2308
+ * The narrowed return type is the point, not decoration: it states at the type
2309
+ * level that this can never hand back a `system` subject, which is the variant
2310
+ * whose null tenant means "all tenants". Producing one here would silently
2311
+ * widen a deliberately narrow view back to every tenant.
2312
+ */
2313
+ export declare const tenantScopedSubject: (subject: Subject, tenantId: string) => Extract<Subject, {
2314
+ readonly type: "serviceAccount";
2315
+ }>;
2316
+
2225
2317
  export declare const toRpc: <Name extends string, Input extends Schema.Schema.Any, Output extends Schema.Schema.Any, Err extends Schema.Schema.All>(descriptor: QueryProcedureDescriptor<Name, Input, Output, Err> | MutationProcedureDescriptor<Name, Input, Output, Err> | ActionProcedureDescriptor<Name, Input, Output, Err> | StreamProcedureDescriptor<Name, Input, Output, Err>) => Rpc.Rpc<Name, Input extends Schema.Struct.Fields ? Schema.Struct<Input> : Input, Stream<Schema.Union<[Schema.Struct<{
2226
2318
  _tag: Schema.Literal<["snapshot"]>;
2227
2319
  revision: typeof Schema.Number;
@@ -2449,6 +2541,24 @@ export declare interface VoltroPlugin {
2449
2541
  * dashboard Env panel. Mirror exactly what the plugin reads.
2450
2542
  */
2451
2543
  readonly declaredEnv?: ReadonlyArray<PluginEnvVar>;
2544
+ /**
2545
+ * The authorization scopes this plugin DEFINES for the app — its scope
2546
+ * vocabulary. Distinct from `permissions` above, which is what the plugin
2547
+ * itself asks to be granted.
2548
+ *
2549
+ * `@voltro/plugin-rbac` fills this with every scope its `roles` map grants,
2550
+ * because that map already IS the app's declared vocabulary. The capability
2551
+ * manifest unions these, and `voltro check` compares each handler's required
2552
+ * scopes against the union: a guard demanding a scope no role can grant (a
2553
+ * typo, a rename) makes that procedure permanently uncallable, silently.
2554
+ *
2555
+ * Only declare this when the list is EXHAUSTIVE. A plugin that also grants
2556
+ * scopes from a dynamic source (a custom resolver, per-row ACLs) must leave
2557
+ * it undefined — the check treats "no declared scopes" as "cannot conclude"
2558
+ * and stays dormant, which is the honest outcome. A partial list would flag
2559
+ * correct code, and a check that cries wolf gets ignored.
2560
+ */
2561
+ readonly declaredScopes?: ReadonlyArray<string>;
2452
2562
  /**
2453
2563
  * Optional `effect/Schema` describing the plugin's user-supplied
2454
2564
  * config. When present the framework decodes the operator's config
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
- import { C as e, S as t, _ as n, a as r, b as i, c as a, d as o, f as ee, g as te, h as ne, i as re, l as s, m as ie, n as ae, o as oe, p as se, r as ce, s as le, t as ue, u as de, v as fe, w as pe, x as me, y as he } from "./serverErrorBus-B3hDwGoL.js";
2
- import { a as ge, c as _e, d as ve, f as ye, i as be, l as xe, n as Se, o as Ce, r as we, s as Te, t as Ee, u as De } from "./auth-DCE6m7Bo.js";
3
- import { Context as Oe, Layer as ke, Schema as c } from "effect";
1
+ import { C as e, D as t, E as n, S as r, T as i, _ as a, a as o, b as ee, c as te, d as ne, f as s, g as re, h as ie, i as ae, l as oe, m as se, n as ce, o as le, p as ue, r as de, s as fe, t as pe, u as me, v as he, w as ge, x as _e, y as ve } from "./serverErrorBus-DhIVDkCi.js";
2
+ import { a as ye, c as be, d as xe, f as Se, i as Ce, l as we, n as Te, o as Ee, p as De, r as Oe, s as ke, t as Ae, u as je } from "./auth-DVrHg739.js";
3
+ import { Context as Me, Layer as Ne, Schema as c } from "effect";
4
4
  import { Rpc as l } from "@effect/rpc";
5
5
  //#region src/rowPatch.ts
6
- var Ae = c.Union(c.String, c.Number), u = c.Record({
6
+ var Pe = c.Union(c.String, c.Number), u = c.Record({
7
7
  key: c.String,
8
8
  value: c.Unknown
9
9
  }).pipe(c.filter((e) => "id" in e, { message: () => "patch row must carry an id" })), d = c.Union(c.Struct({
@@ -19,12 +19,12 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
19
19
  path: c.String
20
20
  })), f = c.Struct({
21
21
  ops: c.Array(d),
22
- order: c.Array(Ae)
23
- }), p = (e) => `/${String(e).replace(/~/g, "~0").replace(/\//g, "~1")}`, je = (e) => (e.startsWith("/") ? e.slice(1) : e).replace(/~1/g, "/").replace(/~0/g, "~"), m = (e) => {
22
+ order: c.Array(Pe)
23
+ }), p = (e) => `/${String(e).replace(/~/g, "~0").replace(/\//g, "~1")}`, Fe = (e) => (e.startsWith("/") ? e.slice(1) : e).replace(/~1/g, "/").replace(/~0/g, "~"), m = (e) => {
24
24
  let t = Object.keys(e).sort(), n = {};
25
25
  for (let r of t) n[r] = e[r];
26
26
  return JSON.stringify(n);
27
- }, Me = (e, t) => m(e) === m(t), Ne = (e, t) => {
27
+ }, Ie = (e, t) => m(e) === m(t), Le = (e, t) => {
28
28
  let n = /* @__PURE__ */ new Map();
29
29
  for (let t of e) n.set(t.id, t);
30
30
  let r = [], i = [], a = /* @__PURE__ */ new Set();
@@ -35,7 +35,7 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
35
35
  op: "add",
36
36
  path: p(e.id),
37
37
  value: e
38
- }) : Me(t, e) || r.push({
38
+ }) : Ie(t, e) || r.push({
39
39
  op: "replace",
40
40
  path: p(e.id),
41
41
  value: e
@@ -49,10 +49,10 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
49
49
  ops: r,
50
50
  order: i
51
51
  };
52
- }, Pe = (e) => e.every((e) => {
52
+ }, Re = (e) => e.every((e) => {
53
53
  let t = e.id;
54
54
  return typeof t == "string" || typeof t == "number";
55
- }), Fe = (e, t) => {
55
+ }), ze = (e, t) => {
56
56
  let n = /* @__PURE__ */ new Map();
57
57
  for (let t of e) n.set(t.id, t);
58
58
  for (let e of t.ops) (e.op === "add" || e.op === "replace") && n.set(e.value.id, e.value);
@@ -76,7 +76,7 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
76
76
  _tag: c.Literal("error"),
77
77
  error: c.Unknown,
78
78
  revision: c.optional(c.Number)
79
- })), Ie = (e) => e.action !== void 0 && e.resourceType !== void 0, g = (e) => ({
79
+ })), Be = (e) => e.action !== void 0 && e.resourceType !== void 0, g = (e) => ({
80
80
  kind: "query",
81
81
  name: e.name,
82
82
  input: e.input,
@@ -106,7 +106,7 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
106
106
  guards: e.guards,
107
107
  publicApi: e.publicApi,
108
108
  exposeAsTool: e.exposeAsTool
109
- }), Le = (e) => ({
109
+ }), Ve = (e) => ({
110
110
  kind: "stream",
111
111
  name: e.name,
112
112
  input: e.input,
@@ -117,27 +117,27 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
117
117
  success: h(e.output),
118
118
  error: y(b(e.error, e.guards), t),
119
119
  stream: !0
120
- }), Re = (e, t) => l.make(e.name, {
120
+ }), S = (e, t) => l.make(e.name, {
121
121
  payload: e.input,
122
122
  success: e.output,
123
123
  error: y(b(e.error, e.guards), t)
124
- }), ze = (e, t) => l.make(e.name, {
124
+ }), C = (e, t) => l.make(e.name, {
125
125
  payload: e.input,
126
126
  success: e.output,
127
127
  error: y(b(e.error, e.guards), t)
128
- }), S = (e, t) => l.make(e.name, {
128
+ }), w = (e, t) => l.make(e.name, {
129
129
  payload: e.input,
130
130
  success: e.element,
131
131
  error: y(e.error, t),
132
132
  stream: !0
133
- }), Be = (e) => {
133
+ }), He = (e) => {
134
134
  switch (e.kind) {
135
135
  case "query": return x(e);
136
- case "mutation": return Re(e);
137
- case "action": return ze(e);
138
- case "stream": return S(e);
136
+ case "mutation": return S(e);
137
+ case "action": return C(e);
138
+ case "stream": return w(e);
139
139
  }
140
- }, Ve = (e) => {
140
+ }, Ue = (e) => {
141
141
  let t = e.input, n = t === void 0 ? {} : { input: t }, r = e.output, i = r === void 0 ? {} : { output: r };
142
142
  if (e.kind === "query") return {
143
143
  kind: "query",
@@ -178,17 +178,17 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
178
178
  ..."shapeItem" in e && e.shapeItem !== void 0 ? { shapeItem: e.shapeItem } : {}
179
179
  }))
180
180
  };
181
- }, He = (e) => e, Ue = (e) => e, We = (e) => {
181
+ }, We = (e) => e, Ge = (e) => e, Ke = (e) => {
182
182
  if (e.length !== 0) return (t, n) => e.reduceRight((e, t) => t(e, n), t);
183
- }, Ge = (e, t) => {
184
- let n = Oe.GenericTag(e);
183
+ }, qe = (e, t) => {
184
+ let n = Me.GenericTag(e);
185
185
  return {
186
186
  Tag: n,
187
- Live: ke.succeed(n, t)
187
+ Live: Ne.succeed(n, t)
188
188
  };
189
- }, Ke = (e, t, n) => {
189
+ }, Je = (e, t, n) => {
190
190
  if (!t) return { ok: !0 };
191
- let r = C(n);
191
+ let r = T(n);
192
192
  if (!r) return {
193
193
  ok: !1,
194
194
  reason: `cannot parse runningVersion "${n}"`
@@ -196,12 +196,12 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
196
196
  let i = t.trim();
197
197
  if (i === "*" || i === "") return { ok: !0 };
198
198
  let a = i.split(/\s+/).filter((e) => e.length > 0);
199
- for (let i of a) if (!qe(i, r)) return {
199
+ for (let i of a) if (!Ye(i, r)) return {
200
200
  ok: !1,
201
201
  reason: `plugin "${e}" requires framework ${t}, running ${n}`
202
202
  };
203
203
  return { ok: !0 };
204
- }, C = (e) => {
204
+ }, T = (e) => {
205
205
  let t = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9a-zA-Z.-]+))?$/.exec(e.trim());
206
206
  return t ? {
207
207
  major: Number(t[1]),
@@ -209,38 +209,38 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
209
209
  patch: Number(t[3]),
210
210
  pre: t[4] ?? ""
211
211
  } : null;
212
- }, w = (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, qe = (e, t) => {
212
+ }, E = (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, Ye = (e, t) => {
213
213
  if (e === "*") return !0;
214
214
  if (e.startsWith("^")) {
215
- let n = C(e.slice(1));
216
- return !n || t.major !== n.major ? !1 : w(t, n) >= 0;
215
+ let n = T(e.slice(1));
216
+ return !n || t.major !== n.major ? !1 : E(t, n) >= 0;
217
217
  }
218
218
  if (e.startsWith("~")) {
219
- let n = C(e.slice(1));
220
- return !n || t.major !== n.major || t.minor !== n.minor ? !1 : w(t, n) >= 0;
219
+ let n = T(e.slice(1));
220
+ return !n || t.major !== n.major || t.minor !== n.minor ? !1 : E(t, n) >= 0;
221
221
  }
222
222
  let n = /^(>=|<=|>|<)(.+)$/.exec(e);
223
223
  if (n) {
224
- let e = n[1], r = C(n[2]);
224
+ let e = n[1], r = T(n[2]);
225
225
  if (!r) return !1;
226
- let i = w(t, r);
226
+ let i = E(t, r);
227
227
  if (e === ">=") return i >= 0;
228
228
  if (e === "<=") return i <= 0;
229
229
  if (e === ">") return i > 0;
230
230
  if (e === "<") return i < 0;
231
231
  }
232
- let r = C(e);
233
- return r ? w(t, r) === 0 : !1;
234
- }, T = c.Literal("running", "succeeded", "failed", "cancelled", "suspended"), E = c.Literal("cancel", "terminate", "abandon"), Je = c.Struct({
232
+ let r = T(e);
233
+ return r ? E(t, r) === 0 : !1;
234
+ }, D = c.Literal("running", "succeeded", "failed", "cancelled", "suspended"), O = c.Literal("cancel", "terminate", "abandon"), Xe = c.Struct({
235
235
  id: c.String,
236
236
  workflowName: c.String,
237
237
  executionId: c.String,
238
238
  status: c.Literal("running")
239
- }), D = c.Struct({
239
+ }), k = c.Struct({
240
240
  id: c.String,
241
241
  tag: c.String,
242
242
  executionId: c.String,
243
- status: T,
243
+ status: D,
244
244
  payload: c.Unknown,
245
245
  workflowVersion: c.NullOr(c.String),
246
246
  workflowPatches: c.NullOr(c.Unknown),
@@ -255,12 +255,12 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
255
255
  durationMs: c.NullOr(c.Number),
256
256
  traceId: c.NullOr(c.String),
257
257
  parentExecutionId: c.NullOr(c.String),
258
- parentClosePolicy: c.NullOr(E)
259
- }), O = c.Struct({
258
+ parentClosePolicy: c.NullOr(O)
259
+ }), A = c.Struct({
260
260
  tag: c.optional(c.String),
261
- status: c.optional(T),
261
+ status: c.optional(D),
262
262
  limit: c.optional(c.Number)
263
- }), k = c.Struct({
263
+ }), j = c.Struct({
264
264
  id: c.String,
265
265
  runId: c.String,
266
266
  stepName: c.String,
@@ -275,7 +275,7 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
275
275
  startedAt: c.Date,
276
276
  completedAt: c.NullOr(c.Date),
277
277
  durationMs: c.NullOr(c.Number)
278
- }), A = c.Struct({
278
+ }), M = c.Struct({
279
279
  id: c.String,
280
280
  runId: c.String,
281
281
  eventType: c.String,
@@ -283,7 +283,7 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
283
283
  occurredAt: c.Date,
284
284
  stepName: c.NullOr(c.String),
285
285
  attempt: c.NullOr(c.Number)
286
- }), j = c.Struct({
286
+ }), N = c.Struct({
287
287
  id: c.String,
288
288
  name: c.String,
289
289
  payload: c.Unknown,
@@ -291,7 +291,7 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
291
291
  subject: c.NullOr(c.Unknown),
292
292
  traceId: c.NullOr(c.String),
293
293
  occurredAt: c.Date
294
- }), M = c.Struct({
294
+ }), P = c.Struct({
295
295
  id: c.String,
296
296
  eventId: c.String,
297
297
  eventName: c.String,
@@ -304,123 +304,123 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
304
304
  errorMessage: c.NullOr(c.String),
305
305
  createdAt: c.Date,
306
306
  completedAt: c.NullOr(c.Date)
307
- }), N = c.Struct({ id: c.String }), P = c.Struct({ runId: c.String }), F = c.Struct({
307
+ }), F = c.Struct({ id: c.String }), I = c.Struct({ runId: c.String }), L = c.Struct({
308
308
  name: c.optional(c.String),
309
309
  limit: c.optional(c.Number)
310
- }), I = c.Struct({ eventId: c.String }), L = c.Struct({
310
+ }), R = c.Struct({ eventId: c.String }), z = c.Struct({
311
311
  workflowName: c.String,
312
312
  executionId: c.String
313
- }), R = c.Struct({
313
+ }), Ze = c.Struct({
314
314
  id: c.String,
315
315
  signalName: c.String,
316
316
  payload: c.optional(c.Unknown)
317
- }), z = c.Struct({
317
+ }), B = c.Struct({
318
318
  id: c.String,
319
319
  updateName: c.String,
320
320
  payload: c.optional(c.Unknown),
321
321
  timeoutMs: c.optional(c.Number)
322
- }), B = c.Struct({
322
+ }), V = c.Struct({
323
323
  eventId: c.String,
324
324
  updateId: c.String,
325
325
  completedEventId: c.String,
326
326
  result: c.Unknown
327
- }), Ye = g({
327
+ }), Qe = g({
328
328
  name: "__voltro.workflow.run",
329
329
  source: "_voltro_workflow_runs",
330
- input: N,
331
- output: c.Array(D)
332
- }), Xe = g({
330
+ input: F,
331
+ output: c.Array(k)
332
+ }), $e = g({
333
333
  name: "__voltro.workflow.runs",
334
334
  source: "_voltro_workflow_runs",
335
- input: O,
336
- output: c.Array(D)
337
- }), Ze = g({
335
+ input: A,
336
+ output: c.Array(k)
337
+ }), et = g({
338
338
  name: "__voltro.workflow.run.steps",
339
339
  source: "_voltro_workflow_run_steps",
340
- input: P,
341
- output: c.Array(k)
342
- }), Qe = g({
340
+ input: I,
341
+ output: c.Array(j)
342
+ }), tt = g({
343
343
  name: "__voltro.workflow.run.events",
344
344
  source: "_voltro_workflow_run_events",
345
- input: P,
346
- output: c.Array(A)
347
- }), $e = g({
345
+ input: I,
346
+ output: c.Array(M)
347
+ }), nt = g({
348
348
  name: "__voltro.workflow.domainEvents",
349
349
  source: "_voltro_workflow_events",
350
- input: F,
351
- output: c.Array(j)
352
- }), et = g({
350
+ input: L,
351
+ output: c.Array(N)
352
+ }), rt = g({
353
353
  name: "__voltro.workflow.event.deliveries",
354
354
  source: "_voltro_workflow_event_deliveries",
355
- input: I,
356
- output: c.Array(M)
357
- }), tt = v({
355
+ input: R,
356
+ output: c.Array(P)
357
+ }), it = v({
358
358
  name: "__voltro.workflow.cancel",
359
- input: L,
359
+ input: z,
360
360
  output: c.Struct({ ok: c.Boolean })
361
- }), nt = v({
361
+ }), at = v({
362
362
  name: "__voltro.workflow.resume",
363
- input: L,
363
+ input: z,
364
364
  output: c.Struct({ ok: c.Boolean })
365
- }), rt = v({
365
+ }), ot = v({
366
366
  name: "__voltro.workflow.signal",
367
- input: R,
367
+ input: Ze,
368
368
  output: c.Struct({ eventId: c.String })
369
- }), it = v({
369
+ }), st = v({
370
370
  name: "__voltro.workflow.update",
371
- input: z,
372
- output: B
373
- }), at = "__voltro.undo.log", V = "__voltro.undo.apply", H = "__voltro.undo.redo", U = c.Struct({
371
+ input: B,
372
+ output: V
373
+ }), H = "__voltro.undo.log", U = "__voltro.undo.apply", W = "__voltro.undo.redo", G = c.Struct({
374
374
  id: c.String,
375
375
  tag: c.String,
376
376
  label: c.NullOr(c.String),
377
377
  undone: c.Boolean,
378
378
  crossesAction: c.Boolean,
379
379
  createdAt: c.String
380
- }), W = class extends c.TaggedError()("UndoNotFound", { invocationId: c.String }) {}, G = class extends c.TaggedError()("UndoForbidden", { invocationId: c.String }) {}, K = class extends c.TaggedError()("UndoConflict", {
380
+ }), K = class extends c.TaggedError()("UndoNotFound", { invocationId: c.String }) {}, q = class extends c.TaggedError()("UndoForbidden", { invocationId: c.String }) {}, J = class extends c.TaggedError()("UndoConflict", {
381
381
  invocationId: c.String,
382
382
  reason: c.Literal("conflict", "action")
383
- }) {}, q = c.Union(W, G, K), ot = g({
384
- name: at,
383
+ }) {}, Y = c.Union(K, q, J), ct = g({
384
+ name: H,
385
385
  source: "_voltro_undo_log",
386
386
  input: c.Struct({ limit: c.optional(c.Number) }),
387
- output: c.Array(U)
388
- }), st = _({
389
- name: V,
387
+ output: c.Array(G)
388
+ }), lt = _({
389
+ name: U,
390
390
  input: c.Struct({ invocationId: c.String }),
391
391
  output: c.Struct({ ok: c.Boolean }),
392
- error: q
393
- }), ct = _({
394
- name: H,
392
+ error: Y
393
+ }), ut = _({
394
+ name: W,
395
395
  input: c.Struct({ invocationId: c.String }),
396
396
  output: c.Struct({ ok: c.Boolean }),
397
- error: q
398
- }), J = "__voltro.connections.list", Y = "__voltro.connections.start", X = "__voltro.connections.submitToken", Z = "__voltro.connections.disconnect", Q = c.Literal("oauth2", "pat"), lt = c.Literal("disconnected", "connected", "expired", "revoked", "error"), ut = c.Struct({
397
+ error: Y
398
+ }), X = "__voltro.connections.list", dt = "__voltro.connections.start", ft = "__voltro.connections.submitToken", pt = "__voltro.connections.disconnect", Z = c.Literal("oauth2", "pat"), mt = c.Literal("disconnected", "connected", "expired", "revoked", "error"), ht = c.Struct({
399
399
  connectionId: c.String,
400
- kind: Q,
400
+ kind: Z,
401
401
  label: c.String,
402
- status: lt,
402
+ status: mt,
403
403
  accountId: c.NullOr(c.String),
404
404
  accountLabel: c.NullOr(c.String),
405
405
  scopes: c.Array(c.String),
406
406
  expiresAt: c.NullOr(c.String),
407
407
  lastError: c.NullOr(c.String),
408
408
  connectedAt: c.NullOr(c.String)
409
- }), dt = class extends c.TaggedError()("ConnectionNotDeclared", { connectionId: c.String }) {}, ft = class extends c.TaggedError()("ConnectionSubjectRequired", { connectionId: c.String }) {}, pt = class extends c.TaggedError()("ConnectionKindMismatch", {
409
+ }), gt = class extends c.TaggedError()("ConnectionNotDeclared", { connectionId: c.String }) {}, _t = class extends c.TaggedError()("ConnectionSubjectRequired", { connectionId: c.String }) {}, vt = class extends c.TaggedError()("ConnectionKindMismatch", {
410
410
  connectionId: c.String,
411
- expected: Q,
412
- actual: Q
413
- }) {}, mt = class extends c.TaggedError()("ConnectionHandshakeFailed", {
411
+ expected: Z,
412
+ actual: Z
413
+ }) {}, Q = class extends c.TaggedError()("ConnectionHandshakeFailed", {
414
414
  connectionId: c.String,
415
415
  reason: c.String,
416
416
  transient: c.Boolean
417
- }) {}, $ = c.Union(dt, ft, pt, mt), ht = g({
418
- name: J,
417
+ }) {}, $ = c.Union(gt, _t, vt, Q), yt = g({
418
+ name: X,
419
419
  source: "_voltro_connections",
420
420
  input: c.Struct({}),
421
- output: c.Array(ut)
422
- }), gt = v({
423
- name: Y,
421
+ output: c.Array(ht)
422
+ }), bt = v({
423
+ name: dt,
424
424
  input: c.Struct({
425
425
  connectionId: c.String,
426
426
  redirectTo: c.optional(c.String)
@@ -430,22 +430,22 @@ var Ae = c.Union(c.String, c.Number), u = c.Record({
430
430
  state: c.String
431
431
  }),
432
432
  error: $
433
- }), _t = _({
434
- name: X,
433
+ }), xt = _({
434
+ name: ft,
435
435
  input: c.Struct({
436
436
  connectionId: c.String,
437
437
  token: c.String
438
438
  }),
439
439
  output: c.Struct({ ok: c.Boolean }),
440
440
  error: $
441
- }), vt = _({
442
- name: Z,
441
+ }), St = _({
442
+ name: pt,
443
443
  input: c.Struct({ connectionId: c.String }),
444
444
  output: c.Struct({ ok: c.Boolean }),
445
445
  error: $
446
- }), yt = (e) => {
446
+ }), Ct = (e) => {
447
447
  let t = e instanceof Date ? e.getTime() : typeof e == "number" ? e : typeof e == "string" ? new Date(e).getTime() : 0;
448
448
  return Number.isNaN(t) ? 0 : t;
449
- }, bt = 1;
449
+ }, wt = 1;
450
450
  //#endregion
451
- export { a as ADMIN_SCOPE, Ee as AuthMiddleware, J as CONNECTIONS_LIST_TAG, Z as CONNECTION_DISCONNECT_TAG, Y as CONNECTION_START_TAG, X as CONNECTION_SUBMIT_TOKEN_TAG, mt as ConnectionHandshakeFailed, Se as ConnectionInfo, we as ConnectionInfoMiddleware, Q as ConnectionKind, pt as ConnectionKindMismatch, dt as ConnectionNotDeclared, ut as ConnectionState, lt as ConnectionStatus, ft as ConnectionSubjectRequired, bt as PROTOCOL_VERSION, s as ScopeError, be as Subject, ge as SubjectService, V as UNDO_APPLY_TAG, at as UNDO_LOG_TAG, H as UNDO_REDO_TAG, Ce as Unauthenticated, K as UndoConflict, G as UndoForbidden, U as UndoLogEntry, W as UndoNotFound, L as WorkflowControlInputSchema, j as WorkflowDomainEventRowSchema, F as WorkflowDomainEventsInputSchema, I as WorkflowEventDeliveriesInputSchema, M as WorkflowEventDeliveryRowSchema, E as WorkflowParentClosePolicySchema, A as WorkflowRunEventRowSchema, Je as WorkflowRunHandleSchema, N as WorkflowRunRefSchema, D as WorkflowRunRowSchema, T as WorkflowRunStatusSchema, k as WorkflowRunStepRowSchema, P as WorkflowRunTableRefSchema, O as WorkflowRunsInputSchema, R as WorkflowSignalInputSchema, z as WorkflowUpdateInputSchema, B as WorkflowUpdateResultSchema, ze as actionToRpc, de as advisoryResourceGuardWarning, Te as anonymousSubject, Fe as applyRowPatch, _e as assertAuthenticated, ce as beginIdempotent, Ke as checkFrameworkCompat, o as checkGuards, ee as checkGuardsEffect, xe as composeAuthStrategies, We as composeRpcInterceptors, vt as connectionDisconnectDescriptor, gt as connectionStartDescriptor, _t as connectionSubmitTokenDescriptor, ht as connectionsListQueryDescriptor, v as defineAction, _ as defineMutation, He as definePlugin, Ue as definePluginRoute, Ge as definePluginService, g as defineQuery, Le as defineStream, Ne as diffRows, se as effectiveScopes, re as failIdempotent, ie as findAdvisoryResourceGuards, r as finishIdempotent, ne as getPolicyGuardResolver, te as getResourceScopeResolver, De as hasCallbackRoutes, n as hasEffectiveScope, fe as hasScope, p as idToPath, oe as idempotencyScope, Pe as isIdKeyed, he as isPolicyCheck, Ie as isPolicyGuard, ve as isSystemSubject, le as memoryIdempotencyStore, Re as mutationToRpc, Ve as normalizeDescriptor, je as pathToId, ue as publishServerError, x as queryToRpc, i as requireScope, d as rowPatchOpSchema, f as rowPatchSchema, me as setEffectiveScopes, t as setPolicyGuardResolver, e as setResourceScopeResolver, S as streamToRpc, pe as subjectScopes, ae as subscribeServerErrors, h as subscriptionEvent, ye as systemSubject, Be as toRpc, yt as tsMs, st as undoApplyDescriptor, ot as undoLogQueryDescriptor, ct as undoRedoDescriptor, tt as workflowCancelDescriptor, $e as workflowDomainEventsQueryDescriptor, et as workflowEventDeliveriesQueryDescriptor, nt as workflowResumeDescriptor, Qe as workflowRunEventsQueryDescriptor, Ye as workflowRunQueryDescriptor, Ze as workflowRunStepsQueryDescriptor, Xe as workflowRunsQueryDescriptor, rt as workflowSignalDescriptor, it as workflowUpdateDescriptor };
451
+ export { te as ADMIN_SCOPE, oe as APIKEY_ISSUE_ORG_SCOPE, me as APIKEY_ISSUE_OTHER_SCOPE, ne as APIKEY_ISSUE_SELF_SCOPE, Ae as AuthMiddleware, X as CONNECTIONS_LIST_TAG, pt as CONNECTION_DISCONNECT_TAG, dt as CONNECTION_START_TAG, ft as CONNECTION_SUBMIT_TOKEN_TAG, Q as ConnectionHandshakeFailed, Te as ConnectionInfo, Oe as ConnectionInfoMiddleware, Z as ConnectionKind, vt as ConnectionKindMismatch, gt as ConnectionNotDeclared, ht as ConnectionState, mt as ConnectionStatus, _t as ConnectionSubjectRequired, wt as PROTOCOL_VERSION, s as ScopeError, Ce as Subject, ye as SubjectService, U as UNDO_APPLY_TAG, H as UNDO_LOG_TAG, W as UNDO_REDO_TAG, Ee as Unauthenticated, J as UndoConflict, q as UndoForbidden, G as UndoLogEntry, K as UndoNotFound, z as WorkflowControlInputSchema, N as WorkflowDomainEventRowSchema, L as WorkflowDomainEventsInputSchema, R as WorkflowEventDeliveriesInputSchema, P as WorkflowEventDeliveryRowSchema, O as WorkflowParentClosePolicySchema, M as WorkflowRunEventRowSchema, Xe as WorkflowRunHandleSchema, F as WorkflowRunRefSchema, k as WorkflowRunRowSchema, D as WorkflowRunStatusSchema, j as WorkflowRunStepRowSchema, I as WorkflowRunTableRefSchema, A as WorkflowRunsInputSchema, Ze as WorkflowSignalInputSchema, B as WorkflowUpdateInputSchema, V as WorkflowUpdateResultSchema, C as actionToRpc, ue as advisoryResourceGuardWarning, ke as anonymousSubject, ze as applyRowPatch, be as assertAuthenticated, de as beginIdempotent, Je as checkFrameworkCompat, se as checkGuards, ie as checkGuardsEffect, we as composeAuthStrategies, Ke as composeRpcInterceptors, St as connectionDisconnectDescriptor, bt as connectionStartDescriptor, xt as connectionSubmitTokenDescriptor, yt as connectionsListQueryDescriptor, v as defineAction, _ as defineMutation, We as definePlugin, Ge as definePluginRoute, qe as definePluginService, g as defineQuery, Ve as defineStream, Le as diffRows, re as effectiveScopes, ae as failIdempotent, a as findAdvisoryResourceGuards, o as finishIdempotent, he as getPolicyGuardResolver, ve as getResourceScopeResolver, je as hasCallbackRoutes, ee as hasEffectiveScope, _e as hasScope, p as idToPath, le as idempotencyScope, Re as isIdKeyed, r as isPolicyCheck, Be as isPolicyGuard, xe as isSystemSubject, fe as memoryIdempotencyStore, S as mutationToRpc, Ue as normalizeDescriptor, Fe as pathToId, pe as publishServerError, x as queryToRpc, e as requireScope, d as rowPatchOpSchema, f as rowPatchSchema, ge as setEffectiveScopes, i as setPolicyGuardResolver, n as setResourceScopeResolver, w as streamToRpc, t as subjectScopes, ce as subscribeServerErrors, h as subscriptionEvent, Se as systemSubject, De as tenantScopedSubject, He as toRpc, Ct as tsMs, lt as undoApplyDescriptor, ct as undoLogQueryDescriptor, ut as undoRedoDescriptor, it as workflowCancelDescriptor, nt as workflowDomainEventsQueryDescriptor, rt as workflowEventDeliveriesQueryDescriptor, at as workflowResumeDescriptor, tt as workflowRunEventsQueryDescriptor, Qe as workflowRunQueryDescriptor, et as workflowRunStepsQueryDescriptor, $e as workflowRunsQueryDescriptor, ot as workflowSignalDescriptor, st as workflowUpdateDescriptor };
package/dist/jwt.d.ts CHANGED
@@ -8,6 +8,24 @@ declare interface AuthStrategy {
8
8
  * in logs + `Subject.metadata.provider`. */
9
9
  readonly id: string;
10
10
  readonly resolve: (input: AuthStrategyInput) => Promise<StrategyResolution> | StrategyResolution;
11
+ /**
12
+ * The bearer-token PREFIX this strategy claims, when it gates on one
13
+ * (`'sk_'`, `'awb_'`). Declared so a collision is DETECTABLE.
14
+ *
15
+ * Two strategies claiming the same prefix is not a harmless duplicate: the
16
+ * chain is first-match-wins, so whichever runs first decides the Subject —
17
+ * and if they resolve the same token to different authority, which one
18
+ * answered decides whether authorization works. A downstream app hit exactly
19
+ * this and had to pin a test asserting it never sets `apiKeys: true`, because
20
+ * doing so would append the framework strategy alongside its own on the same
21
+ * `sk_` prefix, with the framework one resolving without the app's team
22
+ * binding.
23
+ *
24
+ * Optional: a cookie or JWKS strategy claims no prefix and omits it. Only
25
+ * what is declared can be checked — a strategy that gates on a prefix without
26
+ * saying so is invisible to the boot check, exactly as before.
27
+ */
28
+ readonly claimsBearerPrefix?: string;
11
29
  }
12
30
 
13
31
  /** Per-call input the framework hands every strategy. */