@voltro/protocol 0.71.1 → 0.73.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/inspect.js CHANGED
@@ -129,6 +129,7 @@ var t = e.String.pipe(e.filter((e) => Number.isFinite(Date.parse(e)))), n = e.St
129
129
  voltro_replica: e.replicaId
130
130
  }
131
131
  }))) };
132
+ else if (r.length === 1 && i.length === 1) p = i[0].data;
132
133
  else throw Error("This view must compare observations per replica; its values cannot be safely combined.");
133
134
  return {
134
135
  ...t,
package/dist/rest.js CHANGED
@@ -1,4 +1,4 @@
1
- import { F as e, I as t } from "./scopes-CW9_LbDO.js";
1
+ import { I as e, L as t } from "./scopes-Dj7KCJab.js";
2
2
  import { C as n, S as r, T as i, _ as a, a as o, c as s, d as c, f as l, i as u, l as d, n as f, o as p, r as m, s as h, t as g, u as _, v, w as y } from "./restFormat-ChSVyufA.js";
3
3
  import { Effect as b, ParseResult as x, Schema as S } from "effect";
4
4
  import * as C from "effect/SchemaAST";
@@ -1,79 +1,108 @@
1
1
  import { Context as e, Effect as t, Schema as n } from "effect";
2
2
  import { RpcMiddleware as r } from "@effect/rpc";
3
- //#region src/auth.ts
4
- var i = n.Record({
3
+ //#region src/tenantSelection.ts
4
+ var i = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAccount", a = class extends n.TaggedError()("TenantSelectionRefused", {
5
+ subjectType: n.String,
6
+ subjectId: n.NullOr(n.String),
7
+ tenantId: n.String,
8
+ reason: n.Literal("not-a-member", "subject-has-no-tenant")
9
+ }) {}, o = (e, t, n) => {
10
+ if (!i(e)) throw new a({
11
+ subjectType: e.type,
12
+ subjectId: e.id,
13
+ tenantId: t,
14
+ reason: "subject-has-no-tenant"
15
+ });
16
+ if (!n.some((e) => e.tenantId === t)) throw new a({
17
+ subjectType: e.type,
18
+ subjectId: e.id,
19
+ tenantId: t,
20
+ reason: "not-a-member"
21
+ });
22
+ return {
23
+ ...e,
24
+ tenantId: t,
25
+ metadata: {
26
+ ...e.metadata,
27
+ memberships: n.map((e) => e.role === void 0 ? { tenantId: e.tenantId } : {
28
+ tenantId: e.tenantId,
29
+ role: e.role
30
+ })
31
+ }
32
+ };
33
+ }, s = n.Record({
5
34
  key: n.String,
6
35
  value: n.Unknown
7
- }), a = n.optional(n.Array(n.String)), o = n.Struct({
36
+ }), c = n.optional(n.Array(n.String)), l = n.Struct({
8
37
  type: n.Literal("user"),
9
38
  id: n.String,
10
39
  tenantId: n.String,
11
- scopes: a,
12
- metadata: n.optional(i)
13
- }), s = n.Struct({
40
+ scopes: c,
41
+ metadata: n.optional(s)
42
+ }), u = n.Struct({
14
43
  type: n.Literal("apiKey"),
15
44
  id: n.String,
16
45
  tenantId: n.String,
17
- scopes: a,
18
- metadata: n.optional(i)
19
- }), c = n.Struct({
46
+ scopes: c,
47
+ metadata: n.optional(s)
48
+ }), d = n.Struct({
20
49
  type: n.Literal("serviceAccount"),
21
50
  id: n.String,
22
51
  tenantId: n.String,
23
52
  auditActorId: n.optionalWith(n.NullOr(n.String), { exact: !0 }),
24
- scopes: a,
25
- metadata: n.optional(i)
26
- }), l = n.Struct({
53
+ scopes: c,
54
+ metadata: n.optional(s)
55
+ }), f = n.Struct({
27
56
  type: n.Literal("anonymous"),
28
57
  id: n.Null,
29
58
  tenantId: n.NullOr(n.String),
30
59
  credentialRejected: n.optional(n.String)
31
- }), u = n.Struct({
60
+ }), p = n.Struct({
32
61
  type: n.Literal("system"),
33
62
  id: n.String,
34
63
  tenantId: n.Null,
35
- scopes: a,
36
- metadata: n.optional(i)
37
- }), d = n.Union(o, s, c, l, u), f = n.Union(o.omit("scopes"), s.omit("scopes"), c.omit("scopes"), l, u.omit("scopes")), p = (e) => {
64
+ scopes: c,
65
+ metadata: n.optional(s)
66
+ }), ee = n.Union(l, u, d, f, p), m = n.Union(l.omit("scopes"), u.omit("scopes"), d.omit("scopes"), f, p.omit("scopes")), h = (e) => {
38
67
  if (e.type === "anonymous") return e;
39
68
  let { scopes: t, ...n } = e;
40
69
  return n;
41
- }, m = (e) => e.scopes ?? [], h = (e, t) => ({
70
+ }, g = (e) => e.scopes ?? [], _ = (e, t) => ({
42
71
  type: "anonymous",
43
72
  id: null,
44
73
  tenantId: e,
45
74
  ...t === void 0 ? {} : { credentialRejected: t }
46
- }), g = (e, t = ["admin:full"]) => ({
75
+ }), te = (e, t = ["admin:full"]) => ({
47
76
  type: "system",
48
77
  id: e,
49
78
  tenantId: null,
50
79
  scopes: t
51
- }), _ = (e) => {
80
+ }), v = (e) => {
52
81
  if (e.type === "system") return null;
53
82
  if (e.type === "serviceAccount" && e.auditActorId !== void 0) return e.auditActorId;
54
83
  if (e.type !== "apiKey") return e.id;
55
84
  let t = e.metadata?.userId;
56
85
  return typeof t == "string" && t !== "" ? t : e.id;
57
- }, ee = (e, t) => {
86
+ }, ne = (e, t) => {
58
87
  let n = e;
59
88
  return {
60
89
  type: "serviceAccount",
61
90
  id: e.id ?? "system",
62
91
  tenantId: t,
63
- auditActorId: _(e),
64
- scopes: m(e),
92
+ auditActorId: v(e),
93
+ scopes: g(e),
65
94
  ...n.metadata === void 0 ? {} : { metadata: n.metadata }
66
95
  };
67
- }, te = (e) => e.type === "system", ne = "impersonation", v = (e) => e.metadata?.impersonation ?? null, y = class extends e.Tag("@voltro/Subject")() {}, b = class extends e.Tag("@voltro/ConnectionInfo")() {}, x = class extends r.Tag()("@voltro/AuthMiddleware", { provides: y }) {}, S = class extends r.Tag()("@voltro/ConnectionInfoMiddleware", { provides: b }) {}, C = (e) => typeof e.mountRoutes == "function", w = (e) => Array.isArray(e) ? {
96
+ }, re = (e) => e.type === "system", ie = "impersonation", ae = (e) => e.metadata?.impersonation ?? null, y = class extends e.Tag("@voltro/Subject")() {}, b = class extends e.Tag("@voltro/ConnectionInfo")() {}, oe = class extends r.Tag()("@voltro/AuthMiddleware", { provides: y }) {}, x = class extends r.Tag()("@voltro/ConnectionInfoMiddleware", { provides: b }) {}, S = (e) => typeof e.mountRoutes == "function", se = (e) => Array.isArray(e) ? {
68
97
  kind: "grant",
69
98
  scopes: e
70
- } : e, T = (e, t) => {
71
- if (t.kind === "vetoed") throw new k({ reason: `${t.code}: ${t.message}` });
99
+ } : e, C = (e, t) => {
100
+ if (t.kind === "vetoed") throw new A({ reason: `${t.code}: ${t.message}` });
72
101
  if (e.type === "anonymous") return {
73
102
  subject: e,
74
103
  removed: []
75
104
  };
76
- let n = m(e);
105
+ let n = g(e);
77
106
  if (t.kind === "grant") {
78
107
  if (t.scopes.length === 0) return {
79
108
  subject: e,
@@ -96,14 +125,14 @@ var i = n.Record({
96
125
  },
97
126
  removed: i
98
127
  };
99
- }, re = 3e4, ie = 1e4, E = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, ae = () => {
128
+ }, w = 3e4, T = 1e4, E = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, ce = () => {
100
129
  if (typeof process > "u") return;
101
130
  let e = process.env.VOLTRO_AUTH_SCOPE_CACHE_TTL_MS;
102
131
  if (e === void 0 || e === "") return;
103
132
  let t = Number(e);
104
133
  return Number.isFinite(t) && t >= 0 ? t : void 0;
105
134
  }, D = (e = {}) => {
106
- let t = e.ttlMs ?? ae() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
135
+ let t = e.ttlMs ?? ce() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
107
136
  if (i.size <= n) return;
108
137
  let e = r();
109
138
  for (let [t, n] of i) n.freshUntil <= e && i.delete(t);
@@ -139,138 +168,179 @@ var i = n.Record({
139
168
  };
140
169
  }
141
170
  };
142
- }, oe = (e) => typeof e.resolve == "function", se = (e) => {
171
+ }, le = (e) => typeof e.resolve == "function", ue = (e) => {
143
172
  let t = e.authorization ?? e.Authorization;
144
173
  if (t !== void 0) return /^Bearer\s+(.+)$/i.exec(t.trim())?.[1]?.trim();
174
+ }, de = async (e, t, n, r) => {
175
+ if (!i(e)) return e;
176
+ let s = {
177
+ type: e.type,
178
+ id: e.id
179
+ }, c;
180
+ try {
181
+ c = await n.store.read(s);
182
+ } catch (e) {
183
+ throw r(`tenant selection unavailable: ${e instanceof Error ? e.message : String(e)}`);
184
+ }
185
+ if (c === null || c === e.tenantId) return e;
186
+ let l;
187
+ try {
188
+ l = await n.memberships(e, t);
189
+ } catch (e) {
190
+ throw r(`memberships unavailable: ${e instanceof Error ? e.message : String(e)}`);
191
+ }
192
+ try {
193
+ return o(e, c, l);
194
+ } catch (t) {
195
+ if (!(t instanceof a)) throw t;
196
+ try {
197
+ await n.store.clear(s);
198
+ } catch (e) {
199
+ throw r(`tenant selection unavailable: ${e instanceof Error ? e.message : String(e)}`);
200
+ }
201
+ return e;
202
+ }
145
203
  }, O = (e, t) => {
146
- let n = se(t);
204
+ let n = ue(t);
147
205
  if (n === void 0) return e;
148
206
  let r = e.filter((e) => e.claimsBearerPrefix !== void 0 && e.claimsBearerPrefix !== "" && n.startsWith(e.claimsBearerPrefix));
149
207
  if (r.length === 0) return e;
150
208
  let i = new Set(r);
151
209
  return [...r, ...e.filter((e) => !i.has(e))];
152
- }, ce = (e, t) => {
153
- let n = t?.resolveScopes, r = t?.subjectGuards ?? [], i = n !== void 0 || r.length > 0, a = !i || t?.scopeCache === !1 ? null : t?.scopeCache !== void 0 && oe(t.scopeCache) ? t.scopeCache : D(t?.scopeCache ?? {});
154
- return async (o) => {
155
- let s = t?.getStore?.(), c = s === void 0 ? o : {
156
- ...o,
157
- store: s
158
- }, l;
159
- for (let o of O(e, c.headers)) {
160
- let e = await o.resolve(c);
210
+ }, k = (e, t) => {
211
+ let n = t?.resolveScopes, r = t?.subjectGuards ?? [];
212
+ 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.");
213
+ let i = t?.memberships !== void 0 && t.tenantSelection !== void 0 ? {
214
+ memberships: t.memberships,
215
+ store: t.tenantSelection
216
+ } : void 0, a = n !== void 0 || r.length > 0, o = !a || t?.scopeCache === !1 ? null : t?.scopeCache !== void 0 && le(t.scopeCache) ? t.scopeCache : D(t?.scopeCache ?? {});
217
+ return async (s) => {
218
+ let c = t?.getStore?.(), l = c === void 0 ? s : {
219
+ ...s,
220
+ store: c
221
+ }, u;
222
+ for (let s of O(e, l.headers)) {
223
+ let e = await s.resolve(l);
161
224
  if (e.kind === "matched") {
162
- let s = e.subject;
163
- if (i) {
164
- let i = async () => {
165
- let t = n === void 0 ? {
225
+ let c = i === void 0 ? e.subject : await de(e.subject, l, i, (e) => (t?.onStrategyFailed?.({
226
+ strategyId: s.id,
227
+ reason: e
228
+ }), new A({ reason: e }))), u = c;
229
+ if (a) {
230
+ let e = async () => {
231
+ let e = n === void 0 ? {
166
232
  kind: "grant",
167
233
  scopes: []
168
- } : w(await n(e.subject, c));
169
- if (t.kind === "unavailable" || r.length === 0) return t;
170
- let i = T(e.subject, t).subject, a = {
234
+ } : se(await n(c, l));
235
+ if (e.kind === "unavailable" || r.length === 0) return e;
236
+ let t = C(c, e).subject, i = {
171
237
  origin: "request",
172
- headers: c.headers,
173
- clientId: c.clientId,
174
- store: c.store
238
+ headers: l.headers,
239
+ clientId: l.clientId,
240
+ store: l.store
175
241
  };
176
242
  for (let e of r) {
177
- let t = await e(i, a);
178
- if (!t.ok) return {
243
+ let n = await e(t, i);
244
+ if (!n.ok) return {
179
245
  kind: "vetoed",
180
- code: t.code,
181
- message: t.message
246
+ code: n.code,
247
+ message: n.message
182
248
  };
183
249
  }
184
- return t;
185
- }, { decision: l, fresh: u } = a === null ? {
186
- decision: await i(),
250
+ return e;
251
+ }, { decision: i, fresh: a } = o === null ? {
252
+ decision: await e(),
187
253
  fresh: !0
188
- } : await a.resolve(E(e.subject), i);
189
- if (l.kind === "vetoed") {
190
- let e = `${l.code}: ${l.message}`;
254
+ } : await o.resolve(E(c), e);
255
+ if (i.kind === "vetoed") {
256
+ let e = `${i.code}: ${i.message}`;
191
257
  throw t?.onStrategyFailed?.({
192
- strategyId: o.id,
258
+ strategyId: s.id,
193
259
  reason: e
194
- }), new k({ reason: e });
260
+ }), new A({ reason: e });
195
261
  }
196
- if (l.kind === "unavailable") {
197
- let e = `scope resolution unavailable: ${l.reason}`;
262
+ if (i.kind === "unavailable") {
263
+ let e = `scope resolution unavailable: ${i.reason}`;
198
264
  throw t?.onStrategyFailed?.({
199
- strategyId: o.id,
265
+ strategyId: s.id,
200
266
  reason: e
201
- }), new k({ reason: e });
267
+ }), new A({ reason: e });
202
268
  }
203
- let d = T(e.subject, l);
204
- u && d.removed.length > 0 && t?.onScopesNarrowed?.({
205
- strategyId: o.id,
206
- subjectType: e.subject.type,
207
- subjectId: e.subject.id,
269
+ let d = C(c, i);
270
+ a && d.removed.length > 0 && t?.onScopesNarrowed?.({
271
+ strategyId: s.id,
272
+ subjectType: c.type,
273
+ subjectId: c.id,
208
274
  removed: d.removed,
209
- granted: l.scopes
210
- }), s = d.subject;
275
+ granted: i.scopes
276
+ }), u = d.subject;
211
277
  }
212
- return e.credentialExpiresAt === void 0 ? { subject: s } : {
213
- subject: s,
278
+ return e.credentialExpiresAt === void 0 ? { subject: u } : {
279
+ subject: u,
214
280
  credentialExpiresAt: e.credentialExpiresAt
215
281
  };
216
282
  }
217
283
  if (e.kind === "failed") {
218
284
  t?.onStrategyFailed?.({
219
- strategyId: o.id,
285
+ strategyId: s.id,
220
286
  reason: e.reason
221
- }), l = e.reason;
287
+ }), u = e.reason;
222
288
  break;
223
289
  }
224
290
  }
225
- let u = (e) => l !== void 0 && e.type === "anonymous" ? {
226
- ...e,
227
- credentialRejected: l
228
- } : e;
229
- if (t?.fallback) return { subject: u(await t.fallback(c)) };
230
- let d = c.headers["x-tenant"] ?? null;
231
- if (d === null && t?.anonymousTenantRequired === !0) throw new k({ reason: "tenant required (x-tenant header missing)" });
232
- return { subject: u(h(d)) };
291
+ if (t?.fallback) {
292
+ let e = await t.fallback(l);
293
+ return { subject: u !== void 0 && e.type === "anonymous" ? {
294
+ ...e,
295
+ credentialRejected: u
296
+ } : e };
297
+ }
298
+ if (u !== void 0) throw new A({ reason: `credential rejected: ${u}` });
299
+ if (t?.anonymousTenantId !== void 0) return { subject: _(t.anonymousTenantId) };
300
+ let d = l.headers["x-tenant"] ?? null;
301
+ if (d === null && t?.anonymousTenantRequired === !0) throw new A({ reason: "tenant required (x-tenant header missing)" });
302
+ return { subject: _(d) };
233
303
  };
234
- }, k = class extends n.TaggedError()("Unauthenticated", { reason: n.optional(n.String) }) {}, A = (e, t) => {
235
- if (e.type === "anonymous") throw new k(t === void 0 ? {} : { reason: t });
236
- }, j = "admin:full", M = "apikeys:issue:self", N = "apikeys:issue:org", P = "apikeys:issue:other", F = class extends n.TaggedError()("ScopeError", {
304
+ }, A = class extends n.TaggedError()("Unauthenticated", { reason: n.optional(n.String) }) {}, j = (e, t) => {
305
+ if (e.type === "anonymous") throw new A(t === void 0 ? {} : { reason: t });
306
+ }, M = "admin:full", N = "apikeys:issue:self", P = "apikeys:issue:org", F = "apikeys:issue:other", I = "devices:manage", L = class extends n.TaggedError()("ScopeError", {
237
307
  required: n.String,
238
308
  message: n.String
239
- }) {}, I = (e) => "scopes" in e && e.scopes ? e.scopes : [], L = /* @__PURE__ */ new WeakMap(), R = (e, t) => {
240
- typeof e == "object" && e && L.set(e, t);
241
- }, z = (e) => {
309
+ }) {}, R = (e) => "scopes" in e && e.scopes ? e.scopes : [], z = /* @__PURE__ */ new WeakMap(), B = (e, t) => {
310
+ typeof e == "object" && e && z.set(e, t);
311
+ }, V = (e) => {
242
312
  if (typeof e == "object" && e) {
243
- let t = L.get(e);
313
+ let t = z.get(e);
244
314
  if (t) return t;
245
315
  }
246
- return I(e);
247
- }, B = (e, t) => {
248
- let n = z(e);
316
+ return R(e);
317
+ }, H = (e, t) => {
318
+ let n = V(e);
249
319
  return n.includes("admin:full") || n.includes(t);
250
- }, V = (e, t) => {
251
- let n = I(e);
320
+ }, U = (e, t) => {
321
+ let n = R(e);
252
322
  return n.includes("admin:full") || n.includes(t);
253
- }, H = (e, n) => B(e, n) ? t.void : t.fail(new F({
323
+ }, fe = (e, n) => H(e, n) ? t.void : t.fail(new L({
254
324
  required: n,
255
325
  message: `missing required scope '${n}'`
256
- })), U = (e, t) => {
326
+ })), W = (e, t) => {
257
327
  let n;
258
328
  try {
259
329
  n = typeof e.action == "function" ? e.action(t) : e.action;
260
330
  } catch {
261
- throw new F({
331
+ throw new L({
262
332
  required: e.resourceType,
263
333
  message: "policy action selector failed"
264
334
  });
265
335
  }
266
- if (typeof n != "string" || n.trim() === "") throw new F({
336
+ if (typeof n != "string" || n.trim() === "") throw new L({
267
337
  required: e.resourceType,
268
338
  message: "policy action selector returned no action"
269
339
  });
270
340
  return n;
271
- }, W = (e) => (typeof e.action == "string" || typeof e.action == "function") && typeof e.resourceType == "string", G = (e) => typeof e.open == "string", K = (e) => e.authenticated === !0, le = (e) => ({ open: e }), q = (e) => typeof e == "string" ? [e] : e, J, ue = (e) => {
272
- J = e;
273
- }, Y = (e) => J === void 0 ? e.filter((e) => (e.guards ?? []).some((e) => !W(e) && e.resource !== void 0)).map((e) => e.tag) : [], de = (e) => [
341
+ }, G = (e) => (typeof e.action == "string" || typeof e.action == "function") && typeof e.resourceType == "string", K = (e) => typeof e.open == "string", q = (e) => e.authenticated === !0, pe = (e) => ({ open: e }), J = (e) => typeof e == "string" ? [e] : e, Y, me = (e) => {
342
+ Y = e;
343
+ }, he = (e) => Y === void 0 ? e.filter((e) => (e.guards ?? []).some((e) => !G(e) && e.resource !== void 0)).map((e) => e.tag) : [], ge = (e) => [
274
344
  `${e.length} procedure${e.length === 1 ? "" : "s"} declare a per-resource guard that nothing enforces:`,
275
345
  ...e.slice(0, 10).map((e) => ` ${e}`),
276
346
  ...e.length > 10 ? [` … and ${e.length - 10} more`] : [],
@@ -284,56 +354,56 @@ var i = n.Record({
284
354
  " • setResourceScopeResolver((req) => Effect<boolean>) — answers \"does this",
285
355
  " subject hold `req.scope` on `req.resource`\" from your own tables.",
286
356
  " • Drop the `resource` extractor if a global scope check is what you meant."
287
- ].join("\n"), fe = () => J, X, pe = (e) => {
357
+ ].join("\n"), _e = () => Y, X, ve = (e) => {
288
358
  X = e;
289
- }, me = () => X, Z = (e) => new F({
359
+ }, ye = () => X, Z = (e) => new L({
290
360
  required: "access-decision",
291
361
  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>'\`.`
292
362
  }), Q = (e, t) => {
293
363
  let n = e.credentialRejected;
294
- return n === void 0 ? t : new k({ 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.` });
364
+ return n === void 0 ? t : new A({ 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.` });
295
365
  }, $ = (e, t, n) => {
296
366
  if (!t || t.length === 0) return n?.defaultDeny === !0 ? Z(n.procedure) : null;
297
367
  for (let n of t) {
298
- if (G(n)) continue;
299
- if (K(n)) {
368
+ if (K(n)) continue;
369
+ if (q(n)) {
300
370
  if (e.id !== null && e.id !== void 0) continue;
301
- return Q(e, new F({
371
+ return Q(e, new L({
302
372
  required: "authenticated",
303
373
  message: "this procedure requires an authenticated caller"
304
374
  }));
305
375
  }
306
- if (W(n)) {
307
- if (B(e, "admin:full")) continue;
308
- return Q(e, new F({
376
+ if (G(n)) {
377
+ if (H(e, "admin:full")) continue;
378
+ return Q(e, new L({
309
379
  required: `${n.resourceType}:${n.action}`,
310
380
  message: `relationship guard '${n.action}' on '${n.resourceType}' cannot be checked synchronously`
311
381
  }));
312
382
  }
313
- let t = q(n.scope);
383
+ let t = J(n.scope);
314
384
  if (t.length === 0) continue;
315
- let r = t.filter((t) => B(e, t));
385
+ let r = t.filter((t) => H(e, t));
316
386
  if (!((n.mode ?? "all") === "any" ? r.length > 0 : r.length === t.length)) {
317
- let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !B(e, t));
318
- return Q(e, new F({
387
+ let r = (n.mode ?? "all") === "any" ? t[0] : t.find((t) => !H(e, t));
388
+ return Q(e, new L({
319
389
  required: r,
320
390
  message: (n.mode ?? "all") === "any" ? `missing all of required scopes [${t.join(", ")}]` : `missing required scope '${r}'`
321
391
  }));
322
392
  }
323
393
  }
324
394
  return null;
325
- }, he = (e, n, r) => {
395
+ }, be = (e, n, r) => {
326
396
  let i;
327
397
  try {
328
- i = U(n, r);
398
+ i = W(n, r);
329
399
  } catch (e) {
330
400
  return t.succeed(e);
331
401
  }
332
- let a = (e) => new F({
402
+ let a = (e) => new L({
333
403
  required: `${n.resourceType}:${i}`,
334
404
  message: e
335
405
  });
336
- if (B(e, "admin:full")) return t.succeed(null);
406
+ if (H(e, "admin:full")) return t.succeed(null);
337
407
  let o = X;
338
408
  if (o === void 0) return t.succeed(a(`no policy resolver registered — cannot authorize '${i}' on '${n.resourceType}'`));
339
409
  let s = n.resource(r);
@@ -343,29 +413,29 @@ var i = n.Record({
343
413
  resourceType: n.resourceType,
344
414
  resourceId: s
345
415
  })).pipe(t.catchAllCause(() => t.succeed(!1)), t.map((e) => e ? null : a(`no '${i}' relation on ${n.resourceType}:${s}`)));
346
- }, ge = (e, n, r, i) => {
416
+ }, xe = (e, n, r, i) => {
347
417
  if (!n || n.length === 0) return t.succeed($(e, n, i));
348
- let a = J;
349
- return !n.some(W) && (!a || !n.some((e) => e.resource !== void 0)) ? t.succeed($(e, n, i)) : t.gen(function* () {
418
+ let a = Y;
419
+ return !n.some(G) && (!a || !n.some((e) => e.resource !== void 0)) ? t.succeed($(e, n, i)) : t.gen(function* () {
350
420
  for (let i of n) {
351
- if (G(i)) continue;
352
- if (K(i)) {
421
+ if (K(i)) continue;
422
+ if (q(i)) {
353
423
  if (e.id !== null && e.id !== void 0) continue;
354
- return Q(e, new F({
424
+ return Q(e, new L({
355
425
  required: "authenticated",
356
426
  message: "this procedure requires an authenticated caller"
357
427
  }));
358
428
  }
359
- if (W(i)) {
360
- let t = yield* he(e, i, r);
429
+ if (G(i)) {
430
+ let t = yield* be(e, i, r);
361
431
  if (t) return t;
362
432
  continue;
363
433
  }
364
- let n = q(i.scope);
434
+ let n = J(i.scope);
365
435
  if (n.length === 0) continue;
366
436
  let o = i.resource ? i.resource(r) : void 0, s = [];
367
437
  for (let r of n) {
368
- if (B(e, r)) {
438
+ if (H(e, r)) {
369
439
  s.push(!0);
370
440
  continue;
371
441
  }
@@ -381,7 +451,7 @@ var i = n.Record({
381
451
  let c = i.mode ?? "all";
382
452
  if (!(c === "any" ? s.some(Boolean) : s.every(Boolean))) {
383
453
  let e = c === "any" ? n[0] : n[n.findIndex((e, t) => !s[t])];
384
- return new F({
454
+ return new L({
385
455
  required: e,
386
456
  message: c === "any" ? `missing all of required scopes [${n.join(", ")}]` : `missing required scope '${e}'` + (o === void 0 ? "" : ` on resource '${o}'`)
387
457
  });
@@ -391,4 +461,4 @@ var i = n.Record({
391
461
  });
392
462
  };
393
463
  //#endregion
394
- export { re as A, C as B, pe as C, b as D, x as E, k as F, E as G, D as H, h as I, g as J, _ as K, T as L, d as M, f as N, S as O, y as P, A as R, R as S, I as T, O as U, te as V, v as W, ee as Y, W as _, F as a, H as b, ge as c, me as d, fe as f, G as g, K as h, M as i, ne as j, ie as k, z as l, V as m, N as n, de as o, B as p, p as q, P as r, $ as s, j as t, Y as u, Z as v, ue as w, U as x, le as y, ce as z };
464
+ export { o as $, T as A, k as B, B as C, oe as D, R as E, y as F, ae as G, re as H, A as I, h as J, E as K, _ as L, ie as M, ee as N, b as O, m as P, i as Q, C as R, W as S, me as T, D as U, S as V, O as W, ne as X, te as Y, a as Z, K as _, I as a, pe as b, $ as c, he as d, ye as f, q as g, U as h, N as i, w as j, x as k, xe as l, H as m, P as n, L as o, _e as p, v as q, F as r, ge as s, M as t, V as u, G as v, ve as w, fe as x, Z as y, j as z };
package/dist/session.js CHANGED
@@ -1,11 +1,11 @@
1
- import { N as e, T as t, q as n } from "./scopes-CW9_LbDO.js";
1
+ import { E as e, J as t, P as n } from "./scopes-Dj7KCJab.js";
2
2
  import { Schema as r } from "effect";
3
3
  import { createHmac as i, timingSafeEqual as a } from "node:crypto";
4
4
  import { createLogger as o } from "@voltro/logger";
5
5
  //#region src/session.ts
6
6
  var s = o({ scope: "@voltro/protocol:session" }), c = 2, l = r.Struct({
7
7
  v: r.Literal(2),
8
- subject: e,
8
+ subject: n,
9
9
  exp: r.Number,
10
10
  iat: r.Number,
11
11
  kid: r.optional(r.String)
@@ -38,14 +38,14 @@ var s = o({ scope: "@voltro/protocol:session" }), c = 2, l = r.Struct({
38
38
  }, C = 604800, w = .7, T = (e, t) => x(i("sha256", t).update(e).digest()), E = (e) => typeof e == "string" ? {
39
39
  kid: "k0",
40
40
  secret: e
41
- } : e, D = (e, r, i = {}) => {
41
+ } : e, D = (n, r, i = {}) => {
42
42
  let a = E(r);
43
43
  if (!a.secret) throw Error("signSession: secret must be a non-empty string");
44
- let o = t(e);
45
- if (o.length > 0) throw Error(`signSession: refusing to mint a session cookie for a ${e.type} subject carrying ${o.length} scope(s) (${o.slice(0, 5).join(", ")}). A session cookie carries IDENTITY only — authority is resolved per request, so that removing a role takes effect on the session that already exists instead of in up to 7 days. Mint the session from the identity and move the scopes into the auth.resolveScopes resolver, which runs on every request and can be invalidated the moment a role changes.`);
44
+ let o = e(n);
45
+ if (o.length > 0) throw Error(`signSession: refusing to mint a session cookie for a ${n.type} subject carrying ${o.length} scope(s) (${o.slice(0, 5).join(", ")}). A session cookie carries IDENTITY only — authority is resolved per request, so that removing a role takes effect on the session that already exists instead of in up to 7 days. Mint the session from the identity and move the scopes into the auth.resolveScopes resolver, which runs on every request and can be invalidated the moment a role changes.`);
46
46
  let s = i.ttlSeconds ?? C, c = Math.floor((i.now ?? Date.now)() / 1e3), l = {
47
47
  v: 2,
48
- subject: n(e),
48
+ subject: t(n),
49
49
  exp: c + s,
50
50
  iat: c,
51
51
  ...typeof r == "string" ? {} : { kid: a.kid }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/protocol",
3
- "version": "0.71.1",
3
+ "version": "0.73.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",
@@ -63,9 +63,10 @@
63
63
  "node": ">=24.0.0"
64
64
  },
65
65
  "dependencies": {
66
+ "@effect/experimental": "^0.61.1",
66
67
  "@effect/sql": "^0.52.1",
67
- "@voltro/database": "0.71.1",
68
- "@voltro/logger": "0.71.1",
68
+ "@voltro/database": "0.73.0",
69
+ "@voltro/logger": "0.73.0",
69
70
  "jose": "^6.2.10"
70
71
  },
71
72
  "peerDependencies": {