@voltro/protocol 0.84.0 → 0.86.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 (65) hide show
  1. package/CHANGELOG.md +1215 -0
  2. package/README.md +14 -0
  3. package/THIRD-PARTY-NOTICES.md +5 -89
  4. package/dist/apikey.d.ts +32 -34
  5. package/dist/approval.js +64 -0
  6. package/dist/approvalDescriptors.js +26 -0
  7. package/dist/auth.js +369 -0
  8. package/dist/boundedRing.js +27 -0
  9. package/dist/businessRule.js +11 -0
  10. package/dist/connection.js +73 -0
  11. package/dist/devActivity.js +4 -0
  12. package/dist/errorValue.js +21 -0
  13. package/dist/event.js +104 -0
  14. package/dist/idempotency.js +66 -0
  15. package/dist/identity.js +5 -9
  16. package/dist/index.d.ts +1494 -1366
  17. package/dist/index.js +42 -1411
  18. package/dist/inspect.d.ts +88 -88
  19. package/dist/inspect.js +14 -190
  20. package/dist/inspectBackfill.js +52 -0
  21. package/dist/inspectCommandRejection.js +21 -0
  22. package/dist/inspectProjection.js +133 -0
  23. package/dist/inspectStorageProjection.js +86 -0
  24. package/dist/inspectValueIdentity.js +4 -0
  25. package/dist/inspectWorkflowExecution.js +34 -0
  26. package/dist/inspectWorkflowQueuedStart.js +31 -0
  27. package/dist/jsonSchema.js +54 -0
  28. package/dist/jwt.d.ts +32 -34
  29. package/dist/outbox.js +10 -0
  30. package/dist/pairing.js +75 -0
  31. package/dist/plugin.js +61 -0
  32. package/dist/pluginNaming.js +32 -0
  33. package/dist/procedure.js +262 -0
  34. package/dist/publicApi.js +344 -0
  35. package/dist/reactivityChannel.js +34 -0
  36. package/dist/rest.d.ts +105 -57
  37. package/dist/rest.js +259 -437
  38. package/dist/restFormat.js +163 -0
  39. package/dist/rowEquality.js +31 -0
  40. package/dist/rowPatch.js +182 -0
  41. package/dist/schemaShape.js +67 -0
  42. package/dist/scopes.js +240 -0
  43. package/dist/serverErrorBus.js +15 -0
  44. package/dist/serviceCall.js +14 -0
  45. package/dist/session.d.ts +90 -96
  46. package/dist/session.js +13 -15
  47. package/dist/{sessionHeader-DriTZhmv.js → sessionHeader.js} +1 -1
  48. package/dist/storageAccess.js +14 -0
  49. package/dist/storeErrors.js +66 -0
  50. package/dist/storeScreenshots.js +90 -0
  51. package/dist/strictInput.js +27 -0
  52. package/dist/subscriptionEvent.js +37 -0
  53. package/dist/tenantSelection.js +40 -0
  54. package/dist/time.js +7 -0
  55. package/dist/undo.js +38 -0
  56. package/dist/validation.js +39 -0
  57. package/dist/webSocketGateway.js +10 -0
  58. package/dist/workflow.js +297 -0
  59. package/dist/workflowAccess.js +61 -0
  60. package/dist/workflowRestart.js +45 -0
  61. package/dist/workflowRetry.js +44 -0
  62. package/dist/workflowStartOutcome.js +30 -0
  63. package/package.json +4 -8
  64. package/dist/restFormat-EZ8k9ZcQ.js +0 -304
  65. package/dist/scopes-Di756ENh.js +0 -518
