@voltro/protocol 0.81.0 → 0.83.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.
@@ -118,13 +118,18 @@ var r = (e, t, n) => `${e ?? "(global)"}|${t.toUpperCase()}|${n}`, i = (e, t, n,
118
118
  differing: n.sort(),
119
119
  missingHere: r.sort()
120
120
  };
121
- }, S = /* @__PURE__ */ new Set(), C = (e) => {
122
- for (let t of S) try {
121
+ }, S = (e) => {
122
+ let t = e.error, n = t instanceof Error ? t.name : typeof t?._tag == "string" ? String(t._tag) : "Error", r = [e.name === void 0 ? e.source : `${e.source} '${e.name}'`, ...typeof e.fields?.runId == "string" ? [`run ${e.fields.runId}`] : []].join(", "), i = /* @__PURE__ */ Error(`[message withheld: ${r} is declared encrypted — read it where the run is shown]`);
123
+ i.name = n;
124
+ let a = t instanceof Error && typeof t.stack == "string" ? t.stack : "", o = a.search(/^\s+at /m);
125
+ return i.stack = `${n}: ${i.message}${o === -1 ? "" : `\n${a.slice(o)}`}`, i;
126
+ }, C = /* @__PURE__ */ new Set(), w = (e) => {
127
+ for (let t of C) try {
123
128
  t(e);
124
129
  } catch {}
125
- }, w = (e) => (S.add(e), () => {
126
- S.delete(e);
127
- }), T = {
130
+ }, T = (e) => (C.add(e), () => {
131
+ C.delete(e);
132
+ }), E = {
128
133
  rpc: {
129
134
  name: "rpc",
130
135
  dates: "epoch-ms",
@@ -158,16 +163,16 @@ var r = (e, t, n) => `${e ?? "(global)"}|${t.toUpperCase()}|${n}`, i = (e, t, n,
158
163
  naming: "camelCase",
159
164
  etag: !0
160
165
  }
161
- }, E = [
166
+ }, D = [
162
167
  "rpc",
163
168
  "standard",
164
169
  "jsonapi"
165
- ], D = (e, t = void 0) => {
170
+ ], O = (e, t = void 0) => {
166
171
  let n = (e) => {
167
172
  if (e?.profile !== void 0) {
168
173
  if (typeof e.profile == "string") {
169
- let t = T[e.profile];
170
- if (t === void 0) throw Error(`publicApi.profile: unknown profile '${String(e.profile)}' — one of ${E.join(", ")}, or a RestFormat object`);
174
+ let t = E[e.profile];
175
+ if (t === void 0) throw Error(`publicApi.profile: unknown profile '${String(e.profile)}' — one of ${D.join(", ")}, or a RestFormat object`);
171
176
  return t;
172
177
  }
173
178
  return e.profile;
@@ -182,14 +187,14 @@ var r = (e, t, n) => `${e ?? "(global)"}|${t.toUpperCase()}|${n}`, i = (e, t, n,
182
187
  ...t.cursorParam === void 0 ? {} : { cursorParam: t.cursorParam },
183
188
  ...t.naming === void 0 ? {} : { naming: t.naming },
184
189
  ...t.etag === void 0 ? {} : { etag: t.etag }
185
- }, i = r(n(e) ?? T.rpc, e);
190
+ }, i = r(n(e) ?? E.rpc, e);
186
191
  return r(n(t) ?? i, t);
187
- }, O = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase(), k = (e, t) => {
192
+ }, k = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z])([A-Z][a-z])/g, "$1_$2").toLowerCase(), A = (e, t) => {
188
193
  if (typeof e != "object" || !e || Array.isArray(e)) return e;
189
194
  let n = {};
190
195
  for (let [r, i] of Object.entries(e)) n[t(r)] = i;
191
196
  return n;
192
- }, A = (e) => n.getOrUndefined(t.getIdentifierAnnotation(e)), j = (n, r) => {
197
+ }, j = (e) => n.getOrUndefined(t.getIdentifierAnnotation(e)), M = (n, r) => {
193
198
  if (r.dates === "epoch-ms" && r.naming === "camelCase") return n;
194
199
  let i = /* @__PURE__ */ new Map(), a = (e) => {
195
200
  let t = i.get(e);
@@ -199,7 +204,7 @@ var r = (e, t, n) => `${e ?? "(global)"}|${t.toUpperCase()}|${n}`, i = (e, t, n,
199
204
  }, o = (n) => {
200
205
  switch (n._tag) {
201
206
  case "Transformation":
202
- if (r.dates === "iso" && A(n) === "DateFromNumber") {
207
+ if (r.dates === "iso" && j(n) === "DateFromNumber") {
203
208
  let r = e.DateFromString.ast;
204
209
  return new t.Transformation(r.from, r.to, r.transformation, {
205
210
  ...r.annotations,
@@ -219,41 +224,41 @@ var r = (e, t, n) => `${e ?? "(global)"}|${t.toUpperCase()}|${n}`, i = (e, t, n,
219
224
  case "TypeLiteral": {
220
225
  let i = n.propertySignatures.map((e) => new t.PropertySignature(e.name, a(e.type), e.isOptional, e.isReadonly, e.annotations)), o = n.indexSignatures.map((e) => new t.IndexSignature(e.parameter, a(e.type), e.isReadonly)), s = new t.TypeLiteral(i, o, n.annotations);
221
226
  if (r.naming !== "snake_case") return s;
222
- let c = i.map((e) => typeof e.name == "string" ? O(e.name) : e.name);
227
+ let c = i.map((e) => typeof e.name == "string" ? k(e.name) : e.name);
223
228
  if (c.every((e, t) => e === i[t].name)) return s;
224
229
  let l = new t.TypeLiteral(i.map((e, n) => new t.PropertySignature(c[n], t.encodedAST(e.type), e.isOptional, e.isReadonly, e.annotations)), o.map((e) => new t.IndexSignature(e.parameter, t.encodedAST(e.type), e.isReadonly)), n.annotations), u = new Map(i.map((e, t) => [String(c[t]), String(e.name)])), d = new Map(i.map((e, t) => [String(e.name), String(c[t])]));
225
230
  return e.transform(e.make(l), e.make(s), {
226
231
  strict: !1,
227
- decode: (e) => k(e, (e) => u.get(e) ?? e),
228
- encode: (e) => k(e, (e) => d.get(e) ?? e)
232
+ decode: (e) => A(e, (e) => u.get(e) ?? e),
233
+ encode: (e) => A(e, (e) => d.get(e) ?? e)
229
234
  }).ast;
230
235
  }
231
236
  default: return n;
232
237
  }
233
238
  };
234
239
  return e.make(a(n.ast));
235
- }, M = (e) => `urn:voltro:error:${e}`, N = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2"), P = (e) => ({
236
- type: M(e.tag),
237
- title: N(e.tag),
240
+ }, N = (e) => `urn:voltro:error:${e}`, P = (e) => e.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2"), F = (e) => ({
241
+ type: N(e.tag),
242
+ title: P(e.tag),
238
243
  status: e.status,
239
244
  ...e.message.length > 0 ? { detail: e.message } : {},
240
245
  ...e.fields
241
- }), F = "application/problem+json; charset=utf-8", I = "application/vnd.api+json; charset=utf-8", L = (e, t, n) => e.encodeError === void 0 ? e.errors === "problem-details" ? e.envelope === "jsonapi" ? {
246
+ }), I = "application/problem+json; charset=utf-8", L = "application/vnd.api+json; charset=utf-8", R = (e, t, n) => e.encodeError === void 0 ? e.errors === "problem-details" ? e.envelope === "jsonapi" ? {
242
247
  body: { errors: [{
243
248
  status: String(t.status),
244
249
  code: t.tag,
245
- title: N(t.tag),
250
+ title: P(t.tag),
246
251
  ...t.message.length > 0 ? { detail: t.message } : {},
247
252
  ...Object.keys(t.fields).length > 0 ? { meta: t.fields } : {}
248
253
  }] },
249
- contentType: I
254
+ contentType: L
250
255
  } : {
251
- body: P(t),
252
- contentType: F
256
+ body: F(t),
257
+ contentType: I
253
258
  } : { body: {
254
259
  error: t.tag === "InvalidRequest" || t.tag === "InternalServerError" || t.tag === "Unauthorized" || t.tag === "Forbidden" || t.tag === "Gone" || t.tag === "MethodNotAllowed" || t.tag === "Conflict" ? t.message : t.tag,
255
260
  ...t.fields
256
- } } : e.encodeError(t, n), R = (e, t) => {
261
+ } } : e.encodeError(t, n), z = (e, t) => {
257
262
  if (typeof e != "object" || !e || Array.isArray(e)) return {
258
263
  cursor: void 0,
259
264
  rest: e
@@ -266,10 +271,10 @@ var r = (e, t, n) => `${e ?? "(global)"}|${t.toUpperCase()}|${n}`, i = (e, t, n,
266
271
  cursor: String(n),
267
272
  rest: e
268
273
  };
269
- }, z = (e, t, n) => {
274
+ }, B = (e, t, n) => {
270
275
  let r = new URLSearchParams(e.query);
271
276
  return r.set(n, t), `<${e.path}?${r.toString()}>; rel="next"`;
272
- }, B = (e, t) => {
277
+ }, V = (e, t) => {
273
278
  if (typeof e != "object" || !e || Array.isArray(e)) return e;
274
279
  let { id: n, ...r } = e;
275
280
  return {
@@ -277,9 +282,9 @@ var r = (e, t, n) => `${e ?? "(global)"}|${t.toUpperCase()}|${n}`, i = (e, t, n,
277
282
  ...n === void 0 ? {} : { id: String(n) },
278
283
  attributes: r
279
284
  };
280
- }, V = (e, t, n) => {
285
+ }, H = (e, t, n) => {
281
286
  if (e.encodeSuccess !== void 0) return e.encodeSuccess(t, n);
282
- let { cursor: r } = R(t, e.cursorField), i = e.pagination === "link-header" && r !== void 0 ? { Link: z(n, r, e.cursorParam) } : void 0, a = (e, t) => ({
287
+ let { cursor: r } = z(t, e.cursorField), i = e.pagination === "link-header" && r !== void 0 ? { Link: B(n, r, e.cursorParam) } : void 0, a = (e, t) => ({
283
288
  body: e,
284
289
  ...i ? { headers: i } : {},
285
290
  ...t ? { contentType: t } : {}
@@ -291,9 +296,9 @@ var r = (e, t, n) => `${e ?? "(global)"}|${t.toUpperCase()}|${n}`, i = (e, t, n,
291
296
  });
292
297
  let o = n.source ?? n.tag.split(".")[0] ?? "resource", s = Array.isArray(t) ? t : typeof t == "object" && t && Array.isArray(t.items) ? t.items : void 0;
293
298
  return a({
294
- data: s === void 0 ? B(t, o) : s.map((e) => B(e, o)),
295
- ...r === void 0 ? {} : { links: { next: z(n, r, e.cursorParam).replace(/^<|>; rel="next"$/g, "") } }
296
- }, I);
299
+ data: s === void 0 ? V(t, o) : s.map((e) => V(e, o)),
300
+ ...r === void 0 ? {} : { links: { next: B(n, r, e.cursorParam).replace(/^<|>; rel="next"$/g, "") } }
301
+ }, L);
297
302
  };
298
303
  //#endregion
299
- export { i as A, o as C, c as D, f as E, r as O, p as S, l as T, _, V as a, m as b, M as c, O as d, C as f, y as g, b as h, L as i, d as k, D as l, x as m, F as n, z as o, w as p, E as r, P as s, I as t, j as u, v, u as w, a as x, s as y };
304
+ export { d as A, p as C, f as D, l as E, c as O, a as S, u as T, y as _, H as a, s as b, N as c, k as d, w as f, b as g, x as h, R as i, i as j, r as k, O as l, S as m, I as n, B as o, T as p, D as r, F as s, L as t, M as u, _ as v, o as w, m as x, v as y };
@@ -70,55 +70,55 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
70
70
  tenantId: n.Null,
71
71
  scopes: c,
72
72
  metadata: n.optional(s)
73
- }), ee = n.Union(l, u, d, f, p, m), te = n.Union(l.omit("scopes"), u.omit("scopes"), d.omit("scopes"), f, p.omit("scopes"), m.omit("scopes")), h = (e) => {
73
+ }), ee = n.Union(l, u, d, f, p, m), te = n.Union(l.omit("scopes"), u.omit("scopes"), d.omit("scopes"), f, p.omit("scopes"), m.omit("scopes")), ne = (e) => {
74
74
  if (e.type === "anonymous") return e;
75
75
  let { scopes: t, ...n } = e;
76
76
  return n;
77
- }, g = (e) => e.scopes ?? [], _ = (e, t) => ({
77
+ }, h = (e) => e.scopes ?? [], g = (e, t) => ({
78
78
  type: "anonymous",
79
79
  id: null,
80
80
  tenantId: e,
81
81
  ...t === void 0 ? {} : { credentialRejected: t }
82
- }), ne = (e, t = ["admin:full"]) => ({
82
+ }), re = (e, t = ["admin:full"]) => ({
83
83
  type: "system",
84
84
  id: e,
85
85
  tenantId: null,
86
86
  scopes: t
87
- }), re = (e) => ({
87
+ }), ie = (e) => ({
88
88
  type: "service",
89
89
  id: `service:${e}`,
90
90
  service: e,
91
91
  tenantId: null,
92
92
  scopes: []
93
- }), ie = (e) => e.type === "service", ae = (e) => e.type === "system" || e.type === "service", v = (e) => {
93
+ }), ae = (e) => e.type === "service", oe = (e) => e.type === "system" || e.type === "service", _ = (e) => {
94
94
  if (e.type === "system") return null;
95
95
  if (e.type === "serviceAccount" && e.auditActorId !== void 0) return e.auditActorId;
96
96
  if (e.type !== "apiKey") return e.id;
97
97
  let t = e.metadata?.userId;
98
98
  return typeof t == "string" && t !== "" ? t : e.id;
99
- }, y = (e, t) => {
99
+ }, v = (e, t) => {
100
100
  let n = e;
101
101
  return {
102
102
  type: "serviceAccount",
103
103
  id: e.id ?? "system",
104
104
  tenantId: t,
105
- auditActorId: v(e),
106
- scopes: g(e),
105
+ auditActorId: _(e),
106
+ scopes: h(e),
107
107
  ...n.metadata === void 0 ? {} : { metadata: n.metadata }
108
108
  };
109
- }, b = (e) => e.type === "system", oe = "impersonation", se = (e) => e.metadata?.impersonation ?? null, x = class extends e.Tag("@voltro/Subject")() {}, S = class extends e.Tag("@voltro/ConnectionInfo")() {}, C = class extends n.TaggedError()("Unauthenticated", { reason: n.optional(n.String) }) {}, ce = class extends r.Tag()("@voltro/AuthMiddleware", {
110
- provides: x,
111
- failure: C
112
- }) {}, le = class extends r.Tag()("@voltro/ConnectionInfoMiddleware", { provides: S }) {}, ue = (e) => typeof e.mountRoutes == "function", de = (e) => Array.isArray(e) ? {
109
+ }, se = (e) => e.type === "system", ce = "impersonation", le = (e) => e.metadata?.impersonation ?? null, y = class extends e.Tag("@voltro/Subject")() {}, b = class extends e.Tag("@voltro/ConnectionInfo")() {}, x = class extends n.TaggedError()("Unauthenticated", { reason: n.optional(n.String) }) {}, ue = class extends r.Tag()("@voltro/AuthMiddleware", {
110
+ provides: y,
111
+ failure: x
112
+ }) {}, de = class extends r.Tag()("@voltro/ConnectionInfoMiddleware", { provides: b }) {}, fe = (e) => typeof e.mountRoutes == "function", pe = (e) => Array.isArray(e) ? {
113
113
  kind: "grant",
114
114
  scopes: e
115
- } : e, w = (e, t) => {
116
- if (t.kind === "vetoed") throw new C({ reason: `${t.code}: ${t.message}` });
115
+ } : e, S = (e, t) => {
116
+ if (t.kind === "vetoed") throw new x({ reason: `${t.code}: ${t.message}` });
117
117
  if (e.type === "anonymous") return {
118
118
  subject: e,
119
119
  removed: []
120
120
  };
121
- let n = g(e);
121
+ let n = h(e);
122
122
  if (t.kind === "grant") {
123
123
  if (t.scopes.length === 0) return {
124
124
  subject: e,
@@ -141,14 +141,14 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
141
141
  },
142
142
  removed: i
143
143
  };
144
- }, fe = 3e4, pe = 1e4, T = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, me = () => {
144
+ }, me = 3e4, he = 1e4, C = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, ge = () => {
145
145
  if (typeof process > "u") return;
146
146
  let e = process.env.VOLTRO_AUTH_SCOPE_CACHE_TTL_MS;
147
147
  if (e === void 0 || e === "") return;
148
148
  let t = Number(e);
149
149
  return Number.isFinite(t) && t >= 0 ? t : void 0;
150
- }, E = (e = {}) => {
151
- let t = e.ttlMs ?? me() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
150
+ }, w = (e = {}) => {
151
+ let t = e.ttlMs ?? ge() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
152
152
  if (i.size <= n) return;
153
153
  let e = r();
154
154
  for (let [t, n] of i) n.freshUntil <= e && i.delete(t);
@@ -184,10 +184,10 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
184
184
  };
185
185
  }
