@voltro/protocol 0.85.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.
Files changed (80) hide show
  1. package/CHANGELOG.md +5987 -0
  2. package/README.md +14 -0
  3. package/THIRD-PARTY-NOTICES.md +1122 -31
  4. package/dist/apiRoleEndpoints.js +108 -0
  5. package/dist/apiRoleObservation.js +68 -0
  6. package/dist/apikey.d.ts +10 -0
  7. package/dist/approval.js +64 -0
  8. package/dist/approvalDescriptors.js +26 -0
  9. package/dist/auth.js +389 -0
  10. package/dist/boundedRing.js +27 -0
  11. package/dist/businessRule.js +11 -0
  12. package/dist/canonicalJson.js +14 -0
  13. package/dist/connection.js +73 -0
  14. package/dist/devActivity.js +4 -0
  15. package/dist/errorSchema.js +24 -0
  16. package/dist/errorValue.js +21 -0
  17. package/dist/event.js +110 -0
  18. package/dist/formCapture.js +12 -0
  19. package/dist/httpStreamMaxDuration.js +9 -0
  20. package/dist/idempotency.js +121 -0
  21. package/dist/idempotencyInput.js +123 -0
  22. package/dist/identity.js +5 -9
  23. package/dist/index.d.ts +1095 -139
  24. package/dist/index.js +54 -1700
  25. package/dist/inspect.d.ts +1 -1
  26. package/dist/inspect.js +14 -240
  27. package/dist/inspectBackfill.js +52 -0
  28. package/dist/inspectCommandRejection.js +21 -0
  29. package/dist/inspectProjection.js +133 -0
  30. package/dist/inspectStorageProjection.js +86 -0
  31. package/dist/inspectValueIdentity.js +4 -0
  32. package/dist/inspectWorkflowExecution.js +34 -0
  33. package/dist/inspectWorkflowQueuedStart.js +31 -0
  34. package/dist/jsonSchema.js +54 -0
  35. package/dist/jwt.d.ts +10 -0
  36. package/dist/jwt.js +1 -1
  37. package/dist/outbox.js +10 -0
  38. package/dist/pairing.js +75 -0
  39. package/dist/plugin.js +87 -0
  40. package/dist/pluginAccountMerge.js +88 -0
  41. package/dist/pluginNaming.js +32 -0
  42. package/dist/pluginPrivacy.js +166 -0
  43. package/dist/procedure.js +280 -0
  44. package/dist/publicApi.js +325 -0
  45. package/dist/queryOptionalSources.js +14 -0
  46. package/dist/reactivityChannel.js +34 -0
  47. package/dist/rest.d.ts +260 -64
  48. package/dist/rest.js +345 -499
  49. package/dist/restFormat.js +163 -0
  50. package/dist/restReceipt.js +43 -0
  51. package/dist/revalidate.js +13 -0
  52. package/dist/rowEquality.js +31 -0
  53. package/dist/rowPatch.js +182 -0
  54. package/dist/schemaShape.js +67 -0
  55. package/dist/scopes.js +273 -0
  56. package/dist/secretWindows.js +71 -0
  57. package/dist/serverErrorBus.js +15 -0
  58. package/dist/serviceCall.js +14 -0
  59. package/dist/session.d.ts +89 -6
  60. package/dist/session.js +62 -63
  61. package/dist/{sessionHeader-DriTZhmv.js → sessionHeader.js} +1 -1
  62. package/dist/ssrfPolicy.js +30 -0
  63. package/dist/storageAccess.js +14 -0
  64. package/dist/storeErrors.js +66 -0
  65. package/dist/storeScreenshots.js +90 -0
  66. package/dist/strictInput.js +27 -0
  67. package/dist/subscriptionEvent.js +37 -0
  68. package/dist/tenantSelection.js +40 -0
  69. package/dist/time.js +7 -0
  70. package/dist/undo.js +38 -0
  71. package/dist/validation.js +39 -0
  72. package/dist/webSocketGateway.js +16 -0
  73. package/dist/workflow.js +297 -0
  74. package/dist/workflowAccess.js +61 -0
  75. package/dist/workflowRestart.js +45 -0
  76. package/dist/workflowRetry.js +44 -0
  77. package/dist/workflowStartOutcome.js +30 -0
  78. package/package.json +5 -3
  79. package/dist/restFormat-BlgTLxm1.js +0 -298
  80. package/dist/scopes-ClywBij8.js +0 -548
package/dist/session.js CHANGED
@@ -1,99 +1,101 @@
1
- import { j as e, nt as t, z as n } from "./scopes-ClywBij8.js";
2
- import { n as r, t as i } from "./sessionHeader-DriTZhmv.js";
3
- import { Schema as a } from "effect";
4
- import { createHmac as o, timingSafeEqual as s } from "node:crypto";
5
- import { createLogger as c } from "@voltro/logger";
1
+ import { SubjectIdentity as e, subjectIdentity as t } from "./auth.js";
2
+ import { subjectScopes as n } from "./scopes.js";
3
+ import { SESSION_HEADER_NAME as r, sessionValueFromHeaders as i } from "./sessionHeader.js";
4
+ import { applySecretWindows as a, bearerMatches as o, clearSecretWindows as s, drainSecretActivity as c, noteConfigured as l, noteSigned as u, noteSigningLifetime as d, previousAccepted as f, secretFingerprint as p, setSecretWindow as m } from "./secretWindows.js";
5
+ import { Schema as h } from "effect";
6
+ import { createHmac as g, timingSafeEqual as _ } from "node:crypto";
7
+ import { createLogger as v } from "@voltro/logger";
6
8
  //#region src/session.ts
