@voltro/protocol 0.86.0 → 0.87.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.
@@ -0,0 +1,108 @@
1
+ //#region src/apiRoleEndpoints.ts
2
+ var e = (e, t) => {
3
+ if (!e || typeof e != "object" || Array.isArray(e) || ![Object.prototype, null].includes(Object.getPrototypeOf(e))) throw Error("Invalid role endpoint object");
4
+ let n = Object.create(null);
5
+ for (let r of Reflect.ownKeys(e)) {
6
+ let i = Object.getOwnPropertyDescriptor(e, r);
7
+ if (typeof r != "string" || !t.includes(r) || !i.enumerable || !("value" in i)) throw Error("Invalid role endpoint field");
8
+ n[r] = i.value;
9
+ }
10
+ return n;
11
+ }, t = (e, t, n = !1) => {
12
+ if (!Array.isArray(e) || e.length < +!n || e.length > t || Reflect.ownKeys(e).length !== e.length + 1) throw Error("Invalid role endpoint list");
13
+ let r = [];
14
+ for (let t = 0; t < e.length; t++) {
15
+ let n = Object.getOwnPropertyDescriptor(e, String(t));
16
+ if (!n?.enumerable || !("value" in n)) throw Error("Invalid role endpoint list item");
17
+ r.push(n.value);
18
+ }
19
+ return r;
20
+ }, n = /^(?:[A-Za-z][A-Za-z0-9_-]*\.)*[A-Za-z][A-Za-z0-9_-]*$/, r = /^(?:[A-Za-z_][A-Za-z0-9_-]*\.)*[A-Za-z_][A-Za-z0-9_-]*$/, i = [
21
+ "rpc",
22
+ "subscriptions",
23
+ "events",
24
+ "presence",
25
+ "workflows",
26
+ "schedules",
27
+ "directors",
28
+ "drainers",
29
+ "reapers",
30
+ "retention",
31
+ "rest",
32
+ "webhooks",
33
+ "inspect",
34
+ "cdc"
35
+ ], a = (r) => {
36
+ let a = e(r, [
37
+ "version",
38
+ "transport",
39
+ "roles"
40
+ ]);
41
+ if (a.version !== 1 || a.transport !== "compose-nginx" && a.transport !== "kubernetes-nginx") throw Error("Unknown role endpoint protocol");
42
+ let o = /* @__PURE__ */ new Set(), s = t(a.roles, 32, !0).map((r) => {
43
+ let a = e(r, [
44
+ "role",
45
+ "routes",
46
+ "workloads",
47
+ "httpPath",
48
+ "webSocketPath"
49
+ ]), s = a.role;
50
+ if (typeof s != "string" || !/^[a-z][a-z0-9-]{0,63}$/.test(s) || o.has(s)) throw Error("Invalid or duplicate endpoint role");
51
+ o.add(s);
52
+ let c = t(a.workloads, 14).map((e) => {
53
+ if (typeof e != "string" || !i.includes(e)) throw Error("Unknown endpoint workload");
54
+ return e;
55
+ });
56
+ if (new Set(c).size !== c.length) throw Error("Duplicate endpoint workload");
57
+ let l = a.routes === null ? null : t(a.routes, 128).map((e) => {
58
+ if (typeof e != "string" || e.length > 191 || !n.test(e.endsWith(".*") ? e.slice(0, -2) : e)) throw Error("Invalid endpoint tag filter");
59
+ return e;
60
+ });
61
+ if (l && new Set(l).size !== l.length) throw Error("Duplicate endpoint tag filter");
62
+ let u = "/_voltro/roles/" + s;
63
+ if (a.webSocketPath !== u + "/ws" || a.httpPath !== (c.includes("rpc") ? u + "/rpc" : null)) throw Error("Role endpoints must use canonical same-origin paths");
64
+ return Object.freeze({
65
+ role: s,
66
+ routes: l && Object.freeze(l),
67
+ workloads: Object.freeze(c),
68
+ httpPath: a.httpPath,
69
+ webSocketPath: a.webSocketPath
70
+ });
71
+ });
72
+ return Object.freeze({
73
+ version: 1,
74
+ transport: a.transport,
75
+ roles: Object.freeze(s)
76
+ });
77
+ }, o = (e, t, n) => {
78
+ if (t.length > 191 || !r.test(t) || ![
79
+ "rpc",
80
+ "subscriptions",
81
+ "events"
82
+ ].includes(n)) throw Error("Invalid role endpoint request");
83
+ let i, a = -1, o = !1;
84
+ for (let r of e.roles) {
85
+ if (!r.workloads.includes(n)) continue;
86
+ let e = r.routes === null ? 0 : Math.max(-1, ...r.routes.map((e) => e.endsWith(".*") ? t.startsWith(e.slice(0, -1)) ? e.length : -1 : e === t ? 1024 + e.length : -1));
87
+ e > a ? (i = r, a = e, o = !1) : e >= 0 && e === a && (o = !0);
88
+ }
89
+ if (!i || o) throw Error(o ? "Ambiguous role endpoint" : "No role endpoint owns this tag and workload");
90
+ return i;
91
+ }, s = (e) => {
92
+ if (!/^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$/.test(e)) throw Error("Invalid role proxy API name");
93
+ return `/_voltro/role-api/api-${e}/ws`;
94
+ }, c = (e, t, n) => {
95
+ if (!/^[a-z][a-z0-9-]{0,63}$/.test(t.role) || !["http", "websocket"].includes(n)) throw Error("Invalid role transport");
96
+ let r = new URL(e);
97
+ if (![
98
+ "http:",
99
+ "https:",
100
+ "ws:",
101
+ "wss:"
102
+ ].includes(r.protocol) || r.username || r.password) throw Error("Invalid role endpoint origin");
103
+ let i = n === "http" ? t.httpPath : t.webSocketPath;
104
+ if (!i || i !== "/_voltro/roles/" + t.role + (n === "http" ? "/rpc" : "/ws")) throw Error("Missing or noncanonical role transport");
105
+ return r.protocol = n === "http" ? r.protocol === "https:" || r.protocol === "wss:" ? "https:" : "http:" : r.protocol === "https:" || r.protocol === "wss:" ? "wss:" : "ws:", r.pathname = (/^\/_voltro\/role-api\/api-[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}\/ws$/.test(r.pathname) ? r.pathname.slice(0, -3) : "") + i, r.search = "", r.hash = "", r.toString();
106
+ };
107
+ //#endregion
108
+ export { c as apiRoleEndpointUrl, s as apiRoleProxyBasePath, a as captureApiRoleEndpoints, o as selectApiRoleEndpoint };
@@ -0,0 +1,68 @@
1
+ import { API_WORKLOADS as e } from "./plugin.js";
2
+ import { Schema as t } from "effect";
3
+ //#region src/apiRoleObservation.ts
4
+ var n = t.Literals(e), r = t.Number.pipe(t.check(t.isFinite()), t.check(t.isGreaterThanOrEqualTo(0))), i = r.pipe(t.check(t.isInt())), a = i.pipe(t.check(t.isLessThanOrEqualTo(864e13))), o = t.Struct({
5
+ workload: n,
6
+ seconds: r
7
+ }), s = t.Struct({
8
+ replicaId: t.NonEmptyString,
9
+ startedAt: a,
10
+ role: t.NonEmptyString,
11
+ workloads: t.Array(n),
12
+ capturedAt: a,
13
+ publisher: t.Struct({
14
+ ready: t.Boolean,
15
+ stopped: t.Boolean,
16
+ pendingBuckets: i,
17
+ droppedBuckets: i,
18
+ rejectedSegments: i,
19
+ writeFailures: i,
20
+ observationErrors: i
21
+ }),
22
+ currentMeasuredSeconds: t.Array(t.Struct({
23
+ role: t.NonEmptyString,
24
+ workload: n,
25
+ seconds: r
26
+ })),
27
+ history: t.Struct({
28
+ state: t.Literals([
29
+ "pending",
30
+ "available",
31
+ "unavailable"
32
+ ]),
33
+ measuredSeconds: t.Array(o),
34
+ assessment: t.NullOr(t.Struct({
35
+ from: a,
36
+ until: a,
37
+ complete: t.Boolean,
38
+ missingBuckets: i.pipe(t.check(t.isLessThanOrEqualTo(288))),
39
+ incompleteWorkloads: t.Array(n),
40
+ reason: t.Literals([
41
+ "invalid-observation",
42
+ "incomplete",
43
+ "idle",
44
+ "observed"
45
+ ]),
46
+ totals: t.Array(t.Struct({
47
+ workload: n,
48
+ seconds: r,
49
+ share: r.pipe(t.check(t.isLessThanOrEqualTo(1)))
50
+ })),
51
+ splitCandidates: t.Array(n)
52
+ }))
53
+ })
54
+ }).pipe(t.check(t.makeFilter((e) => {
55
+ let t = (e) => new Set(e).size === e.length, n = (n) => t(n) && n.every((t) => e.workloads.some((e) => e === t));
56
+ if (!e.workloads.length || !t(e.workloads) || e.startedAt > e.capturedAt || !n(e.currentMeasuredSeconds.map((e) => e.workload)) || e.currentMeasuredSeconds.some((t) => t.role !== e.role) || !n(e.history.measuredSeconds.map((e) => e.workload))) return !1;
57
+ let r = e.history.assessment;
58
+ if (e.history.state !== "available") return r === null && e.history.measuredSeconds.length === 0;
59
+ if (!r || r.until - r.from !== 864e5 || r.until % 3e5 != 0 || r.until > e.capturedAt || !n(r.incompleteWorkloads) || !n(r.splitCandidates) || !n(r.totals.map((e) => e.workload))) return !1;
60
+ if (!r.complete) return r.totals.length === 0 && r.splitCandidates.length === 0 && ["incomplete", "invalid-observation"].includes(r.reason);
61
+ if (r.from < e.startedAt || r.missingBuckets !== 0 || r.incompleteWorkloads.length !== 0 || r.totals.length !== e.workloads.length) return !1;
62
+ let i = r.totals.reduce((e, t) => e + t.seconds, 0);
63
+ if (i > 86400 || r.reason !== (i === 0 ? "idle" : "observed") || r.totals.some((e) => Math.abs(e.share - (i === 0 ? 0 : e.seconds / i)) > 1e-9)) return !1;
64
+ let a = r.totals.filter((e) => e.share > .3).map((e) => e.workload), o = a.length >= 2 ? a : [];
65
+ return o.length === r.splitCandidates.length && o.every((e) => r.splitCandidates.includes(e));
66
+ }, { message: "Inconsistent API role observation" })));
67
+ //#endregion
68
+ export { s as ApiRoleObservationSchema };
package/dist/apikey.d.ts CHANGED
@@ -129,6 +129,13 @@ declare interface AuthStrategy {
129
129
  declare interface AuthStrategyInput {
130
130
  readonly headers: Readonly<Record<string, string | undefined>>;
131
131
  readonly clientId: number;
132
+ /** HTTP request target, supplied by REST transports. Request selectors may
133
+ * read an explicit tenant hint here; it is never authentication or authority.
134
+ * Absent for connection transports and non-HTTP callers. */
135
+ readonly http?: {
136
+ readonly path: string;
137
+ readonly query: string;
138
+ };
132
139
  /**
133
140
  * The app's DataStore, for a strategy that must READ to identify the caller.
134
141
  *
@@ -196,6 +203,9 @@ declare type StrategyResolution = {
196
203
 
197
204
  declare const Subject: Schema.Union<readonly [Schema.Struct<{
198
205
  readonly type: Schema.Literal<"user">;
206
+ /** A guest has a stable identity but has not linked a registered account.
207
+ * This identity mark survives session signing; it never grants authority. */
208
+ readonly kind: Schema.optional<Schema.Literal<"guest">>;
199
209
  readonly id: Schema.String;
200
210
  readonly tenantId: Schema.String;
201
211
  readonly scopes: Schema.optional<Schema.$Array<Schema.String>>;
package/dist/auth.js CHANGED
@@ -4,6 +4,7 @@ import { RpcMiddleware as s } from "effect/rpc";
4
4
  //#region src/auth.ts
5
5
  var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.Struct({
6
6
  type: a.Literal("user"),
7
+ kind: a.optional(a.Literal("guest")),
7
8
  id: a.String,
8
9
  tenantId: a.String,
9
10
  scopes: l,
@@ -103,7 +104,7 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
103
104
  let n = F.get(e);
104
105
  if (n === void 0) return await t?.(e), e;
105
106
  let r = await n.resolve();
106
- if (r.type !== e.type || r.id !== e.id || r.tenantId !== e.tenantId || e.type === "service" && (r.type !== "service" || r.service !== e.service)) throw new M({ reason: "subscription identity or tenant changed; subscribe again" });
107
+ if (r.type !== e.type || r.id !== e.id || r.tenantId !== e.tenantId || e.type === "service" && (r.type !== "service" || r.service !== e.service) || e.type === "user" && (r.type !== "user" || r.kind !== e.kind) || e.type === "serviceAccount" && (r.type !== "serviceAccount" || r.auditActorId !== e.auditActorId)) throw new M({ reason: "request identity or tenant changed; authenticate again" });
107
108
  return F.set(r, {
108
109
  current: r,
109
110
  resolve: n.resolve
@@ -140,14 +141,33 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
140
141
  },
141
142
  removed: i
142
143
  };
143
- }, H = 3e4, U = 1e4, W = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, G = () => {
144
+ }, H = async (e, t, n) => {
145
+ let r = n?.resolveScopes, i = B(r === void 0 ? [] : await r(e, t));
146
+ if (i.kind === "unavailable") return {
147
+ kind: "unavailable",
148
+ reason: i.reason
149
+ };
150
+ let a = V(e, i).subject;
151
+ if (a.type !== "anonymous") for (let e of n?.subjectGuards ?? []) {
152
+ let n = await e(a, t);
153
+ if (!n.ok) return {
154
+ kind: "vetoed",
155
+ code: n.code,
156
+ message: n.message
157
+ };
158
+ }
159
+ return {
160
+ kind: "resolved",
161
+ subject: a
162
+ };
163
+ }, U = 3e4, W = 1e4, G = (e) => `${e.type}\u0000${e.tenantId ?? ""}\u0000${e.id ?? ""}`, K = () => {
144
164
  if (typeof process > "u") return;
145
165
  let e = process.env.VOLTRO_AUTH_SCOPE_CACHE_TTL_MS;
146
166
  if (e === void 0 || e === "") return;
147
167
  let t = Number(e);
148
168
  return Number.isFinite(t) && t >= 0 ? t : void 0;
149
- }, K = (e = {}) => {
150
- let t = e.ttlMs ?? G() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
169
+ }, q = (e = {}) => {
170
+ let t = e.ttlMs ?? K() ?? 3e4, n = e.maxEntries ?? 1e4, r = e.now ?? Date.now, i = /* @__PURE__ */ new Map(), a = () => {
151
171
  if (i.size <= n) return;
152
172
  let e = r();
153
173
  for (let [t, n] of i) n.freshUntil <= e && i.delete(t);
@@ -183,10 +203,10 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
183
203
  };
184
204
  }
185
205
  };
186
- }, q = (e) => typeof e.resolve == "function", J = (e) => {
206
+ }, J = (e) => typeof e.resolve == "function", Y = (e) => {
187
207
  let t = e.authorization ?? e.Authorization;
188
208
  if (t !== void 0) return /^Bearer\s+(.+)$/i.exec(t.trim())?.[1]?.trim();
189
- }, Y = async (e, i, a, o) => {
209
+ }, X = async (e, i, a, o) => {
190
210
  let s;
191
211
  try {
192
212
  s = await a.source(e, i);
@@ -207,7 +227,7 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
207
227
  } catch (e) {
208
228
  throw e instanceof t ? o(e.reason) : e;
209
229
  }
210
- }, X = async (e, i, a, o) => {
230
+ }, Z = async (e, i, a, o) => {
211
231
  if (!n(e)) return e;
212
232
  let s = {
213
233
  type: e.type,
@@ -236,14 +256,14 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
236
256
  }
237
257
  return e;
238
258
  }
239
- }, Z = (e, t) => {
240
- let n = J(t);
259
+ }, Q = (e, t) => {
260
+ let n = Y(t);
241
261
  if (n === void 0) return e;
242
262
  let r = e.filter((e) => e.claimsBearerPrefix !== void 0 && e.claimsBearerPrefix !== "" && n.startsWith(e.claimsBearerPrefix));
243
263
  if (r.length === 0) return e;
244
264
  let i = new Set(r);
245
265
  return [...r, ...e.filter((e) => !i.has(e))];
246
- }, Q = (e, t) => {
266
+ }, $ = (e, t) => {
247
267
  if (e === "refuse") throw new M({ reason: "anonymous callers are refused" });
248
268
  if (e === "no-tenant") return "";
249
269
  if (e === "header") {
@@ -251,24 +271,24 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
251
271
  return t;
252
272
  }
253
273
  return "tenantId" in e ? e.tenantId : t !== void 0 && t !== "" ? t : e.headerOrTenant;
254
- }, $ = (t, n) => {
274
+ }, ee = (t, n) => {
255
275
  let r = n?.resolveScopes, i = n?.subjectGuards ?? [], a = n?.tenantSelection !== void 0 || n?.requestedTenant !== void 0;
256
276
  if (n?.memberships !== void 0 !== a) throw Error("composeAuthStrategies: memberships and tenant selection come together; `memberships` requires `tenantSelection` or `requestedTenant`; each tenant selector requires `memberships` to authorize its target.");
257
277
  let o = n?.memberships !== void 0 && n.tenantSelection !== void 0 ? {
258
278
  memberships: n.memberships,
259
279
  store: n.tenantSelection
260
- } : void 0, s = r !== void 0 || i.length > 0, c = !s || n?.scopeCache === !1 ? null : n?.scopeCache !== void 0 && q(n.scopeCache) ? n.scopeCache : K(n?.scopeCache ?? {}), l = async (a) => {
280
+ } : void 0, s = r !== void 0 || i.length > 0, c = !s || n?.scopeCache === !1 ? null : n?.scopeCache !== void 0 && J(n.scopeCache) ? n.scopeCache : q(n?.scopeCache ?? {}), l = async (a) => {
261
281
  let l = n?.getStore?.(), u = l === void 0 ? a : {
262
282
  ...a,
263
283
  store: l
264
284
  }, d;
265
- for (let a of Z(t, u.headers)) {
285
+ for (let a of Q(t, u.headers)) {
266
286
  let t = await a.resolve(u);
267
287
  if (t.kind === "matched") {
268
288
  let l = (e) => (n?.onStrategyFailed?.({
269
289
  strategyId: a.id,
270
290
  reason: e
271
- }), new M({ reason: e })), d = n?.requestedTenant === void 0 || n.memberships === void 0 ? void 0 : await Y(t.subject, u, {
291
+ }), new M({ reason: e })), d = n?.requestedTenant === void 0 || n.memberships === void 0 ? void 0 : await X(t.subject, u, {
272
292
  source: n.requestedTenant,
273
293
  memberships: n.memberships
274
294
  }, (t) => (n?.onStrategyFailed?.({
@@ -279,7 +299,7 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
279
299
  ...t.subject,
280
300
  scopes: []
281
301
  } };
282
- let f = d ?? (o === void 0 ? t.subject : await X(t.subject, u, o, l)), p = f;
302
+ let f = d ?? (o === void 0 ? t.subject : await Z(t.subject, u, o, l)), p = f;
283
303
  if (s) {
284
304
  let e = async () => {
285
305
  let e = r === void 0 ? {
@@ -305,7 +325,7 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
305
325
  }, { decision: t, fresh: o } = c === null ? {
306
326
  decision: await e(),
307
327
  fresh: !0
308
- } : await c.resolve(W(f), e);
328
+ } : await c.resolve(G(f), e);
309
329
  if (t.kind === "vetoed") {
310
330
  let e = `${t.code}: ${t.message}`;
311
331
  throw n?.onStrategyFailed?.({
@@ -350,7 +370,7 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
350
370
  } : e };
351
371
  }
352
372
  if (d !== void 0) throw new M({ reason: `credential rejected: ${d}` });
353
- return { subject: b(Q(n?.anonymous ?? "no-tenant", u.headers["x-tenant"])) };
373
+ return { subject: b($(n?.anonymous ?? "no-tenant", u.headers["x-tenant"])) };
354
374
  };
355
375
  return async (e) => {
356
376
  let t = {
@@ -362,8 +382,8 @@ var c = a.Record(a.String, a.Unknown), l = a.optional(a.Array(a.String)), u = a.
362
382
  subject: r
363
383
  };
364
384
  };
365
- }, ee = (e, t) => {
385
+ }, te = (e, t) => {
366
386
  if (e.type === "anonymous") throw new M(t === void 0 ? {} : { reason: t });
367
387
  };
368
388
  //#endregion
369
- export { N as AuthMiddleware, j as ConnectionInfo, P as ConnectionInfoMiddleware, U as DEFAULT_SCOPE_CACHE_MAX_ENTRIES, H as DEFAULT_SCOPE_CACHE_TTL_MS, O as IMPERSONATION_METADATA_KEY, g as Subject, _ as SubjectIdentity, A as SubjectService, M as Unauthenticated, b as anonymousSubject, V as applyScopeDecision, ee as assertAuthenticated, I as bindSubjectAuthority, $ as composeAuthStrategies, L as currentSubjectAuthority, z as hasCallbackRoutes, w as isMachineSubject, C as isServiceSubject, D as isSystemSubject, K as makeScopeCache, Z as orderByBearerPrefix, k as rawImpersonationMark, R as reauthorizeSubject, W as scopeCacheKey, S as serviceSubject, T as subjectAuditActorId, v as subjectIdentity, x as systemSubject, E as tenantScopedSubject };
389
+ export { N as AuthMiddleware, j as ConnectionInfo, P as ConnectionInfoMiddleware, W as DEFAULT_SCOPE_CACHE_MAX_ENTRIES, U as DEFAULT_SCOPE_CACHE_TTL_MS, O as IMPERSONATION_METADATA_KEY, g as Subject, _ as SubjectIdentity, A as SubjectService, M as Unauthenticated, b as anonymousSubject, V as applyScopeDecision, te as assertAuthenticated, I as bindSubjectAuthority, ee as composeAuthStrategies, L as currentSubjectAuthority, z as hasCallbackRoutes, w as isMachineSubject, C as isServiceSubject, D as isSystemSubject, q as makeScopeCache, Q as orderByBearerPrefix, k as rawImpersonationMark, R as reauthorizeSubject, H as resolveIdentityAuthority, G as scopeCacheKey, S as serviceSubject, T as subjectAuditActorId, v as subjectIdentity, x as systemSubject, E as tenantScopedSubject };
@@ -0,0 +1,14 @@
1
+ //#region src/canonicalJson.ts
2
+ var e = (e) => {
3
+ let t = (e) => {
4
+ if (Array.isArray(e)) return e.map(t);
5
+ if (typeof e == "object" && e) {
6
+ let n = e;
7
+ return Object.fromEntries(Object.keys(n).sort().map((e) => [e, t(n[e])]));
8
+ }
9
+ return e;
10
+ };
11
+ return JSON.stringify(t(e) ?? null);
12
+ };
13
+ //#endregion
14
+ export { e as canonicalJson };
@@ -0,0 +1,24 @@
1
+ import { Schema as e } from "effect";
2
+ import * as t from "effect/SchemaAST";
3
+ //#region src/errorSchema.ts
4
+ var n = (e) => {
5
+ let n = /* @__PURE__ */ new Set();
6
+ if (e === void 0) return n;
7
+ let r = (e) => {
8
+ if (t.isUnion(e)) for (let t of e.types) r(t);
9
+ else if (e.encoding !== void 0 && r(t.toEncoded(e)), t.isObjects(e)) {
10
+ let r = e.propertySignatures.find((e) => e.name === "_tag");
11
+ r !== void 0 && t.isLiteral(r.type) && typeof r.type.literal == "string" && n.add(r.type.literal);
12
+ }
13
+ };
14
+ return r(e.ast), n;
15
+ }, r = (r, i) => {
16
+ if (r === void 0) return;
17
+ let a = t.isUnion(r.ast) ? r.ast.types : [r.ast];
18
+ for (let t of a) {
19
+ let r = e.make(t);
20
+ if (n(r).has(i)) return r;
21
+ }
22
+ };
23
+ //#endregion
24
+ export { n as declaredErrorTags, r as errorMemberSchema };
package/dist/event.js CHANGED
@@ -3,10 +3,15 @@ import { Unauthenticated as t } from "./auth.js";
3
3
  import { NoSuchResource as n, ScopeError as r, declaresResourceGuard as i, isOpenAccess as a, openAccessSpec as o } from "./scopes.js";
4
4
  import { strictInput as s } from "./strictInput.js";
5
5
  import { hasEnforcedGuard as c } from "./procedure.js";
6
- import { Schema as l } from "effect";
7
- import { Rpc as u } from "effect/rpc";
6
+ import { Context as l, Schema as u } from "effect";
7
+ import { Rpc as d } from "effect/rpc";
8
8
  //#region src/event.ts
9
- var d = 7500, f = (e) => typeof e == "object" && !!e && e.kind === "event", p = (e) => {
9
+ var f = 7500, p = (e) => typeof e == "object" && !!e && e.kind === "event", m = (e) => {
10
+ if (e.clientPolicy !== void 0) {
11
+ if (!/^[A-Za-z][A-Za-z0-9_.:-]{0,255}$/.test(e.clientPolicy)) throw Error("defineEvent: invalid client policy ID");
12
+ if (e.openAccess !== void 0 || !c(e.guards)) throw Error("defineEvent: a client policy requires explicit enforced guards");
13
+ if (e.webhook !== void 0) throw Error("defineEvent: private client policies do not authorize webhook recipients");
14
+ }
10
15
  if (e.name.length === 0) throw Error("defineEvent: `name` must not be empty");
11
16
  if (/\s/.test(e.name)) throw Error(`defineEvent("${e.name}"): \`name\` must not contain whitespace.\n The name is used as a broker subject segment. NATS refuses a subject with
12
17
  whitespace and delivers nothing — silently, and only on that broker, so an
@@ -39,66 +44,67 @@ var d = 7500, f = (e) => typeof e == "object" && !!e && e.kind === "event", p =
39
44
  openAccess: e.openAccess,
40
45
  rewind: e.rewind,
41
46
  webhook: e.webhook,
42
- delivery: e.delivery
47
+ delivery: e.delivery,
48
+ ...e.clientPolicy === void 0 ? {} : { clientPolicy: e.clientPolicy }
43
49
  };
44
- }, m = (e) => l.Struct({
45
- _tag: l.Literal("event"),
46
- origin: l.String,
47
- n: l.Number,
48
- emittedAt: l.Number,
50
+ }, h = (e) => u.Struct({
51
+ _tag: u.Literal("event"),
52
+ origin: u.String,
53
+ n: u.Number,
54
+ emittedAt: u.Number,
49
55
  payload: e
50
- }), h = l.Struct({
51
- _tag: l.Literal("gap"),
52
- missed: l.Number,
53
- reason: l.Literals(["buffer", "resume"])
54
- }), g = l.Struct({ _tag: l.Literal("attached") }), _ = (e) => l.Union([
55
- g,
56
- m(e),
57
- h
58
- ]), v = l.Struct({
59
- origin: l.String,
60
- n: l.Number
61
- }), y = (e) => l.Struct({
56
+ }), g = u.Struct({
57
+ _tag: u.Literal("gap"),
58
+ missed: u.Number,
59
+ reason: u.Literals(["buffer", "resume"])
60
+ }), _ = u.Struct({ _tag: u.Literal("attached") }), v = (e) => u.Union([
61
+ _,
62
+ h(e),
63
+ g
64
+ ]), y = u.Struct({
65
+ origin: u.String,
66
+ n: u.Number
67
+ }), b = (e) => u.Struct({
62
68
  key: e,
63
- resume: l.optional(l.Array(v))
64
- }), b = (o, d) => {
65
- let f = o.guards !== void 0 && o.guards.some((e) => !a(e)) ? l.Union([
69
+ resume: u.optional(u.Array(y))
70
+ }), x = l.Service("@voltro/protocol/EventRpcWorkload"), S = (o, l) => {
71
+ let f = o.guards !== void 0 && o.guards.some((e) => !a(e)) ? u.Union([
66
72
  r,
67
73
  t,
68
74
  e
69
- ]) : l.Union([t, e]), p = i(o.guards) ? l.Union([f, n]) : f, m = d && d.length > 0 ? l.Union([p, ...d]) : p;
70
- return u.make(o.name, {
71
- payload: s(y(o.key), o.name, c(o.guards)),
72
- success: _(o.payload),
75
+ ]) : u.Union([t, e]), p = i(o.guards) ? u.Union([f, n]) : f, m = l && l.length > 0 ? u.Union([p, ...l]) : p;
76
+ return d.make(o.name, {
77
+ payload: s(b(o.key), o.name, c(o.guards)),
78
+ success: v(o.payload),
73
79
  error: m,
74
80
  stream: !0
75
- });
76
- }, x = class extends l.TaggedError()("EventPayloadInvalid", {
77
- event: l.String,
78
- message: l.String
79
- }) {}, S = class extends l.TaggedError()("EventKeyInvalid", {
80
- event: l.String,
81
- message: l.String
82
- }) {}, C = class extends l.TaggedError()("EventPayloadTooLarge", {
83
- event: l.String,
84
- bytes: l.Number,
85
- limit: l.Number
86
- }) {}, w = (e) => {
81
+ }).annotate(x, !0);
82
+ }, C = class extends u.TaggedError()("EventPayloadInvalid", {
83
+ event: u.String,
84
+ message: u.String
85
+ }) {}, w = class extends u.TaggedError()("EventKeyInvalid", {
86
+ event: u.String,
87
+ message: u.String
88
+ }) {}, T = class extends u.TaggedError()("EventPayloadTooLarge", {
89
+ event: u.String,
90
+ bytes: u.Number,
91
+ limit: u.Number
92
+ }) {}, E = (e) => {
87
93
  if (typeof e != "object" || !e) return JSON.stringify(e) ?? "null";
88
94
  let t = Object.entries(e), n = 0;
89
95
  for (let e of t) e[1] !== void 0 && (t[n++] = e);
90
96
  return t.length = n, t.sort(([e], [t]) => e < t ? -1 : +(e > t)), JSON.stringify(t);
91
- }, T = "\0", E = (e, t, n) => [
97
+ }, D = "\0", O = (e, t, n) => [
92
98
  e ?? "~",
93
99
  t,
94
- w(n)
95
- ].join("\0"), D = (e) => {
100
+ E(n)
101
+ ].join("\0"), k = (e) => {
96
102
  let [t = "~", n = "", r = ""] = e.split("\0");
97
103
  return {
98
104
  tenantId: t === "~" ? null : t,
99
105
  event: n,
100
106
  key: r
101
107
  };
102
- }, O = (e) => e.split("\0").join(" · ");
108
+ }, A = (e) => e.split("\0").join(" · ");
103
109
  //#endregion
104
- export { T as EVENT_ROUTE_SEP, S as EventKeyInvalid, x as EventPayloadInvalid, C as EventPayloadTooLarge, d as MAX_EVENT_ENVELOPE_BYTES, p as defineEvent, w as encodeEventKey, g as eventAttached, m as eventEnvelope, h as eventGap, v as eventResumePoint, E as eventRoute, _ as eventStreamEvent, y as eventSubscribeInput, b as eventToRpc, O as formatEventRoute, f as isEventDescriptor, D as parseEventRoute };
110
+ export { D as EVENT_ROUTE_SEP, w as EventKeyInvalid, C as EventPayloadInvalid, T as EventPayloadTooLarge, x as EventRpcWorkload, f as MAX_EVENT_ENVELOPE_BYTES, m as defineEvent, E as encodeEventKey, _ as eventAttached, h as eventEnvelope, g as eventGap, y as eventResumePoint, O as eventRoute, v as eventStreamEvent, b as eventSubscribeInput, S as eventToRpc, A as formatEventRoute, p as isEventDescriptor, k as parseEventRoute };
@@ -0,0 +1,12 @@
1
+ //#region src/formCapture.ts
2
+ var e = "__voltro_capture", t = "voltro:form-source", n = (e) => typeof e == "string" && e.length === 261 && /^1(?:\.[a-f0-9]{64}){4}$/.test(e) ? e : void 0, r = (e, t) => {
3
+ let r = n(e + "." + t);
4
+ if (r === void 0) throw Error("Native form source unavailable");
5
+ return r;
6
+ }, i = (e = globalThis.document) => {
7
+ if (e === void 0) return;
8
+ let r = e.querySelectorAll(`meta[name="${t}"]`);
9
+ return r.length === 1 ? n(r[0].getAttribute("content")) : void 0;
10
+ };
11
+ //#endregion
12
+ export { e as FORM_CAPTURE_FIELD, t as FORM_CAPTURE_META_NAME, r as encodeNativeFormCapture, n as formCaptureSource, i as readFormCaptureSource };
@@ -0,0 +1,9 @@
1
+ //#region src/httpStreamMaxDuration.ts
2
+ var e = (e) => {
3
+ if (e !== void 0) {
4
+ if (typeof e != "number" || !Number.isSafeInteger(e) || e < 1 || e > 2147483647) throw RangeError("SSE maxDurationMs must be an integer between 1 and 2147483647.");
5
+ return e;
6
+ }
7
+ };
8
+ //#endregion
9
+ export { e as validateHttpStreamMaxDuration };