186
186
  };
187
- }, he = (e) => typeof e.resolve == "function", ge = (e) => {
187
+ }, _e = (e) => typeof e.resolve == "function", ve = (e) => {
188
188
  let t = e.authorization ?? e.Authorization;
189
189
  if (t !== void 0) return /^Bearer\s+(.+)$/i.exec(t.trim())?.[1]?.trim();
190
- }, _e = async (e, t, n, r) => {
190
+ }, ye = async (e, t, n, r) => {
191
191
  if (!i(e)) return e;
192
192
  let s = {
193
193
  type: e.type,
@@ -216,44 +216,52 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
216
216
  }
217
217
  return e;
218
218
  }
219
- }, D = (e, t) => {
220
- let n = ge(t);
219
+ }, T = (e, t) => {
220
+ let n = ve(t);
221
221
  if (n === void 0) return e;
222
222
  let r = e.filter((e) => e.claimsBearerPrefix !== void 0 && e.claimsBearerPrefix !== "" && n.startsWith(e.claimsBearerPrefix));
223
223
  if (r.length === 0) return e;
224
224
  let i = new Set(r);
225
225
  return [...r, ...e.filter((e) => !i.has(e))];
226
- }, O = (e, t) => {
226
+ }, be = (e, t) => {
227
+ if (e === "refuse") throw new x({ reason: "anonymous callers are refused" });
228
+ if (e === "no-tenant") return "";
229
+ if (e === "header") {
230
+ if (t === void 0 || t === "") throw new x({ reason: "tenant required (x-tenant header missing)" });
231
+ return t;
232
+ }
233
+ return "tenantId" in e ? e.tenantId : t !== void 0 && t !== "" ? t : e.headerOrTenant;
234
+ }, E = (e, t) => {
227
235
  let n = t?.resolveScopes, r = t?.subjectGuards ?? [];
228
236
  if (t?.memberships === void 0 != (t?.tenantSelection === void 0)) throw Error("composeAuthStrategies: `memberships` and `tenantSelection` come together — a tenant selection is verified against the memberships and read from the store; configure both or neither.");
229
237
  let i = t?.memberships !== void 0 && t.tenantSelection !== void 0 ? {
230
238
  memberships: t.memberships,
231
239
  store: t.tenantSelection
232
- } : void 0, a = n !== void 0 || r.length > 0, o = !a || t?.scopeCache === !1 ? null : t?.scopeCache !== void 0 && he(t.scopeCache) ? t.scopeCache : E(t?.scopeCache ?? {});
240
+ } : void 0, a = n !== void 0 || r.length > 0, o = !a || t?.scopeCache === !1 ? null : t?.scopeCache !== void 0 && _e(t.scopeCache) ? t.scopeCache : w(t?.scopeCache ?? {});
233
241
  return async (s) => {
234
242
  let c = t?.getStore?.(), l = c === void 0 ? s : {
235
243
  ...s,
236
244
  store: c
237
245
  }, u;
238
- for (let s of D(e, l.headers)) {
246
+ for (let s of T(e, l.headers)) {
239
247
  let e = await s.resolve(l);
240
248
  if (e.kind === "matched") {
241
249
  if (e.subject.type === "service") return { subject: {
242
250
  ...e.subject,
243
251
  scopes: []
244
252
  } };
245
- let c = i === void 0 ? e.subject : await _e(e.subject, l, i, (e) => (t?.onStrategyFailed?.({
253
+ let c = i === void 0 ? e.subject : await ye(e.subject, l, i, (e) => (t?.onStrategyFailed?.({
246
254
  strategyId: s.id,
247
255
  reason: e
248
- }), new C({ reason: e }))), u = c;
256
+ }), new x({ reason: e }))), u = c;
249
257
  if (a) {
250
258
  let e = async () => {
251
259
  let e = n === void 0 ? {
252
260
  kind: "grant",
253
261
  scopes: []
254
- } : de(await n(c, l));
262
+ } : pe(await n(c, l));
255
263
  if (e.kind === "unavailable" || r.length === 0) return e;
256
- let t = w(c, e).subject, i = {
264
+ let t = S(c, e).subject, i = {
257
265
  origin: "request",
258
266
  headers: l.headers,
259
267
  clientId: l.clientId,
@@ -271,22 +279,22 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
271
279
  }, { decision: i, fresh: a } = o === null ? {
272
280
  decision: await e(),
273
281
  fresh: !0
274
- } : await o.resolve(T(c), e);
282
+ } : await o.resolve(C(c), e);
275
283
  if (i.kind === "vetoed") {
276
284
  let e = `${i.code}: ${i.message}`;
277
285
  throw t?.onStrategyFailed?.({
278
286
  strategyId: s.id,
279
287
  reason: e
280
- }), new C({ reason: e });
288
+ }), new x({ reason: e });
281
289
  }
282
290
  if (i.kind === "unavailable") {
283
291
  let e = `scope resolution unavailable: ${i.reason}`;
284
292
  throw t?.onStrategyFailed?.({
285
293
  strategyId: s.id,
286
294
  reason: e
287
- }), new C({ reason: e });
295
+ }), new x({ reason: e });
288
296
  }
289
- let d = w(c, i);
297
+ let d = S(c, i);
290
298
  a && d.removed.length > 0 && t?.onScopesNarrowed?.({
291
299
  strategyId: s.id,
292
300
  subjectType: c.type,
@@ -315,62 +323,63 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
315
323
  credentialRejected: u
316
324
  } : e };
317
325
  }