7
- var l = c({ scope: "@voltro/protocol:session" }), u = 2, d = a.Struct({
8
- v: a.Literal(2),
9
- subject: n,
10
- exp: a.Number,
11
- iat: a.Number,
12
- kid: a.optional(a.String)
13
- }), f = "k0", p = "k-previous", m = () => {
9
+ var y = v({ scope: "@voltro/protocol:session" }), b = 2, x = h.Struct({
10
+ v: h.Literal(2),
11
+ subject: e,
12
+ exp: h.Number,
13
+ iat: h.Number,
14
+ kid: h.optional(h.String)
15
+ }), S = "k0", C = "k-previous", w = () => {
14
16
  throw Error("VOLTRO_SESSION_SECRET is not set. It signs and verifies session cookies, and the framework refuses to invent one at this point: a fixed fallback would be readable by anyone with the source, making every session forgeable. `voltro dev` mints a project-local value into .env.local on boot; for a deployment, generate one with `voltro secret generate session`.");
15
- }, h = () => process.env.VOLTRO_SESSION_SECRET ?? m(), g = () => v() ?? { current: {
16
- kid: _(),
17
- secret: h()
18
- } }, _ = () => process.env.VOLTRO_SESSION_KID ?? "k0", v = () => {
17
+ }, T = () => process.env.VOLTRO_SESSION_SECRET ?? w(), E = () => O() ?? { current: {
18
+ kid: D(),
19
+ secret: T()
20
+ } }, D = () => process.env.VOLTRO_SESSION_KID ?? "k0", O = () => {
19
21
  let e = process.env.VOLTRO_SESSION_SECRET;
20
22
  if (e === void 0 || e === "") return;
21
23
  let t = {
22
- kid: _(),
24
+ kid: D(),
23
25
  secret: e
24
26
  }, n = process.env.VOLTRO_SESSION_SECRET_PREVIOUS;
25
- return n ? {
27
+ return l("session", e, n), n && f("session", n) ? {
26
28
  current: t,
27
29
  previous: {
28
30
  kid: process.env.VOLTRO_SESSION_KID_PREVIOUS ?? "k-previous",
29
31
  secret: n
30
32
  }
31
33
  } : { current: t };
32
- }, y = 32, b = (e = process.env) => {
34
+ }, k = 32, A = (e = process.env) => {
33
35
  let t = e.VOLTRO_SESSION_SECRET;
34
36
  if (t === void 0 || t === "") throw Error("VOLTRO_SESSION_SECRET is not set — refusing to serve. It signs session cookies, and there is no fallback: a built-in one would be readable by anyone with the source. Generate one with `voltro secret generate session`. (`voltro dev` mints a project-local value automatically; a deployment needs its own.)");
35
37
  if (t.length < 32) throw Error(`VOLTRO_SESSION_SECRET is only ${t.length} chars — too short for a session secret (need >= 32). This looks like a placeholder. Generate a real one with \`voltro secret generate session\`.`);
36
- }, x = a.encodeSync(d), S = a.decodeUnknownSync(d), C = (e) => (typeof e == "string" ? Buffer.from(e, "utf8") : e).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""), w = (e) => {
38
+ }, j = h.encodeSync(x), M = h.decodeUnknownSync(x), N = (e) => (typeof e == "string" ? Buffer.from(e, "utf8") : e).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""), P = (e) => {
37
39
  let t = e.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - e.length % 4) % 4);
38
40
  return Buffer.from(t, "base64");
