@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
@@ -0,0 +1,27 @@
1
+ //#region src/boundedRing.ts
2
+ var e = class {
3
+ slots = [];
4
+ head = 0;
5
+ size = 0;
6
+ capacity;
7
+ constructor(e) {
8
+ this.capacity = Number.isNaN(e) ? Infinity : Math.max(0, Math.floor(e));
9
+ }
10
+ get length() {
11
+ return this.size;
12
+ }
13
+ get(e) {
14
+ if (!(!Number.isInteger(e) || e < 0 || e >= this.size)) return this.slots[(this.head + e) % this.capacity];
15
+ }
16
+ push(e) {
17
+ this.capacity !== 0 && (this.size < this.capacity ? (this.slots[(this.head + this.size) % this.capacity] = e, this.size++) : (this.slots[this.head] = e, this.head = (this.head + 1) % this.capacity));
18
+ }
19
+ clear() {
20
+ this.slots.length = 0, this.head = 0, this.size = 0;
21
+ }
22
+ *[Symbol.iterator]() {
23
+ for (let e = 0; e < this.size; e++) yield this.get(e);
24
+ }
25
+ };
26
+ //#endregion
27
+ export { e as BoundedRing };
@@ -0,0 +1,11 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/businessRule.ts
3
+ var t = class extends e.TaggedError()("BusinessRuleViolation", {
4
+ rule: e.String,
5
+ params: e.optional(e.Record(e.String, e.Unknown)),
6
+ field: e.optional(e.String),
7
+ message: e.optional(e.String),
8
+ severity: e.Literals(["error", "warning"])
9
+ }) {};
10
+ //#endregion
11
+ export { t as BusinessRuleViolation };
@@ -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,73 @@
1
+ import { defineAction as e, defineMutation as t, defineQuery as n } from "./procedure.js";
2
+ import { Schema as r } from "effect";
3
+ //#region src/connection.ts
4
+ var i = "__voltro.connections.list", a = "__voltro.connections.start", o = "__voltro.connections.submitToken", s = "__voltro.connections.disconnect", c = r.Literals(["oauth2", "pat"]), l = r.Literals([
5
+ "disconnected",
6
+ "connected",
7
+ "expired",
8
+ "revoked",
9
+ "error"
10
+ ]), u = r.Struct({
11
+ connectionId: r.String,
12
+ kind: c,
13
+ label: r.String,
14
+ status: l,
15
+ accountId: r.NullOr(r.String),
16
+ accountLabel: r.NullOr(r.String),
17
+ scopes: r.Array(r.String),
18
+ expiresAt: r.NullOr(r.String),
19
+ lastError: r.NullOr(r.String),
20
+ connectedAt: r.NullOr(r.String)
21
+ }), d = class extends r.TaggedError()("ConnectionNotDeclared", { connectionId: r.String }) {}, f = class extends r.TaggedError()("ConnectionSubjectRequired", { connectionId: r.String }) {}, p = class extends r.TaggedError()("ConnectionKindMismatch", {
22
+ connectionId: r.String,
23
+ expected: c,
24
+ actual: c
25
+ }) {}, m = class extends r.TaggedError()("ConnectionHandshakeFailed", {
26
+ connectionId: r.String,
27
+ reason: r.String,
28
+ transient: r.Boolean
29
+ }) {
30
+ get message() {
31
+ return this.reason;
32
+ }
33
+ }, h = r.Union([
34
+ d,
35
+ f,
36
+ p,
37
+ m
38
+ ]), g = n({
39
+ name: i,
40
+ source: "_voltro_connections",
41
+ input: r.Struct({}),
42
+ output: r.Array(u),
43
+ openAccess: "self-scoped read: projects the declared connections for the calling subject only (its own connect/disconnect state)"
44
+ }), _ = e({
45
+ name: a,
46
+ input: r.Struct({
47
+ connectionId: r.String,
48
+ redirectTo: r.optional(r.String)
49
+ }),
50
+ output: r.Struct({
51
+ authorizeUrl: r.String,
52
+ state: r.String
53
+ }),
54
+ error: h,
55
+ openAccess: "self-service: begins an oauth handshake that stores a credential for the calling subject only; anonymous callers fail typed with ConnectionSubjectRequired"
56
+ }), v = t({
57
+ name: o,
58
+ input: r.Struct({
59
+ connectionId: r.String,
60
+ token: r.String
61
+ }),
62
+ output: r.Struct({ ok: r.Boolean }),
63
+ error: h,
64
+ openAccess: "self-service: stores a pasted token as the calling subject's own credential; anonymous callers fail typed with ConnectionSubjectRequired"
65
+ }), y = t({
66
+ name: s,
67
+ input: r.Struct({ connectionId: r.String }),
68
+ output: r.Struct({ ok: r.Boolean }),
69
+ error: h,
70
+ openAccess: "self-service: deletes only the calling subject's own credential row for this connection"
71
+ });
72
+ //#endregion
73
+ export { i as CONNECTIONS_LIST_TAG, s as CONNECTION_DISCONNECT_TAG, a as CONNECTION_START_TAG, o as CONNECTION_SUBMIT_TOKEN_TAG, m as ConnectionHandshakeFailed, c as ConnectionKind, p as ConnectionKindMismatch, d as ConnectionNotDeclared, u as ConnectionState, l as ConnectionStatus, f as ConnectionSubjectRequired, y as connectionDisconnectDescriptor, _ as connectionStartDescriptor, v as connectionSubmitTokenDescriptor, g as connectionsListQueryDescriptor };
@@ -0,0 +1,4 @@
1
+ //#region src/devActivity.ts
2
+ var e = "voltro:dev-activity";
3
+ //#endregion
4
+ export { e as DEV_ACTIVITY_EVENT };
@@ -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 };
@@ -0,0 +1,21 @@
1
+ //#region src/errorValue.ts
2
+ var e = (e) => {
3
+ if (typeof e != "object" || !e) return;
4
+ let t = e._tag;
5
+ return typeof t == "string" ? t : void 0;
6
+ }, t = (t) => {
7
+ if (typeof t == "string") return t;
8
+ if (typeof t == "object" && t) {
9
+ let n = t.message;
10
+ if (typeof n == "string" && n !== "") return n;
11
+ let r = e(t);
12
+ if (r !== void 0) return r;
13
+ }
14
+ return String(t);
15
+ }, n = (n) => {
16
+ if (n instanceof Error) return n;
17
+ let r = Error(t(n), { cause: n });
18
+ return r.name = e(n) ?? "NonErrorThrown", r;
19
+ };
20
+ //#endregion
21
+ export { t as errorMessage, e as errorTag, n as reportableError };
package/dist/event.js ADDED
@@ -0,0 +1,110 @@
1
+ import { RequestedTenantRefused as e } from "./tenantSelection.js";
2
+ import { Unauthenticated as t } from "./auth.js";
3
+ import { NoSuchResource as n, ScopeError as r, declaresResourceGuard as i, isOpenAccess as a, openAccessSpec as o } from "./scopes.js";
4
+ import { strictInput as s } from "./strictInput.js";
5
+ import { hasEnforcedGuard as c } from "./procedure.js";
6
+ import { Context as l, Schema as u } from "effect";
7
+ import { Rpc as d } from "effect/rpc";
8
+ //#region src/event.ts
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
+ }
15
+ if (e.name.length === 0) throw Error("defineEvent: `name` must not be empty");
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
17
+ whitespace and delivers nothing — silently, and only on that broker, so an
18
+ app that works on Redis stops working when the transport changes.
19
+ Use a dot for namespacing: "orders.paid".`);
20
+ if (e.guards !== void 0 && e.guards.length === 0) throw Error(`defineEvent("${e.name}"): \`guards: []\` is empty, so it enforces nothing —\n but it reads at the call site as if the event were protected. Omit the field
21
+ for an unguarded event, or list the scopes that may subscribe.`);
22
+ if (e.openAccess !== void 0) {
23
+ if (e.openAccess.trim() === "") throw Error(`defineEvent("${e.name}"): \`openAccess\` needs a REASON, not an empty string. It is\n the sentence a reviewer reads to decide whether anyone should really be able to
24
+ subscribe without an authorization check — e.g. \`openAccess: 'public scoreboard,
25
+ no caller data'\`.`);
26
+ if (e.guards !== void 0 && e.guards.length > 0) throw Error(`defineEvent("${e.name}"): \`openAccess\` and \`guards\` are two different access\n decisions, so declaring both says the event is protected AND open. Keep the guards
27
+ if a subscriber must hold a scope; drop them if anyone may listen.`);
28
+ }
29
+ if (e.webhook?.rateLimit !== void 0 && e.webhook.rateLimit.perMinute < 1) throw Error(`defineEvent("${e.name}"): \`webhook.rateLimit.perMinute\` is ${e.webhook.rateLimit.perMinute}, which defers every delivery forever.\n There is no "unlimited" spelling here — omit \`rateLimit\` for no ceiling.`);
30
+ if (e.webhook?.version !== void 0 && e.webhook.version < 1) throw Error(`defineEvent("${e.name}"): \`webhook.version\` must be >= 1 (default 1).\n A subscriber pinned to 1 would read this event as BEHIND its own version,
31
+ which is the opposite of what a version bump is for.`);
32
+ if (e.delivery === "latest" && e.webhook !== void 0) throw Error(`defineEvent(${e.name}): \`delivery: 'latest'\` cannot be combined with \`webhook\`.\n \`latest\` means a superseded delivery did not matter — but a webhook delivery is a
33
+ durable side effect at a third party, and one already sent cannot be superseded.
34
+ A high-rate event with an HTTP audience is also 60 deliveries per second per target;
35
+ the rate limit DEFERS the excess as pending rows, so it grows a table instead of failing.
36
+ Split them: publish the high-rate event for clients, and publish a separate, coarser
37
+ event (a summary, a state change) for the outside world.`);
38
+ return {
39
+ kind: "event",
40
+ name: e.name,
41
+ key: e.key,
42
+ payload: e.payload,
43
+ guards: e.openAccess === void 0 ? e.guards : [o(e.openAccess.trim())],
44
+ openAccess: e.openAccess,
45
+ rewind: e.rewind,
46
+ webhook: e.webhook,
47
+ delivery: e.delivery,
48
+ ...e.clientPolicy === void 0 ? {} : { clientPolicy: e.clientPolicy }
49
+ };
50
+ }, h = (e) => u.Struct({
51
+ _tag: u.Literal("event"),
52
+ origin: u.String,
53
+ n: u.Number,
54
+ emittedAt: u.Number,
55
+ payload: e
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({
68
+ key: e,
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([
72
+ r,
73
+ t,
74
+ e
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),
79
+ error: m,
80
+ stream: !0
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) => {
93
+ if (typeof e != "object" || !e) return JSON.stringify(e) ?? "null";
94
+ let t = Object.entries(e), n = 0;
95
+ for (let e of t) e[1] !== void 0 && (t[n++] = e);
96
+ return t.length = n, t.sort(([e], [t]) => e < t ? -1 : +(e > t)), JSON.stringify(t);
97
+ }, D = "\0", O = (e, t, n) => [
98
+ e ?? "~",
99
+ t,
100
+ E(n)
101
+ ].join("\0"), k = (e) => {
102
+ let [t = "~", n = "", r = ""] = e.split("\0");
103
+ return {
104
+ tenantId: t === "~" ? null : t,
105
+ event: n,
106
+ key: r
107
+ };
108
+ }, A = (e) => e.split("\0").join(" · ");
109
+ //#endregion
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 };
@@ -0,0 +1,121 @@
1
+ import { Schema as e } from "effect";
2
+ //#region src/idempotency.ts
3
+ var t = (e, t, n) => `${e ?? "(global)"}|${t.toUpperCase()}|${n}`, n = (e, t, n, r) => r === void 0 ? `${e ?? "(global)"}|${t ?? "(anon)"}|WS|${n}` : `WS-connection:${JSON.stringify([
4
+ e ?? null,
5
+ t ?? null,
6
+ n,
7
+ r
8
+ ])}`, r = class extends e.TaggedError()("MutationConnectionRequired", { traceId: e.String }) {
9
+ get message() {
10
+ return "this mutation requires a live native WebSocket connection";
11
+ }
12
+ }, i = async (e, t, n, r, i, o, s = a) => {
13
+ if (!/^[0-9a-f]{64}$/.test(r)) throw Error("Invalid idempotency input fingerprint");
14
+ let c = await e.claim(t, n, r, o, i, s);
15
+ if (c.kind === "claimed") {
16
+ let e = c.claim;
17
+ if (typeof e.id != "string" || e.id.length === 0 || e.scope !== t || e.key !== n || e.inputHash !== r) throw Error("Idempotency store returned an invalid claim identity");
18
+ return {
19
+ kind: "fresh",
20
+ claim: Object.freeze({
21
+ scope: t,
22
+ key: n,
23
+ id: e.id,
24
+ inputHash: r
25
+ })
26
+ };
27
+ }
28
+ if (c.kind === "existing") {
29
+ if (c.record.scope !== t || c.record.key !== n) throw Error("Idempotency store returned an invalid receipt identity");
30
+ return typeof c.record.inputHash == "string" ? c.record.inputHash === r ? c.record.status === "completed" && c.record.response ? {
31
+ kind: "replay",
32
+ response: c.record.response
33
+ } : { kind: "conflict" } : { kind: "input-conflict" } : { kind: "input-unbound" };
34
+ }
35
+ if (c.kind === "conflict") return { kind: "conflict" };
36
+ throw Error("Idempotency store returned an invalid acquisition result");
37
+ }, a = 3e4, o = (e, t, n = a, r = Date.now) => {
38
+ let i = Object.freeze({
39
+ scope: t.scope,
40
+ key: t.key,
41
+ id: t.id,
42
+ inputHash: t.inputHash
43
+ }), o = !1, s = setInterval(() => {
44
+ o || (o = !0, e.renew(i, r()).then((e) => {
45
+ e || clearInterval(s);
46
+ }, () => {}).finally(() => {
47
+ o = !1;
48
+ }));
49
+ }, Math.max(1, Math.floor(n / 3)));
50
+ return s.unref?.(), () => clearInterval(s);
51
+ }, s = (e, t, n, r) => e.complete(t, n, r), c = (e, t) => e.release(t), l = 0n, u = () => {
52
+ let e = /* @__PURE__ */ new Map(), t = (e, t) => JSON.stringify([e, t]), n = (n) => {
53
+ let r = e.get(t(n.scope, n.key));
54
+ return r?.status === "in_flight" && r.id === n.id && r.inputHash === n.inputHash ? r : void 0;
55
+ };
56
+ return {
57
+ get: async (n, r) => e.get(t(n, r)) ?? null,
58
+ claim: async (n, r, i, a, o, s) => {
59
+ let c = e.get(t(n, r));
60
+ if (c && c.inputHash !== i || c && a - c.createdAt < d(c.status, o, s)) return {
61
+ kind: "existing",
62
+ record: c
63
+ };
64
+ let u = `memory:${(++l).toString(36)}`;
65
+ return e.set(t(n, r), {
66
+ id: u,
67
+ scope: n,
68
+ key: r,
69
+ inputHash: i,
70
+ status: "in_flight",
71
+ response: null,
72
+ createdAt: a
73
+ }), {
74
+ kind: "claimed",
75
+ claim: Object.freeze({
76
+ id: u,
77
+ scope: n,
78
+ key: r,
79
+ inputHash: i
80
+ })
81
+ };
82
+ },
83
+ complete: async (r, i, a) => {
84
+ let o = n(r);
85
+ return o ? (e.set(t(r.scope, r.key), {
86
+ ...o,
87
+ status: "completed",
88
+ response: i,
89
+ createdAt: a
90
+ }), !0) : !1;
91
+ },
92
+ release: async (r) => n(r) ? e.delete(t(r.scope, r.key)) : !1,
93
+ renew: async (r, i) => {
94
+ let a = n(r);
95
+ return a ? (e.set(t(r.scope, r.key), {
96
+ ...a,
97
+ createdAt: i
98
+ }), !0) : !1;
99
+ }
100
+ };
101
+ }, d = (e, t, n) => e === "completed" ? t : Math.min(t, n), f = class extends e.TaggedError()("MutationInFlight", {
102
+ traceId: e.String,
103
+ retryAfterMs: e.Number
104
+ }) {
105
+ get message() {
106
+ return `a request with this idempotency key is still in flight — replay the same key after ${this.retryAfterMs}ms`;
107
+ }
108
+ }, p = 1e3, m = class extends e.TaggedError()("MutationIdempotencyRefused", {
109
+ traceId: e.String,
110
+ reason: e.Literals([
111
+ "input-conflict",
112
+ "input-unbound",
113
+ "input-unavailable"
114
+ ])
115
+ }) {
116
+ get message() {
117
+ return this.reason === "input-unavailable" ? "This request input could not be captured safely. This attempt was stopped before execution." : "This request cannot be safely matched to the original operation. Check its current state before starting another action.";
118
+ }
119
+ };
120
+ //#endregion
121
+ export { a as DEFAULT_IN_FLIGHT_LEASE_MS, p as MUTATION_IN_FLIGHT_RETRY_AFTER_MS, r as MutationConnectionRequired, m as MutationIdempotencyRefused, f as MutationInFlight, i as beginIdempotent, c as failIdempotent, s as finishIdempotent, d as freshFor, o as holdIdempotentClaim, t as idempotencyScope, u as memoryIdempotencyStore, n as wsMutationIdempotencyScope };
@@ -0,0 +1,123 @@
1
+ import { canonicalJson as e } from "./canonicalJson.js";
2
+ import { Schema as t } from "effect";
3
+ //#region src/idempotencyInput.ts
4
+ var n = (t) => {
5
+ let n = /* @__PURE__ */ new WeakMap(), r = 0, i = (e, t = () => !1) => {
6
+ if (Object.getOwnPropertySymbols(e).length) throw Error("Unsupported wire fields");
7
+ return Object.getOwnPropertyNames(e).sort().filter((e) => !t(e)).map((t) => {
8
+ let n = Object.getOwnPropertyDescriptor(e, t);
9
+ if (!("value" in n)) throw Error("Unsupported wire accessor");
10
+ return [t, o(n.value)];
11
+ });
12
+ }, a = (e, t = 0, n = e.byteLength) => {
13
+ let r = "";
14
+ for (let i of new Uint8Array(e, t, n)) r += i.toString(16).padStart(2, "0");
15
+ return r;
16
+ }, o = (e) => {
17
+ if (e === null) return ["null"];
18
+ if (e === void 0) return ["undefined"];
19
+ if (typeof e == "string" || typeof e == "boolean") return [typeof e, e];
20
+ if (typeof e == "number") return ["number", Object.is(e, -0) ? "-0" : String(e)];
21
+ if (typeof e == "bigint") return ["bigint", String(e)];
22
+ if (typeof e != "object") throw Error("Unsupported wire value");
23
+ let t = n.get(e);
24
+ if (t !== void 0) return ["reference", t];
25
+ let s = r++;
26
+ if (n.set(e, s), Array.isArray(e)) {
27
+ if (Object.getPrototypeOf(e) !== Array.prototype) throw Error("Unsupported wire array prototype");
28
+ return [
29
+ "array",
30
+ s,
31
+ e.length,
32
+ i(e, (e) => e === "length")
33
+ ];
34
+ }
35
+ if (Object.getPrototypeOf(e) === Date.prototype) return [
36
+ "date",
37
+ s,
38
+ String(Date.prototype.getTime.call(e)),
39
+ i(e)
40
+ ];
41
+ if (Object.getPrototypeOf(e) === Map.prototype) return [
42
+ "map",
43
+ s,
44
+ Array.from(Map.prototype.entries.call(e), ([e, t]) => [o(e), o(t)]),
45
+ i(e)
46
+ ];
47
+ if (Object.getPrototypeOf(e) === Set.prototype) return [
48
+ "set",
49
+ s,
50
+ Array.from(Set.prototype.values.call(e), o),
51
+ i(e)
52
+ ];
53
+ if (Object.getPrototypeOf(e) === ArrayBuffer.prototype) return [
54
+ "buffer",
55
+ s,
56
+ a(e),
57
+ i(e)
58
+ ];
59
+ if (ArrayBuffer.isView(e)) {
60
+ if (!(e.buffer instanceof ArrayBuffer)) throw Error("Shared wire memory is not a snapshot");
61
+ let t = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(e), "constructor")?.value;
62
+ if (typeof t != "function" || ![
63
+ "DataView",
64
+ "Int8Array",
65
+ "Uint8Array",
66
+ "Uint8ClampedArray",
67
+ "Int16Array",
68
+ "Uint16Array",
69
+ "Int32Array",
70
+ "Uint32Array",
71
+ "Float16Array",
72
+ "Float32Array",
73
+ "Float64Array",
74
+ "BigInt64Array",
75
+ "BigUint64Array"
76
+ ].includes(t.name) || globalThis[t.name] !== t) throw Error("Unsupported wire view prototype");
77
+ return [
78
+ "view",
79
+ s,
80
+ t.name,
81
+ o(e.buffer),
82
+ e.byteOffset,
83
+ e.byteLength,
84
+ i(e, (t) => !(e instanceof DataView) && /^(0|[1-9]\d*)$/.test(t))
85
+ ];
86
+ }
87
+ let c = Object.getPrototypeOf(e);
88
+ if (c !== null && c !== Object.prototype) throw Error("Unsupported wire object");
89
+ return [
90
+ "object",
91
+ s,
92
+ i(e)
93
+ ];
94
+ };
95
+ return e(o(t));
96
+ }, r = async (r, i, a, o = void 0) => {
97
+ try {
98
+ let s = t.encodeUnknownSync(r), c = t.decodeUnknownSync(r), l = s(i), u = n(l), d = c(structuredClone(l));
99
+ if (n(s(d)) !== u) throw Error("Input does not round trip");
100
+ let f = e({
101
+ protocol: "voltro-idempotency-input-v1",
102
+ principal: {
103
+ type: a.type,
104
+ id: a.id,
105
+ tenantId: a.tenantId,
106
+ kind: a.type === "user" ? a.kind ?? null : null,
107
+ service: a.type === "service" ? a.service : null,
108
+ auditActor: a.type === "serviceAccount" ? a.auditActorId === void 0 ? ["implicit"] : ["explicit", a.auditActorId] : null
109
+ },
110
+ wire: u,
111
+ transport: n(o)
112
+ }), p = await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(f)), m = "";
113
+ for (let e of new Uint8Array(p)) m += e.toString(16).padStart(2, "0");
114
+ return Object.freeze({
115
+ input: d,
116
+ fingerprint: m
117
+ });
118
+ } catch {
119
+ throw Error("Mutation input cannot be captured for idempotent execution");
120
+ }
121
+ };
122
+ //#endregion
123
+ export { r as prepareIdempotentInput };
package/dist/identity.js CHANGED
@@ -33,15 +33,11 @@ var a = e({ scope: "voltro:identity" }), o = Symbol.for("@voltro/runtime/process
33
33
  } catch {}
34
34
  return l.version;
35
35
  }, m = (e) => {
36
- if (l.frozen !== void 0 && l.frozen.bootPath !== e) {
37
- a.warn(`process identity was already resolved as ${l.frozen.replicaId} (bootPath=${l.frozen.bootPath}); keeping it rather than becoming ${e}. Call setProcessBootPath before anything reads the identity.`, {
38
- resolved: l.frozen.replicaId,
39
- was: l.frozen.bootPath,
40
- requested: e
41
- });
42
- return;
43
- }
44
- l.bootPath = e;
36
+ l.frozen !== void 0 && l.frozen.bootPath !== e ? a.warn(`process identity was already resolved as ${l.frozen.replicaId} (bootPath=${l.frozen.bootPath}); keeping it rather than becoming ${e}. Call setProcessBootPath before anything reads the identity.`, {
37
+ resolved: l.frozen.replicaId,
38
+ was: l.frozen.bootPath,
39
+ requested: e
40
+ }) : l.bootPath = e;
45
41
  }, h = (e) => {
46
42
  l.appVersion = e, l.frozen !== void 0 && (l.frozen = {
47
43
  ...l.frozen,