318
- if (u !== void 0) throw new C({ reason: `credential rejected: ${u}` });
319
- if (t?.anonymousTenantId !== void 0) return { subject: _(t.anonymousTenantId) };
320
- let d = l.headers["x-tenant"] ?? t?.anonymousTenantDefault ?? null;
321
- if (d === null && t?.anonymousTenantRequired === !0) throw new C({ reason: "tenant required (x-tenant header missing)" });
322
- return { subject: _(d) };
326
+ if (u !== void 0) throw new x({ reason: `credential rejected: ${u}` });
327
+ return { subject: g(be(t?.anonymous ?? "no-tenant", l.headers["x-tenant"])) };
323
328
  };
324
- }, k = (e, t) => {
325
- if (e.type === "anonymous") throw new C(t === void 0 ? {} : { reason: t });
326
- }, A = "admin:full", j = "apikeys:issue:self", M = "apikeys:issue:org", N = "apikeys:issue:other", P = "devices:manage", F = class extends n.TaggedError()("ScopeError", {
329
+ }, D = (e, t) => {
330
+ if (e.type === "anonymous") throw new x(t === void 0 ? {} : { reason: t });
331
+ }, O = "admin:full", k = "apikeys:issue:self", A = "apikeys:issue:org", j = "apikeys:issue:other", M = "devices:manage", N = class extends n.TaggedError()("ScopeError", {
327
332
  required: n.String,
328
333
  message: n.String
329
- }) {}, I = (e) => "scopes" in e && e.scopes ? e.scopes : [], L = /* @__PURE__ */ new WeakMap(), ve = (e, t) => {
330
- typeof e == "object" && e && L.set(e, t);
331
- }, R = (e) => {
334
+ }) {}, P = class extends n.TaggedError()("NoSuchResource", {
335
+ resourceType: n.NullOr(n.String),
336
+ resourceId: n.String,
337
+ message: n.String
338
+ }) {}, F = (e) => "scopes" in e && e.scopes ? e.scopes : [], I = /* @__PURE__ */ new WeakMap(), xe = (e, t) => {
339
+ typeof e == "object" && e && I.set(e, t);
340
+ }, L = (e) => {
332
341
  if (typeof e == "object" && e) {
333
- let t = L.get(e);
342
+ let t = I.get(e);
334
343
  if (t) return t;
335
344
  }
336
- return I(e);
337
- }, z = (e, t) => {
338
- let n = R(e);
345
+ return F(e);
346
+ }, R = (e, t) => {
347
+ let n = L(e);
339
348
  return n.includes("admin:full") || n.includes(t);
340
- }, ye = (e, t) => {
341
- let n = I(e);
349
+ }, Se = (e, t) => {
350
+ let n = F(e);
342
351
  return n.includes("admin:full") || n.includes(t);
343
- }, be = (e, n) => z(e, n) ? t.void : t.fail(new F({
352
+ }, Ce = (e, n) => R(e, n) ? t.void : t.fail(new N({
344
353
  required: n,
345
354
  message: `missing required scope '${n}'`
346
- })), B = (e, t) => {
355
+ })), z = (e, t) => {
347
356
  let n;
348
357
  try {
349
358
  n = typeof e.action == "function" ? e.action(t) : e.action;
350
359
  } catch {
351
- throw new F({
360
+ throw new N({
352
361
  required: e.resourceType,
353
362
  message: "policy action selector failed"
354
363
  });
355
364
  }
356
- if (typeof n != "string" || n.trim() === "") throw new F({
365
+ if (typeof n != "string" || n.trim() === "") throw new N({
357
366
  required: e.resourceType,
358
367
  message: "policy action selector returned no action"
359
368
  });
360
369
  return n;
361
- }, V = (e) => (typeof e.action == "string" || typeof e.action == "function") && typeof e.resourceType == "string", H = (e) => typeof e.open == "string", U = (e) => Array.isArray(e.service), xe = (e) => ({ service: [...e] }), W = (e) => e?.find(U)?.service, G = (e, t) => {
362
- let n = W(t);
363
- if (e.type === "service") return n !== void 0 && n.includes(e.service) ? null : new F({
370
+ }, B = (e) => (typeof e.action == "string" || typeof e.action == "function") && typeof e.resourceType == "string", we = (e) => e !== void 0 && e.some((e) => B(e) || typeof e.resource == "function"), V = (e) => typeof e.open == "string", H = (e) => Array.isArray(e.service), Te = (e) => ({ service: [...e] }), U = (e) => e?.find(H)?.service, W = (e, t) => {
371
+ let n = U(t);
372
+ if (e.type === "service") return n !== void 0 && n.includes(e.service) ? null : new N({
364
373
  required: "service-procedure",
365
374
  message: n === void 0 ? `service '${e.service}' may only call procedures that declare it in \`service: { callers }\`` : `service '${e.service}' is not among this procedure's callers [${n.join(", ")}]`
366
375
  });
367
- if (n !== void 0) return Q(e, new F({
376
+ if (n !== void 0) return Z(e, new N({
368
377
  required: `service:${n.join("|")}`,
369
378
  message: `this procedure is callable only by the service${n.length === 1 ? "" : "s"} [${n.join(", ")}]`
370
379
  }));
371
- }, K = (e) => e.authenticated === !0, Se = (e) => ({ open: e }), q = (e) => typeof e == "string" ? [e] : e, J, Ce = (e) => {
372
- J = e;
373
- }, we = (e) => J === void 0 ? e.filter((e) => (e.guards ?? []).some((e) => !V(e) && e.resource !== void 0)).map((e) => e.tag) : [], Te = (e) => [
380
+ }, G = (e) => e.authenticated === !0, Ee = (e) => ({ open: e }), K = (e) => typeof e == "string" ? [e] : e, q, De = (e) => {
381
+ q = e;
382
+ }, Oe = (e) => q === void 0 ? e.filter((e) => (e.guards ?? []).some((e) => !B(e) && e.resource !== void 0)).map((e) => e.tag) : [], ke = (e) => [
374
383
  `${e.length} procedure${e.length === 1 ? "" : "s"} declare a per-resource guard that nothing enforces:`,
375
384
  ...e.slice(0, 10).map((e) => ` ${e}`),
376
385
  ...e.length > 10 ? [` … and ${e.length - 10} more`] : [],
@@ -384,59 +393,59 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
384
393
  " • setResourceScopeResolver((req) => Effect<boolean>) — answers \"does this",
385
394
  " subject hold `req.scope` on `req.resource`\" from your own tables.",
386
395
  " • Drop the `resource` extractor if a global scope check is what you meant."
387
- ].join("\n"), Ee = () => J, Y, X = (e) => {
388
- Y = e;
389
- }, De = () => Y, Z = (e) => new F({
396
+ ].join("\n"), Ae = () => q, J, je = (e) => {
397
+ J = e;
398
+ }, Y = () => J, X = (e) => new N({
390
399
  required: "access-decision",
391
400
  message: `${e ?? "this procedure"} declares no access decision: it has neither \`guards:\` nor \`openAccess:\`, and the app runs with \`security.defaultDeny\`. Add the scope(s) a caller must hold (\`guards: [{ scope: '…' }]\`), or declare it open on purpose with \`openAccess: '<why anyone may call this>'\`.`
392
- }), Q = (e, t) => {
401
+ }), Z = (e, t) => {
393
402
  let n = e.credentialRejected;
394
- return n === void 0 ? t : new C({ reason: `${n} — the request carried a credential and it was REJECTED, so this caller resolved to anonymous. The guard then refused (${t.message}); the scope is not the problem. Re-authenticate and retry.` });
395
- }, $ = (e, t, n) => {
396
- let r = G(e, t);
403
+ return n === void 0 ? t : new x({ reason: `${n} — the request carried a credential and it was REJECTED, so this caller resolved to anonymous. The guard then refused (${t.message}); the scope is not the problem. Re-authenticate and retry.` });
404
+ }, Q = (e, t, n) => {
405
+ let r = W(e, t);
397
406
  if (r !== void 0) return r;
398
- if (!t || t.length === 0) return n?.defaultDeny === !0 ? Z(n.procedure) : null;
407
+ if (!t || t.length === 0) return n?.defaultDeny === !0 ? X(n.procedure) : null;
399
408
  for (let n of t) {
400
- if (H(n) || U(n)) continue;
401
- if (K(n)) {
409
+ if (V(n) || H(n)) continue;
410
+ if (G(n)) {
402
411
  if (e.id !== null && e.id !== void 0) continue;
403
- return Q(e, new F({
412
+ return Z(e, new N({
404
413
  required: "authenticated",
405
414
  message: "this procedure requires an authenticated caller"
406
415
  }));
407
416
  }
408
- if (V(n)) {
409
- if (z(e, "admin:full")) continue;
410
- return Q(e, new F({
417
+ if (B(n)) {
418
+ if (R(e, "admin:full")) continue;
419
+ return Z(e, new N({
411
420
  required: `${n.resourceType}:${n.action}`,
412
421
  message: `relationship guard '${n.action}' on '${n.resourceType}' cannot be checked synchronously`
413
422
  }));
414
423
  }
415
- let t = q(n.scope);
424
+ let t = K(n.scope);
416
425
  if (t.length === 0) continue;
417
- let r = t.filter((t) => z(e, t));
426
+ let r = t.filter((t) => R(e, t));
418
427
  if (!((n.mode ?? "all") === "any" ? r.length > 0 : r.length === t.length)) {
419
- let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !z(e, t));
420
- return Q(e, new F({
428
+ let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !R(e, t));
429
+ return Z(e, new N({
421
430
  required: r,
422
431
  message: (n.mode ?? "all") === "any" ? `missing all of required scopes [${t.join(", ")}]` : `missing required scope '${r}'`
423
432
  }));
424
433
  }
425
434
  }
426
435
  return null;
427
- }, Oe = (e, n, r) => {
436
+ }, Me = (e, n, r) => {
428
437
  let i;
429
438
  try {
430
- i = B(n, r);
439
+ i = z(n, r);
431
440
  } catch (e) {
432
441
  return t.succeed(e);
433
442
  }
434
- let a = (e) => new F({
443
+ let a = (e) => new N({
435
444
  required: `${n.resourceType}:${i}`,
436
445
  message: e
437
446
  });
438
- if (z(e, "admin:full")) return t.succeed(null);
439
- let o = Y;
447
+ if (R(e, "admin:full")) return t.succeed(null);
448
+ let o = J;
440
449
  if (o === void 0) return t.succeed(a(`no policy resolver registered — cannot authorize '${i}' on '${n.resourceType}'`));
441
450
  let s = n.resource(r);
442
451
  return s === void 0 || s === "" ? t.succeed(a(`input does not identify a '${n.resourceType}' to authorize`)) : t.suspend(() => o({
@@ -444,32 +453,36 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
444
453
  action: i,
445
454
  resourceType: n.resourceType,
446
455
  resourceId: s
447
- })).pipe(t.catchAllCause(() => t.succeed(!1)), t.map((e) => e ? null : a(`no '${i}' relation on ${n.resourceType}:${s}`)));
448
- }, ke = (e, n, r, i) => {
449
- let a = G(e, n);
456
+ })).pipe(t.catchAllCause(() => t.succeed(!1)), t.map((t) => $(e, t, n.resourceType, s, () => a(`no '${i}' relation on ${n.resourceType}:${s}`))));
457
+ }, $ = (e, t, n, r, i) => t === !0 ? null : t === "missing" && e.id !== null && e.id !== void 0 ? new P({
458
+ resourceType: n,
459
+ resourceId: r,
460
+ message: n === null ? `no such resource '${r}'` : `no such ${n} '${r}'`
461
+ }) : i(), Ne = (e, n, r, i) => {
462
+ let a = W(e, n);
450
463
  if (a !== void 0) return t.succeed(a);
451
- if (!n || n.length === 0) return t.succeed($(e, n, i));
452
- let o = J;
453
- return !n.some(V) && (!o || !n.some((e) => e.resource !== void 0)) ? t.succeed($(e, n, i)) : t.gen(function* () {
464
+ if (!n || n.length === 0) return t.succeed(Q(e, n, i));
465
+ let o = q;
466
+ return !n.some(B) && (!o || !n.some((e) => e.resource !== void 0)) ? t.succeed(Q(e, n, i)) : t.gen(function* () {
454
467
  for (let i of n) {
455
- if (H(i) || U(i)) continue;
456
- if (K(i)) {
468
+ if (V(i) || H(i)) continue;
469
+ if (G(i)) {
457
470
  if (e.id !== null && e.id !== void 0) continue;
458
- return Q(e, new F({
471
+ return Z(e, new N({
459
472
  required: "authenticated",
460
473
  message: "this procedure requires an authenticated caller"
461
474
  }));
462
475
  }
463
- if (V(i)) {
464
- let t = yield* Oe(e, i, r);
476
+ if (B(i)) {
477
+ let t = yield* Me(e, i, r);
465
478
  if (t) return t;
466
479
  continue;
467
480
  }
468
- let n = q(i.scope);
481
+ let n = K(i.scope);
469
482
  if (n.length === 0) continue;
470
- let a = i.resource ? i.resource(r) : void 0, s = [];
483
+ let a = i.resource ? i.resource(r) : void 0, s = [], c = !1;
471
484
  for (let r of n) {
472
- if (z(e, r)) {
485
+ if (R(e, r)) {
473
486
  s.push(!0);
474
487
  continue;
475
488
  }
@@ -479,15 +492,22 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
479
492
  scope: r,
480
493
  resource: a
481
494
  })).pipe(t.catchAllCause(() => t.succeed(!1)));
482
- s.push(n);
495
+ n === "missing" && (c = !0), s.push(n === !0);
483
496
  } else s.push(!1);
484
497
  }
485
- let c = i.mode ?? "all";
486
- if (!(c === "any" ? s.some(Boolean) : s.every(Boolean))) {
487
- let e = c === "any" ? n[0] : n[n.findIndex((e, t) => !s[t])];
488
- return new F({
489
- required: e,
490
- message: c === "any" ? `missing all of required scopes [${n.join(", ")}]` : `missing required scope '${e}'` + (a === void 0 ? "" : ` on resource '${a}'`)
498
+ let l = i.mode ?? "all";
499
+ if (!(l === "any" ? s.some(Boolean) : s.every(Boolean))) {
500
+ let t = l === "any" ? n[0] : n[n.findIndex((e, t) => !s[t])];
501
+ if (c && a !== void 0) {
502
+ let n = $(e, "missing", null, a, () => new N({
503
+ required: t,
504
+ message: `missing required scope '${t}' on resource '${a}'`
505
+ }));
506
+ if (n !== null) return n;
507
+ }
508
+ return new N({
509
+ required: t,
510
+ message: l === "any" ? `missing all of required scopes [${n.join(", ")}]` : `missing required scope '${t}'` + (a === void 0 ? "" : ` on resource '${a}'`)
491
511
  });
492
512
  }
493
513
  }
@@ -495,4 +515,4 @@ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAc
495
515
  });
496
516
  };
497
517
  //#endregion
498
- export { v as $, ce as A, _ as B, B as C, X as D, ve as E, oe as F, ae as G, k as H, ee as I, E as J, ie as K, te as L, le as M, pe as N, Ce as O, fe as P, re as Q, x as R, be as S, W as T, O as U, w as V, ue as W, se as X, D as Y, T as Z, H as _, P as a, o as at, Z as b, $ as c, we as d, h as et, De as f, K as g, ye as h, j as i, i as it, S as j, I as k, ke as l, z as m, M as n, y as nt, F as o, Ee as p, b as q, N as r, a as rt, Te as s, A as t, ne as tt, R as u, V as v, xe as w, Se as x, U as y, C as z };
518
+ export { C as $, De as A, y as B, Ee as C, U as D, Te as E, he as F, E as G, g as H, me as I, ae as J, fe as K, ce as L, ue as M, b as N, xe as O, de as P, le as Q, ee as R, X as S, z as T, S as U, x as V, D as W, w as X, se as Y, T as Z, Se as _, M as a, a as at, B as b, ke as c, we as d, ie as et, L as f, R as g, Ae as h, k as i, v as it, F as j, je as k, Q as l, Y as m, A as n, ne as nt, P as o, i as ot, Oe as p, oe as q, j as r, re as rt, N as s, o as st, O as t, _ as tt, Ne as u, G as v, Ce as w, H as x, V as y, te as z };
package/dist/session.d.ts CHANGED
@@ -74,6 +74,14 @@ export declare const MIN_SESSION_SECRET_LENGTH: 32;
74
74
  */
75
75
  export declare const readCookie: (header: string | undefined, name: string) => string | undefined;
76
76
 
77
+ /**
78
+ * The session value this request presents — the `x-voltro-session` header a
79
+ * cookie-less client (React Native, a desktop shell) sets on every rpc frame,
80
+ * or the session cookie a browser carries. See `./sessionHeader.ts` for why
81
+ * it is a dedicated header and why the header wins.
82
+ */
83
+ export declare const readSessionValue: (headers: Readonly<Record<string, string | undefined>>, cookieName?: string) => string | undefined;
84
+
77
85
  /**
78
86
  * Reset the once-per-process warning latch. Tests only — the latch is what
79
87
  * makes the diagnostic readable in production and unobservable in a second
@@ -125,6 +133,9 @@ export declare const resolveSessionSecrets: () => SessionSecrets;
125
133
  */
126
134
  export declare const SESSION_COOKIE_NAME: string;
127
135
 
136
+ /** The request header carrying a framework session value outside a cookie. */
137
+ export declare const SESSION_HEADER_NAME: "x-voltro-session";
138
+
128
139
  /**
129
140
  * The session payload format this build mints and accepts.
130
141
  *