39
- }, T = 604800, E = .7, D = (e, t) => C(o("sha256", t).update(e).digest()), O = (e) => typeof e == "string" ? {
41
+ }, F = 604800, I = .7, L = (e, t) => N(g("sha256", t).update(e).digest()), R = (e) => typeof e == "string" ? {
40
42
  kid: "k0",
41
43
  secret: e
42
- } : e, k = (n, r, i = {}) => {
43
- let a = O(r);
44
+ } : e, z = (e, r, i = {}) => {
45
+ let a = R(r);
44
46
  if (!a.secret) throw Error("signSession: secret must be a non-empty string");
45
- let o = e(n);
46
- 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.`);
47
- let s = i.ttlSeconds ?? T, c = Math.floor((i.now ?? Date.now)() / 1e3), l = {
47
+ let o = n(e);
48
+ 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.`);
49
+ let s = i.ttlSeconds ?? F, c = Math.floor((i.now ?? Date.now)() / 1e3), l = {
48
50
  v: 2,
49
- subject: t(n),
51
+ subject: t(e),
50
52
  exp: c + s,
51
53
  iat: c,
52
54
  ...typeof r == "string" ? {} : { kid: a.kid }
53
- }, u = C(JSON.stringify(x(l)));
54
- return `${u}.${D(u, a.secret)}`;
55
- }, A = (e, t, n, r) => {
56
- let i = o("sha256", n).update(e).digest(), a = w(t);
57
- if (a.length !== i.length || !s(a, i)) return null;
55
+ }, d = N(JSON.stringify(j(l)));
56
+ return u("session", a.secret, l.exp * 1e3, c * 1e3), `${d}.${L(d, a.secret)}`;
57
+ }, B = (e, t, n, r) => {
58
+ let i = g("sha256", n).update(e).digest(), a = P(t);
59
+ if (a.length !== i.length || !_(a, i)) return null;
58
60
  try {
59
- let t = w(e).toString("utf8"), n = S(JSON.parse(t));
61
+ let t = P(e).toString("utf8"), n = M(JSON.parse(t));
60
62
  return n.exp < Math.floor(r() / 1e3) ? null : n;
61
63
  } catch {
62
64
  return null;
63
65
  }
64
- }, j = (e, t) => {
66
+ }, V = (e, t) => {
65
67
  if (!e || !t) return null;
66
68
  let n = e.indexOf(".");
67
69
  if (n <= 0 || n === e.length - 1) return null;
68
- let r = A(e.slice(0, n), e.slice(n + 1), t, Date.now);
70
+ let r = B(e.slice(0, n), e.slice(n + 1), t, Date.now);
69
71
  return r ? r.subject : null;
70
- }, M = (e, t, n = {}) => {
72
+ }, H = (e, t, n = {}) => {
71
73
  if (!e) return null;
72
74
  let r = e.indexOf(".");
73
75
  if (r <= 0 || r === e.length - 1) return null;
74
76
  let i = e.slice(0, r), a = e.slice(r + 1), o = t.previous ? [t.current, t.previous] : [t.current], s = n.now ?? Date.now;
75
77
  for (let e of o) {
76
- if (!e.secret) continue;
77
- let t = A(i, a, e.secret, s);
78
- if (!t) continue;
79
- let r = n.renewFraction ?? E, o = Math.floor(s() / 1e3), c = t.exp - t.iat, l = t.iat + c * r;
78
+ if (!e.secret || e === t.previous && !f("session", e.secret, s())) continue;
79
+ let r = B(i, a, e.secret, s);
80
+ if (!r) continue;
81
+ let o = n.renewFraction ?? I, c = Math.floor(s() / 1e3), l = r.exp - r.iat, u = r.iat + l * o;
80
82
  return {
81
- subject: t.subject,
82
- renew: o >= l,
83
+ subject: r.subject,
84
+ renew: c >= u,
83
85
  kid: e.kid,
84
- exp: t.exp,
85
- iat: t.iat
86
+ exp: r.exp,
87
+ iat: r.iat
86
88
  };
87
89
  }
88
90
  return null;
89
- }, N = (e, t) => {
91
+ }, U = (e, t) => {
90
92
  let n = Buffer.from(e, "utf8"), r = Buffer.from(t, "utf8");
91
- return n.length === r.length && s(n, r);
92
- }, P = (e, t, n) => {
93
+ return n.length === r.length && _(n, r);
94
+ }, W = (e, t, n) => {
93
95
  if (!t || t.length === 0) return n?.openWhenUnset === !0;
94
96
  let r = e.authorization ?? e.Authorization;
95
- return !r || !r.startsWith("Bearer ") ? !1 : N(r.slice(7).trim(), t);
96
- }, F = (e, t) => {
97
+ return !r || !r.startsWith("Bearer ") ? !1 : U(r.slice(7).trim(), t);
98
+ }, G = (e, t) => {
97
99
  if (e) for (let n of e.split(";")) {
98
100
  let e = n.indexOf("=");
99
101
  if (e < 0 || n.slice(0, e).trim() !== t) continue;
@@ -105,26 +107,23 @@ var l = c({ scope: "@voltro/protocol:session" }), u = 2, d = a.Struct({
105
107
  return r;
106
108
  }
107
109
  }
108
- }, I = (e, t, n = {}) => {
110
+ }, K = (e, t, n = {}) => {
109
111
  let r = [`${e}=${encodeURIComponent(t)}`];
110
112
  if (r.push(`Path=${n.path ?? "/"}`), n.maxAgeSeconds !== void 0 && r.push(`Max-Age=${n.maxAgeSeconds}`), r.push("HttpOnly"), n.httpOnly === !1) {
111
113
  let e = r.indexOf("HttpOnly");
112
114
  e >= 0 && r.splice(e, 1);
113
115
  }
114
116
  return r.push(`SameSite=${n.sameSite ?? "Lax"}`), (n.secure ?? !0) && r.push("Secure"), n.domain && r.push(`Domain=${n.domain}`), r.join("; ");
115
- }, L = process.env.VOLTRO_SESSION_COOKIE ?? "voltro:session", R = (e, t = L) => r(e, t, F), z = (e) => {
116
- let t = r(e, L, F);
117
+ }, q = process.env.VOLTRO_SESSION_COOKIE ?? "voltro:session", J = (e, t = q) => i(e, t, G), Y = (e) => {
118
+ let t = i(e, q, G);
117
119
  if (!t) return;
118
- let n = v();
119
- if (!n) {
120
- V();
121
- return;
122
- }
123
- return M(t, n)?.exp;
124
- }, B = !1, V = () => {
125
- B || (B = !0, l.error("a `voltro:session` cookie was presented but VOLTRO_SESSION_SECRET is not set — the session cannot be verified, so no credential-expiry bound is imposed and realtime subscriptions on this connection will not be cut off when the session expires. Set VOLTRO_SESSION_SECRET (`voltro secret generate session`); `voltro dev` mints one, `voltro serve` refuses to boot without one.", { cookieName: L }));
126
- }, H = () => {
127
- B = !1;
120
+ let n = O();
121
+ if (!n) Z();
122
+ else return H(t, n)?.exp;
123
+ }, X = !1, Z = () => {
124
+ X || (X = !0, y.error("a `voltro:session` cookie was presented but VOLTRO_SESSION_SECRET is not set — the session cannot be verified, so no credential-expiry bound is imposed and realtime subscriptions on this connection will not be cut off when the session expires. Set VOLTRO_SESSION_SECRET (`voltro secret generate session`); `voltro dev` mints one, `voltro serve` refuses to boot without one.", { cookieName: q }));
125
+ }, Q = () => {
126
+ X = !1;
128
127
  };
129
128
  //#endregion
130
- export { p as DEFAULT_PREVIOUS_SESSION_KID, f as DEFAULT_SESSION_KID, y as MIN_SESSION_SECRET_LENGTH, L as SESSION_COOKIE_NAME, i as SESSION_HEADER_NAME, u as SESSION_PAYLOAD_VERSION, b as assertProductionSessionSecret, I as buildSetCookie, P as checkBearer, F as readCookie, R as readSessionValue, H as resetSessionSecretWarningLatch, v as resolveOptionalSessionSecrets, h as resolveSessionSecret, g as resolveSessionSecrets, z as sessionExpiryFromHeaders, k as signSession, N as timingSafeStringEqual, j as verifySession, M as verifySessionKeyed };
129
+ export { C as DEFAULT_PREVIOUS_SESSION_KID, S as DEFAULT_SESSION_KID, k as MIN_SESSION_SECRET_LENGTH, q as SESSION_COOKIE_NAME, r as SESSION_HEADER_NAME, b as SESSION_PAYLOAD_VERSION, a as applySecretWindows, A as assertProductionSessionSecret, o as bearerMatches, K as buildSetCookie, W as checkBearer, s as clearSecretWindows, c as drainSecretActivity, l as noteConfigured, u as noteSigned, d as noteSigningLifetime, f as previousAccepted, G as readCookie, J as readSessionValue, Q as resetSessionSecretWarningLatch, O as resolveOptionalSessionSecrets, T as resolveSessionSecret, E as resolveSessionSecrets, p as secretFingerprint, Y as sessionExpiryFromHeaders, m as setSecretWindow, z as signSession, U as timingSafeStringEqual, V as verifySession, H as verifySessionKeyed };
@@ -4,4 +4,4 @@ var e = "x-voltro-session", t = (e, t, n) => {
4
4
  return r !== void 0 && r.length > 0 ? r : n(e.cookie ?? e.Cookie, t);
5
5
  };
6
6
  //#endregion
7
- export { t as n, e as t };
7
+ export { e as SESSION_HEADER_NAME, t as sessionValueFromHeaders };
@@ -0,0 +1,30 @@
1
+ //#region src/ssrfPolicy.ts
2
+ var e = (e, t) => e === 0 || e === 10 || e === 127 || e === 100 && t >= 64 && t <= 127 || e === 169 && t === 254 || e === 172 && t >= 16 && t <= 31 || e === 192 && t === 168 || e === 198 && (t === 18 || t === 19) || e >= 224, t = (e) => e === "::" || e === "::1" || e.startsWith("fc") || e.startsWith("fd") || e.startsWith("fe80") || e.startsWith("::ffff:"), n = (e, t, n) => n.some((n) => {
3
+ let r = n.trim().toLowerCase();
4
+ if (r === "") return !1;
5
+ let i = t === "" ? e : `${e}:${t}`;
6
+ return r === e || r === i ? !0 : r.startsWith("*.") ? e.endsWith(r.slice(1)) : !1;
7
+ }), r = (i, a = {}) => {
8
+ let o;
9
+ try {
10
+ o = new URL(i);
11
+ } catch {
12
+ return "malformed url";
13
+ }
14
+ if (o.protocol !== "http:" && o.protocol !== "https:") return `blocked scheme "${o.protocol}"`;
15
+ let s = o.hostname.replace(/^\[|\]$/g, "").toLowerCase();
16
+ if (n(s, o.port, a.allowHosts ?? [])) return null;
17
+ if (s === "localhost" || s.endsWith(".localhost") || s.endsWith(".internal") || s.endsWith(".local")) return `blocked host "${s}"`;
18
+ let c = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(s);
19
+ if (c) {
20
+ let t = Number(c[1]), n = Number(c[2]);
21
+ return t > 255 || n > 255 || Number(c[3]) > 255 || Number(c[4]) > 255 ? "malformed ipv4 literal" : e(t, n) ? `blocked private/reserved address ${s}` : null;
22
+ }
23
+ if (s.includes(":")) {
24
+ let e = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(s);
25
+ return e ? r(`${o.protocol}//${e[1]}`, a) : t(s) ? `blocked private/reserved address ${s}` : null;
26
+ }
27
+ return null;
28
+ };
29
+ //#endregion
30
+ export { r as ssrfReason };
@@ -0,0 +1,14 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/storageAccess.ts
3
+ var t = e.Array(e.String).pipe(e.check(e.isMaxLength(64))), n = e.Struct({
4
+ owner: e.optional(e.Boolean),
5
+ roles: e.optional(t),
6
+ groups: e.optional(t),
7
+ scopes: e.optional(t),
8
+ tenant: e.optional(e.Boolean),
9
+ apiKey: e.optional(e.Boolean),
10
+ password: e.optional(e.Boolean),
11
+ guard: e.optional(e.String)
12
+ }), r = e.Array(n).pipe(e.check(e.isMaxLength(32)));
13
+ //#endregion
14
+ export { r as StorageAccessPolicySchema, n as StorageAccessRuleSchema };
@@ -0,0 +1,66 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/storeErrors.ts
3
+ var t = class extends e.TaggedError()("TenantScopeViolation", {
4
+ table: e.String,
5
+ reason: e.String
6
+ }) {}, n = class extends e.TaggedError()("StoreOperationFailed", {
7
+ operation: e.String,
8
+ table: e.String,
9
+ cause: e.String
10
+ }) {
11
+ get message() {
12
+ return `${this.operation} on '${this.table}' failed: ${this.cause}`;
13
+ }
14
+ }, r = class extends e.TaggedError()("NoRowFound", {
15
+ table: e.String,
16
+ found: e.Number
17
+ }) {
18
+ get message() {
19
+ return this.found === 0 ? `expected exactly one row in '${this.table}', found none` : `expected exactly one row in '${this.table}', found more than one`;
20
+ }
21
+ }, i = class extends e.TaggedError()("TableValidationFailed", {
22
+ table: e.String,
23
+ summary: e.String,
24
+ issues: e.Array(e.Struct({
25
+ path: e.String,
26
+ message: e.String
27
+ }))
28
+ }) {
29
+ get message() {
30
+ return this.summary;
31
+ }
32
+ }, a = class extends e.TaggedError()("TenantRowNotFound", {
33
+ table: e.String,
34
+ id: e.String,
35
+ reason: e.String
36
+ }) {}, o = class extends e.TaggedError()("ServerOnlyColumnWrite", {
37
+ table: e.String,
38
+ columns: e.Array(e.String)
39
+ }) {}, s = class extends e.TaggedError()("ConstraintViolation", {
40
+ kind: e.Literals([
41
+ "foreignKey",
42
+ "foreignKeyInUse",
43
+ "unique",
44
+ "notNull",
45
+ "check"
46
+ ]),
47
+ table: e.String,
48
+ operation: e.String,
49
+ constraint: e.optional(e.String),
50
+ column: e.optional(e.String),
51
+ detail: e.optional(e.String)
52
+ }) {
53
+ get message() {
54
+ if (this.detail !== void 0) return this.detail;
55
+ let e = this.constraint ?? this.column, t = e === void 0 ? `on ${this.table}` : `${e} on ${this.table}`;
56
+ switch (this.kind) {
57
+ case "foreignKey": return `foreign key ${t}: the referenced row does not exist`;
58
+ case "foreignKeyInUse": return `foreign key ${t}: the row is still referenced by other rows`;
59
+ case "unique": return `unique constraint ${t}: a row with this value already exists`;
60
+ case "notNull": return `not-null constraint ${t}: the column requires a value`;
61
+ case "check": return `check constraint ${t}: the row does not satisfy it`;
62
+ }
63
+ }
64
+ };
65
+ //#endregion
66
+ export { s as ConstraintViolation, r as NoRowFound, o as ServerOnlyColumnWrite, n as StoreOperationFailed, i as TableValidationFailed, a as TenantRowNotFound, t as TenantScopeViolation };
@@ -0,0 +1,90 @@
1
+ //#region src/storeScreenshots.ts
2
+ var e = [
3
+ "app-store",
4
+ "play",
5
+ "mac-app-store",
6
+ "microsoft-store"
7
+ ], t = [
8
+ {
9
+ name: "iphone-6.7",
10
+ label: "iPhone 6.7″ — 1290×2796",
11
+ kind: "phone",
12
+ viewport: {
13
+ width: 430,
14
+ height: 932
15
+ },
16
+ deviceScaleFactor: 3,
17
+ stores: ["app-store"]
18
+ },
19
+ {
20
+ name: "iphone-6.5",
21
+ label: "iPhone 6.5″ — 1242×2688",
22
+ kind: "phone",
23
+ viewport: {
24
+ width: 414,
25
+ height: 896
26
+ },
27
+ deviceScaleFactor: 3,
28
+ stores: ["app-store"]
29
+ },
30
+ {
31
+ name: "ipad-12.9",
32
+ label: "iPad 12.9″ — 2048×2732",
33
+ kind: "tablet",
34
+ viewport: {
35
+ width: 1024,
36
+ height: 1366
37
+ },
38
+ deviceScaleFactor: 2,
39
+ stores: ["app-store"]
40
+ },
41
+ {
42
+ name: "android-phone",
43
+ label: "Android phone — 1080×2160",
44
+ kind: "phone",
45
+ viewport: {
46
+ width: 360,
47
+ height: 720
48
+ },
49
+ deviceScaleFactor: 3,
50
+ stores: ["play"]
51
+ },
52
+ {
53
+ name: "android-tablet-10",
54
+ label: "Android 10″ tablet — 1600×2560",
55
+ kind: "tablet",
56
+ viewport: {
57
+ width: 800,
58
+ height: 1280
59
+ },
60
+ deviceScaleFactor: 2,
61
+ stores: ["play"]
62
+ },
63
+ {
64
+ name: "mac",
65
+ label: "Mac — 2880×1800",
66
+ kind: "desktop",
67
+ viewport: {
68
+ width: 1440,
69
+ height: 900
70
+ },
71
+ deviceScaleFactor: 2,
72
+ stores: ["mac-app-store"]
73
+ },
74
+ {
75
+ name: "windows",
76
+ label: "Windows — 1366×768",
77
+ kind: "desktop",
78
+ viewport: {
79
+ width: 1366,
80
+ height: 768
81
+ },
82
+ deviceScaleFactor: 1,
83
+ stores: ["microsoft-store"]
84
+ }
85
+ ], n = t.map((e) => e.name), r = (e) => t.find((t) => t.name === e), i = (e) => ({
86
+ width: e.viewport.width * e.deviceScaleFactor,
87
+ height: e.viewport.height * e.deviceScaleFactor
88
+ }), a = (e) => t.filter((t) => t.stores.includes(e));
89
+ //#endregion
90
+ export { t as STORE_SCREENSHOT_PRESETS, n as STORE_SCREENSHOT_PRESET_NAMES, e as STORE_SCREENSHOT_STORES, i as storeScreenshotPixels, r as storeScreenshotPreset, a as storeScreenshotPresetsFor };
@@ -0,0 +1,27 @@
1
+ import { Effect as e, Schema as t, SchemaIssue as n } from "effect";
2
+ import * as r from "effect/SchemaAST";
3
+ //#region src/strictInput.ts
4
+ var i = (e, t) => {
5
+ let n = `${e ?? "this procedure"} input`;
6
+ return t === !0 ? `${n} (guarded — the guard did NOT run: the payload failed to decode first, so this says nothing about access)` : n;
7
+ }, a = n.makeFormatterDefault(), o = (e) => t.Never.annotate({ message: e.size === 0 ? "is unexpected: the input declares no keys" : `is unexpected: the input declares ${[...e].map(String).join(", ")}` }).ast, s = (e) => t.String.check(t.makeFilter((t) => e.has(t) ? "a declared key" : void 0)).ast, c = (e, t = /* @__PURE__ */ new Map()) => {
8
+ let n = t.get(e);
9
+ if (n !== void 0) return n;
10
+ let r = l(e, t);
11
+ return t.set(e, r), r;
12
+ }, l = (e, n) => {
13
+ if (e.encoding !== void 0) {
14
+ let i = r.toEncoded(e), a = c(i, n);
15
+ return a === i ? e : t.make(a).pipe(t.decodeTo(t.make(e))).ast;
16
+ }
17
+ if (r.isObjects(e)) {
18
+ let t = e.propertySignatures.map((e) => new r.PropertySignature(e.name, c(e.type, n))), i = new Set(t.map((e) => e.name)), a = e.indexSignatures.length > 0 ? e.indexSignatures.map((e) => new r.IndexSignature(e.parameter, c(e.type, n))) : [new r.IndexSignature(s(i), o(i))];
19
+ return new r.Objects(t, a, e.annotations, e.checks, void 0, e.context);
20
+ }
21
+ return r.isArrays(e) ? new r.Arrays(e.isMutable, e.elements.map((e) => c(e, n)), e.rest.map((e) => c(e, n)), e.annotations, e.checks, void 0, e.context) : r.isUnion(e) ? new r.Union(e.types.map((e) => c(e, n)), e.options, e.annotations, e.checks, void 0, e.context) : r.isSuspend(e) ? new r.Suspend(() => c(e.thunk(), n), e.annotations, e.checks, void 0, e.context) : e;
22
+ }, u = (r, o, s) => {
23
+ let l = i(o, s);
24
+ return t.make(c(r.ast)).pipe(t.middlewareDecoding((t) => e.catch(t, (t) => e.fail(new n.InvalidValue({ message: `${l}\n${a(t)}` })))));
25
+ };
26
+ //#endregion
27
+ export { i as inputLabel, u as strictInput };
@@ -0,0 +1,37 @@
1
+ import { rowPatchSchema as e } from "./rowPatch.js";
2
+ import { Schema as t, SchemaGetter as n } from "effect";
3
+ //#region src/subscriptionEvent.ts
4
+ var r = t.Json.pipe(t.decodeTo(t.Unknown, {
5
+ decode: n.passthrough({ strict: !1 }),
6
+ encode: n.transform((e) => JSON.parse(JSON.stringify(e)))
7
+ })), i = (n) => t.Union([
8
+ t.Struct({
9
+ _tag: t.Literal("snapshot"),
10
+ revision: t.Number,
11
+ data: n,
12
+ computed: t.optional(t.Boolean),
13
+ reconciles: t.optional(t.String)
14
+ }),
15
+ t.Struct({
16
+ _tag: t.Literal("delta"),
17
+ revision: t.Number,
18
+ emittedAt: t.Number,
19
+ patch: e
20
+ }),
21
+ t.Struct({
22
+ _tag: t.Literal("error"),
23
+ error: r,
24
+ revision: t.optional(t.Number)
25
+ })
26
+ ]), a = (e, t, n) => {
27
+ if (!t || typeof t != "object" || !("_tag" in t)) return t;
28
+ let r = t;
29
+ switch (r._tag) {
30
+ case "snapshot": return r.data;
31
+ case "error": throw r.error === void 0 ? Error(`${n} ${e}: the rpc refused without an error value`) : r.error;
32
+ case "delta": throw Error(`${n} ${e}: received a 'delta' as the first event, which should be impossible — a subscription always opens with a snapshot. This is a framework bug.`);
33
+ default: return t;
34
+ }
35
+ };
36
+ //#endregion
37
+ export { a as firstEventValue, i as subscriptionEvent };
@@ -0,0 +1,40 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/tenantSelection.ts
3
+ var t = class extends e.TaggedError()("RequestedTenantRefused", { reason: e.Literals([
4
+ "target-unavailable",
5
+ "invalid-target",
6
+ "memberships-unavailable",
7
+ "not-a-member",
8
+ "subject-has-no-tenant"
9
+ ]) }) {}, n = (e) => e.type === "user" || e.type === "apiKey" || e.type === "serviceAccount", r = class extends e.TaggedError()("TenantSelectionRefused", {
10
+ subjectType: e.String,
11
+ subjectId: e.NullOr(e.String),
12
+ tenantId: e.String,
13
+ reason: e.Literals(["not-a-member", "subject-has-no-tenant"])
14
+ }) {}, i = (e, t, i) => {
15
+ if (!n(e)) throw new r({
16
+ subjectType: e.type,
17
+ subjectId: e.id,
18
+ tenantId: t,
19
+ reason: "subject-has-no-tenant"
20
+ });
21
+ if (!i.some((e) => e.tenantId === t)) throw new r({
22
+ subjectType: e.type,
23
+ subjectId: e.id,
24
+ tenantId: t,
25
+ reason: "not-a-member"
26
+ });
27
+ return {
28
+ ...e,
29
+ tenantId: t,
30
+ metadata: {
31
+ ...e.metadata,
32
+ memberships: i.map((e) => e.role === void 0 ? { tenantId: e.tenantId } : {
33
+ tenantId: e.tenantId,
34
+ role: e.role
35
+ })
36
+ }
37
+ };
38
+ };
39
+ //#endregion
40
+ export { t as RequestedTenantRefused, r as TenantSelectionRefused, n as isTenantSelectable, i as selectSubjectTenant };
package/dist/time.js ADDED
@@ -0,0 +1,7 @@
1
+ //#region src/time.ts
2
+ var e = (e) => {
3
+ let t = e instanceof Date ? e.getTime() : typeof e == "number" ? e : typeof e == "string" ? new Date(e).getTime() : 0;
4
+ return Number.isNaN(t) ? 0 : t;
5
+ };
6
+ //#endregion
7
+ export { e as tsMs };
package/dist/undo.js ADDED
@@ -0,0 +1,38 @@
1
+ import { defineMutation as e, defineQuery as t } from "./procedure.js";
2
+ import { Schema as n } from "effect";
3
+ //#region src/undo.ts
4
+ var r = "__voltro.undo.log", i = "__voltro.undo.apply", a = "__voltro.undo.redo", o = n.Struct({
5
+ id: n.String,
6
+ tag: n.String,
7
+ label: n.NullOr(n.String),
8
+ undone: n.Boolean,
9
+ crossesAction: n.Boolean,
10
+ createdAt: n.String
11
+ }), s = class extends n.TaggedError()("UndoNotFound", { invocationId: n.String }) {}, c = class extends n.TaggedError()("UndoForbidden", { invocationId: n.String }) {}, l = class extends n.TaggedError()("UndoConflict", {
12
+ invocationId: n.String,
13
+ reason: n.Literals(["conflict", "action"])
14
+ }) {}, u = n.Union([
15
+ s,
16
+ c,
17
+ l
18
+ ]), d = t({
19
+ name: r,
20
+ source: "_voltro_undo_log",
21
+ input: n.Struct({ limit: n.optional(n.Number) }),
22
+ output: n.Array(o),
23
+ openAccess: "subject-scoped by construction: lists only the calling subject's own undoable actions"
24
+ }), f = e({
25
+ name: i,
26
+ input: n.Struct({ invocationId: n.String }),
27
+ output: n.Struct({ ok: n.Boolean }),
28
+ error: u,
29
+ openAccess: "subject-scoped by construction: undo is per-actor — another subject's invocation fails typed with UndoForbidden"
30
+ }), p = e({
31
+ name: a,
32
+ input: n.Struct({ invocationId: n.String }),
33
+ output: n.Struct({ ok: n.Boolean }),
34
+ error: u,
35
+ openAccess: "subject-scoped by construction: redo is per-actor — another subject's invocation fails typed with UndoForbidden"
36
+ });
37
+ //#endregion
38
+ export { i as UNDO_APPLY_TAG, r as UNDO_LOG_TAG, a as UNDO_REDO_TAG, l as UndoConflict, c as UndoForbidden, o as UndoLogEntry, s as UndoNotFound, f as undoApplyDescriptor, d as undoLogQueryDescriptor, p as undoRedoDescriptor };
@@ -0,0 +1,39 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/validation.ts
3
+ var t = e.Struct({
4
+ field: e.String,
5
+ message: e.String,
6
+ params: e.optional(e.Record(e.String, e.Unknown))
7
+ }), n = class extends e.TaggedError()("ValidationError", {
8
+ field: e.String,
9
+ message: e.String,
10
+ params: e.optional(e.Record(e.String, e.Unknown))
11
+ }) {}, r = class extends e.TaggedError()("ValidationErrors", { issues: e.Array(t).pipe(e.check(e.isMinLength(1))) }) {}, i = (e) => {
12
+ if (typeof e != "object" || !e) return [];
13
+ let t = e._tag;
14
+ if (t === "ValidationError") {
15
+ let t = e;
16
+ return [{
17
+ field: t.field,
18
+ message: t.message,
19
+ ...t.params === void 0 ? {} : { params: t.params }
20
+ }];
21
+ }
22
+ if (t === "ValidationErrors") {
23
+ let t = e;
24
+ return Array.isArray(t.issues) ? t.issues : [];
25
+ }
26
+ if (t === "BusinessRuleViolation") {
27
+ let t = e;
28
+ if (typeof t.field != "string" || t.field.length === 0) return [];
29
+ let n = typeof t.message == "string" && t.message.length > 0 ? t.message : String(t.rule ?? "validation.invalid");
30
+ return [{
31
+ field: t.field,
32
+ message: n,
33
+ ...t.params === void 0 ? {} : { params: t.params }
34
+ }];
35
+ }
36
+ return [];
37
+ };
38
+ //#endregion
39
+ export { n as ValidationError, r as ValidationErrors, t as ValidationIssue, i as fieldIssuesOf };
@@ -0,0 +1,16 @@
1
+ //#region src/webSocketGateway.ts
2
+ var e = 4001, t = /* @__PURE__ */ new Set(["/rpc", "/ws"]), n = (e) => {
3
+ if (e.auth === "token") {
4
+ if (typeof e.verify != "function") throw Error("defineWebSocket: auth: 'token' requires verify(firstFrame, context)");
5
+ let t = e.verificationTimeoutMs;
6
+ if (t !== void 0 && (!Number.isSafeInteger(t) || t <= 0 || t > 2147483647)) throw Error("defineWebSocket: verificationTimeoutMs must be a positive safe integer at most 2147483647");
7
+ } else if (e.verify !== void 0 || e.verificationTimeoutMs !== void 0) throw Error("defineWebSocket: verify and verificationTimeoutMs require auth: 'token'");
8
+ if (e.limits?.burst !== void 0 && e.limits.maxInboundMessagesPerSecond === void 0) throw Error("defineWebSocket: limits.burst requires limits.maxInboundMessagesPerSecond");
9
+ for (let [t, n] of Object.entries(e.limits ?? {})) if (n !== void 0 && (!Number.isSafeInteger(n) || n <= 0)) throw Error(`defineWebSocket: limits.${t} must be a positive safe integer`);
10
+ if (!e.path.startsWith("/")) throw Error(`defineWebSocket: path must be absolute, got '${e.path}'`);
11
+ if (t.has(e.path)) throw Error(`defineWebSocket: '${e.path}' is the framework's own socket surface — mount the gateway elsewhere`);
12
+ if (e.auth !== "subject" && e.auth !== "public" && e.auth !== "token") throw Error(`defineWebSocket: auth must be 'subject', 'public' or 'token' (explicitly — an unauthenticated socket has to be a decision somebody wrote down), got '${String(e.auth)}'`);
13
+ return e;
14
+ };
15
+ //#endregion
16
+ export { e as GATEWAY_CREDENTIAL_EXPIRED_CLOSE_CODE, n as defineWebSocket };