@@ -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,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,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,104 @@
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 { Schema as l } from "effect";
7
+ import { Rpc as u } from "effect/rpc";
8
+ //#region src/event.ts
9
+ var d = 7500, f = (e) => typeof e == "object" && !!e && e.kind === "event", p = (e) => {
10
+ if (e.name.length === 0) throw Error("defineEvent: `name` must not be empty");
11
+ 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
+ whitespace and delivers nothing — silently, and only on that broker, so an
13
+ app that works on Redis stops working when the transport changes.
14
+ Use a dot for namespacing: "orders.paid".`);
15
+ 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
16
+ for an unguarded event, or list the scopes that may subscribe.`);
17
+ if (e.openAccess !== void 0) {
18
+ 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
19
+ subscribe without an authorization check — e.g. \`openAccess: 'public scoreboard,
20
+ no caller data'\`.`);
21
+ 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
22
+ if a subscriber must hold a scope; drop them if anyone may listen.`);
23
+ }
24
+ 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.`);
25
+ 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,
26
+ which is the opposite of what a version bump is for.`);
27
+ 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
28
+ durable side effect at a third party, and one already sent cannot be superseded.
29
+ A high-rate event with an HTTP audience is also 60 deliveries per second per target;
30
+ the rate limit DEFERS the excess as pending rows, so it grows a table instead of failing.
31
+ Split them: publish the high-rate event for clients, and publish a separate, coarser
32
+ event (a summary, a state change) for the outside world.`);
33
+ return {
34
+ kind: "event",
35
+ name: e.name,
36
+ key: e.key,
37
+ payload: e.payload,
38
+ guards: e.openAccess === void 0 ? e.guards : [o(e.openAccess.trim())],
39
+ openAccess: e.openAccess,
40
+ rewind: e.rewind,
41
+ webhook: e.webhook,
42
+ delivery: e.delivery
43
+ };
44
+ }, m = (e) => l.Struct({
45
+ _tag: l.Literal("event"),
46
+ origin: l.String,
47
+ n: l.Number,
48
+ emittedAt: l.Number,
49
+ 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({
62
+ 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([
66
+ r,
67
+ t,
68
+ 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),
73
+ error: m,
74
+ 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) => {
87
+ if (typeof e != "object" || !e) return JSON.stringify(e) ?? "null";
88
+ let t = Object.entries(e), n = 0;
89
+ for (let e of t) e[1] !== void 0 && (t[n++] = e);
90
+ return t.length = n, t.sort(([e], [t]) => e < t ? -1 : +(e > t)), JSON.stringify(t);
91
+ }, T = "\0", E = (e, t, n) => [
92
+ e ?? "~",
93
+ t,
94
+ w(n)
95
+ ].join("\0"), D = (e) => {
96
+ let [t = "~", n = "", r = ""] = e.split("\0");
97
+ return {
98
+ tenantId: t === "~" ? null : t,
99
+ event: n,
100
+ key: r
101
+ };
102
+ }, O = (e) => e.split("\0").join(" · ");
103
+ //#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 };
@@ -0,0 +1,66 @@
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 = a) => {
13
+ let s = await e.claim(t, n, i, r, o);
14
+ return s === "claimed" ? { kind: "fresh" } : s.status === "completed" && s.response ? {
15
+ kind: "replay",
16
+ response: s.response
17
+ } : { kind: "conflict" };
18
+ }, a = 3e4, o = (e, t, n, r = a, i = Date.now) => {
19
+ let o = setInterval(() => {
20
+ e.renew(t, n, i()).catch(() => {});
21
+ }, Math.max(1, Math.floor(r / 3)));
22
+ return o.unref?.(), () => clearInterval(o);
23
+ }, s = (e, t, n, r, i) => e.complete(t, n, r, i), c = (e, t, n) => e.release(t, n), l = () => {
24
+ let e = /* @__PURE__ */ new Map(), t = (e, t) => `${e}\u0000${t}`;
25
+ return {
26
+ get: async (n, r) => e.get(t(n, r)) ?? null,
27
+ claim: async (n, r, i, a, o) => {
28
+ let s = e.get(t(n, r));
29
+ return s && i - s.createdAt < u(s.status, a, o) ? s : (e.set(t(n, r), {
30
+ scope: n,
31
+ key: r,
32
+ status: "in_flight",
33
+ response: null,
34
+ createdAt: i
35
+ }), "claimed");
36
+ },
37
+ complete: async (n, r, i, a) => {
38
+ e.set(t(n, r), {
39
+ scope: n,
40
+ key: r,
41
+ status: "completed",
42
+ response: i,
43
+ createdAt: a
44
+ });
45
+ },
46
+ release: async (n, r) => {
47
+ e.delete(t(n, r));
48
+ },
49
+ renew: async (n, r, i) => {
50
+ let a = e.get(t(n, r));
51
+ a?.status === "in_flight" && e.set(t(n, r), {
52
+ ...a,
53
+ createdAt: i
54
+ });
55
+ }
56
+ };
57
+ }, u = (e, t, n) => e === "completed" ? t : Math.min(t, n), d = class extends e.TaggedError()("MutationInFlight", {
58
+ traceId: e.String,
59
+ retryAfterMs: e.Number
60
+ }) {
61
+ get message() {
62
+ return `a request with this idempotency key is still in flight — replay the same key after ${this.retryAfterMs}ms`;
63
+ }
64
+ }, f = 1e3;
65
+ //#endregion
66
+ export { a as DEFAULT_IN_FLIGHT_LEASE_MS, f as MUTATION_IN_FLIGHT_RETRY_AFTER_MS, r as MutationConnectionRequired, d as MutationInFlight, i as beginIdempotent, c as failIdempotent, s as finishIdempotent, u as freshFor, o as holdIdempotentClaim, t as idempotencyScope, l as memoryIdempotencyStore, n as wsMutationIdempotencyScope };
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,