@voltro/protocol 0.85.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.
- package/CHANGELOG.md +666 -0
- package/README.md +14 -0
- package/THIRD-PARTY-NOTICES.md +5 -33
- package/dist/approval.js +64 -0
- package/dist/approvalDescriptors.js +26 -0
- package/dist/auth.js +369 -0
- package/dist/boundedRing.js +27 -0
- package/dist/businessRule.js +11 -0
- package/dist/connection.js +73 -0
- package/dist/devActivity.js +4 -0
- package/dist/errorValue.js +21 -0
- package/dist/event.js +104 -0
- package/dist/idempotency.js +66 -0
- package/dist/identity.js +5 -9
- package/dist/index.d.ts +225 -21
- package/dist/index.js +42 -1700
- package/dist/inspect.js +14 -240
- package/dist/inspectBackfill.js +52 -0
- package/dist/inspectCommandRejection.js +21 -0
- package/dist/inspectProjection.js +133 -0
- package/dist/inspectStorageProjection.js +86 -0
- package/dist/inspectValueIdentity.js +4 -0
- package/dist/inspectWorkflowExecution.js +34 -0
- package/dist/inspectWorkflowQueuedStart.js +31 -0
- package/dist/jsonSchema.js +54 -0
- package/dist/outbox.js +10 -0
- package/dist/pairing.js +75 -0
- package/dist/plugin.js +61 -0
- package/dist/pluginNaming.js +32 -0
- package/dist/procedure.js +262 -0
- package/dist/publicApi.js +344 -0
- package/dist/reactivityChannel.js +34 -0
- package/dist/rest.d.ts +33 -0
- package/dist/rest.js +213 -470
- package/dist/restFormat.js +163 -0
- package/dist/rowEquality.js +31 -0
- package/dist/rowPatch.js +182 -0
- package/dist/schemaShape.js +67 -0
- package/dist/scopes.js +240 -0
- package/dist/serverErrorBus.js +15 -0
- package/dist/serviceCall.js +14 -0
- package/dist/session.js +13 -15
- package/dist/{sessionHeader-DriTZhmv.js → sessionHeader.js} +1 -1
- package/dist/storageAccess.js +14 -0
- package/dist/storeErrors.js +66 -0
- package/dist/storeScreenshots.js +90 -0
- package/dist/strictInput.js +27 -0
- package/dist/subscriptionEvent.js +37 -0
- package/dist/tenantSelection.js +40 -0
- package/dist/time.js +7 -0
- package/dist/undo.js +38 -0
- package/dist/validation.js +39 -0
- package/dist/webSocketGateway.js +10 -0
- package/dist/workflow.js +297 -0
- package/dist/workflowAccess.js +61 -0
- package/dist/workflowRestart.js +45 -0
- package/dist/workflowRetry.js +44 -0
- package/dist/workflowStartOutcome.js +30 -0
- package/package.json +3 -3
- package/dist/restFormat-BlgTLxm1.js +0 -298
- package/dist/scopes-ClywBij8.js +0 -548
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//#region src/serverErrorBus.ts
|
|
2
|
+
var e = (e) => {
|
|
3
|
+
let t = e.error, n = t instanceof Error ? t.name : typeof t?._tag == "string" ? String(t._tag) : "Error", r = [e.name === void 0 ? e.source : `${e.source} '${e.name}'`, ...typeof e.fields?.runId == "string" ? [`run ${e.fields.runId}`] : []].join(", "), i = /* @__PURE__ */ Error(`[message withheld: ${r} is declared encrypted — read it where the run is shown]`);
|
|
4
|
+
i.name = n;
|
|
5
|
+
let a = t instanceof Error && typeof t.stack == "string" ? t.stack : "", o = a.search(/^\s+at /m);
|
|
6
|
+
return i.stack = `${n}: ${i.message}${o === -1 ? "" : `\n${a.slice(o)}`}`, i;
|
|
7
|
+
}, t = /* @__PURE__ */ new Set(), n = (e) => {
|
|
8
|
+
for (let n of t) try {
|
|
9
|
+
n(e);
|
|
10
|
+
} catch {}
|
|
11
|
+
}, r = (e) => (t.add(e), () => {
|
|
12
|
+
t.delete(e);
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
export { n as publishServerError, r as subscribeServerErrors, e as withheldServerError };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
//#region src/serviceCall.ts
|
|
3
|
+
var t = class extends e.TaggedError()("ApiUnavailable", {
|
|
4
|
+
api: e.String,
|
|
5
|
+
tag: e.String,
|
|
6
|
+
reason: e.Literals([
|
|
7
|
+
"timeout",
|
|
8
|
+
"transport",
|
|
9
|
+
"closed"
|
|
10
|
+
]),
|
|
11
|
+
message: e.String
|
|
12
|
+
}) {}, n = (e) => typeof e == "object" && !!e && e._tag === "ApiUnavailable";
|
|
13
|
+
//#endregion
|
|
14
|
+
export { t as ApiUnavailable, n as isApiUnavailable };
|
package/dist/session.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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";
|
|
3
4
|
import { Schema as a } from "effect";
|
|
4
5
|
import { createHmac as o, timingSafeEqual as s } from "node:crypto";
|
|
5
6
|
import { createLogger as c } from "@voltro/logger";
|
|
6
7
|
//#region src/session.ts
|
|
7
8
|
var l = c({ scope: "@voltro/protocol:session" }), u = 2, d = a.Struct({
|
|
8
9
|
v: a.Literal(2),
|
|
9
|
-
subject:
|
|
10
|
+
subject: e,
|
|
10
11
|
exp: a.Number,
|
|
11
12
|
iat: a.Number,
|
|
12
13
|
kid: a.optional(a.String)
|
|
@@ -39,14 +40,14 @@ var l = c({ scope: "@voltro/protocol:session" }), u = 2, d = a.Struct({
|
|
|
39
40
|
}, T = 604800, E = .7, D = (e, t) => C(o("sha256", t).update(e).digest()), O = (e) => typeof e == "string" ? {
|
|
40
41
|
kid: "k0",
|
|
41
42
|
secret: e
|
|
42
|
-
} : e, k = (
|
|
43
|
+
} : e, k = (e, r, i = {}) => {
|
|
43
44
|
let a = O(r);
|
|
44
45
|
if (!a.secret) throw Error("signSession: secret must be a non-empty string");
|
|
45
|
-
let o = e
|
|
46
|
-
if (o.length > 0) throw Error(`signSession: refusing to mint a session cookie for a ${
|
|
46
|
+
let o = n(e);
|
|
47
|
+
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.`);
|
|
47
48
|
let s = i.ttlSeconds ?? T, c = Math.floor((i.now ?? Date.now)() / 1e3), l = {
|
|
48
49
|
v: 2,
|
|
49
|
-
subject: t(
|
|
50
|
+
subject: t(e),
|
|
50
51
|
exp: c + s,
|
|
51
52
|
iat: c,
|
|
52
53
|
...typeof r == "string" ? {} : { kid: a.kid }
|
|
@@ -112,19 +113,16 @@ var l = c({ scope: "@voltro/protocol:session" }), u = 2, d = a.Struct({
|
|
|
112
113
|
e >= 0 && r.splice(e, 1);
|
|
113
114
|
}
|
|
114
115
|
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) =>
|
|
116
|
-
let t =
|
|
116
|
+
}, L = process.env.VOLTRO_SESSION_COOKIE ?? "voltro:session", R = (e, t = L) => i(e, t, F), z = (e) => {
|
|
117
|
+
let t = i(e, L, F);
|
|
117
118
|
if (!t) return;
|
|
118
119
|
let n = v();
|
|
119
|
-
if (!n)
|
|
120
|
-
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
return M(t, n)?.exp;
|
|
120
|
+
if (!n) V();
|
|
121
|
+
else return M(t, n)?.exp;
|
|
124
122
|
}, B = !1, V = () => {
|
|
125
123
|
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
124
|
}, H = () => {
|
|
127
125
|
B = !1;
|
|
128
126
|
};
|
|
129
127
|
//#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,
|
|
128
|
+
export { p as DEFAULT_PREVIOUS_SESSION_KID, f as DEFAULT_SESSION_KID, y as MIN_SESSION_SECRET_LENGTH, L as SESSION_COOKIE_NAME, r 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 };
|
|
@@ -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
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,10 @@
|
|
|
1
|
+
//#region src/webSocketGateway.ts
|
|
2
|
+
var e = 4001, t = /* @__PURE__ */ new Set(["/rpc", "/ws"]), n = (e) => {
|
|
3
|
+
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`);
|
|
4
|
+
if (!e.path.startsWith("/")) throw Error(`defineWebSocket: path must be absolute, got '${e.path}'`);
|
|
5
|
+
if (t.has(e.path)) throw Error(`defineWebSocket: '${e.path}' is the framework's own socket surface — mount the gateway elsewhere`);
|
|
6
|
+
if (e.auth !== "subject" && e.auth !== "public") throw Error(`defineWebSocket: auth must be 'subject' or 'public' (explicitly — an unauthenticated socket has to be a decision somebody wrote down), got '${String(e.auth)}'`);
|
|
7
|
+
return e;
|
|
8
|
+
};
|
|
9
|
+
//#endregion
|
|
10
|
+
export { e as GATEWAY_CREDENTIAL_EXPIRED_CLOSE_CODE, n as defineWebSocket };
|