@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.
Files changed (61) hide show
  1. package/CHANGELOG.md +666 -0
  2. package/README.md +14 -0
  3. package/THIRD-PARTY-NOTICES.md +5 -33
  4. package/dist/approval.js +64 -0
  5. package/dist/approvalDescriptors.js +26 -0
  6. package/dist/auth.js +369 -0
  7. package/dist/boundedRing.js +27 -0
  8. package/dist/businessRule.js +11 -0
  9. package/dist/connection.js +73 -0
  10. package/dist/devActivity.js +4 -0
  11. package/dist/errorValue.js +21 -0
  12. package/dist/event.js +104 -0
  13. package/dist/idempotency.js +66 -0
  14. package/dist/identity.js +5 -9
  15. package/dist/index.d.ts +225 -21
  16. package/dist/index.js +42 -1700
  17. package/dist/inspect.js +14 -240
  18. package/dist/inspectBackfill.js +52 -0
  19. package/dist/inspectCommandRejection.js +21 -0
  20. package/dist/inspectProjection.js +133 -0
  21. package/dist/inspectStorageProjection.js +86 -0
  22. package/dist/inspectValueIdentity.js +4 -0
  23. package/dist/inspectWorkflowExecution.js +34 -0
  24. package/dist/inspectWorkflowQueuedStart.js +31 -0
  25. package/dist/jsonSchema.js +54 -0
  26. package/dist/outbox.js +10 -0
  27. package/dist/pairing.js +75 -0
  28. package/dist/plugin.js +61 -0
  29. package/dist/pluginNaming.js +32 -0
  30. package/dist/procedure.js +262 -0
  31. package/dist/publicApi.js +344 -0
  32. package/dist/reactivityChannel.js +34 -0
  33. package/dist/rest.d.ts +33 -0
  34. package/dist/rest.js +213 -470
  35. package/dist/restFormat.js +163 -0
  36. package/dist/rowEquality.js +31 -0
  37. package/dist/rowPatch.js +182 -0
  38. package/dist/schemaShape.js +67 -0
  39. package/dist/scopes.js +240 -0
  40. package/dist/serverErrorBus.js +15 -0
  41. package/dist/serviceCall.js +14 -0
  42. package/dist/session.js +13 -15
  43. package/dist/{sessionHeader-DriTZhmv.js → sessionHeader.js} +1 -1
  44. package/dist/storageAccess.js +14 -0
  45. package/dist/storeErrors.js +66 -0
  46. package/dist/storeScreenshots.js +90 -0
  47. package/dist/strictInput.js +27 -0
  48. package/dist/subscriptionEvent.js +37 -0
  49. package/dist/tenantSelection.js +40 -0
  50. package/dist/time.js +7 -0
  51. package/dist/undo.js +38 -0
  52. package/dist/validation.js +39 -0
  53. package/dist/webSocketGateway.js +10 -0
  54. package/dist/workflow.js +297 -0
  55. package/dist/workflowAccess.js +61 -0
  56. package/dist/workflowRestart.js +45 -0
  57. package/dist/workflowRetry.js +44 -0
  58. package/dist/workflowStartOutcome.js +30 -0
  59. package/package.json +3 -3
  60. package/dist/restFormat-BlgTLxm1.js +0 -298
  61. package/dist/scopes-ClywBij8.js +0 -548
@@ -0,0 +1,344 @@
1
+ import { schemaShape as e, shapeDigest as t } from "./schemaShape.js";
2
+ import { encodeRestError as n, encodeRestSuccess as r, resolveRestFormat as i, restViewSchema as a, toSnakeCase as o } from "./restFormat.js";
3
+ import { RestRouteError as s, defineRestRoute as c, requireAuthenticated as l, requireScope as u, response as ee, sse as d, sseFrame as f } from "./rest.js";
4
+ import { Effect as p, Schema as m, SchemaGetter as h, SchemaIssue as g } from "effect";
5
+ import * as _ from "effect/SchemaAST";
6
+ //#region src/publicApi.ts
7
+ var v = (e, t, n) => t.method ?? (t.resource !== void 0 && n !== void 0 ? S("", t, n).method : void 0) ?? (e === "query" ? "GET" : "POST"), y = (e, t, n) => C(t.path ?? (t.resource !== void 0 && n !== void 0 ? S(e, t, n).path : `/${t.version ?? "v1"}/${e.replace(/\./g, "/")}`)), b = (e) => {
8
+ let t = e.split("/").pop() ?? e;
9
+ return t.endsWith("ies") ? `${t.slice(0, -3)}y` : t.endsWith("ses") || t.endsWith("xes") ? t.slice(0, -2) : t.endsWith("s") ? t.slice(0, -1) : t;
10
+ }, x = (e, t) => {
11
+ let n = t != null && (typeof t == "object" || typeof t == "function") && "ast" in t ? L(t) : void 0;
12
+ if (n === void 0) return;
13
+ let r = `${b(e)}Id`;
14
+ return n.has(r) ? r : n.has("id") ? "id" : void 0;
15
+ }, S = (e, t, n) => {
16
+ let r = (t.resource ?? "").replace(/^\/+|\/+$/g, ""), i = `/${t.version ?? "v1"}/${r}`, a = x(r, n.input), o = a === void 0 ? i : `${i}/:${a}`, s = e.split(".").pop() ?? e, c = n.target?.op;
17
+ return n.kind === "query" || n.kind === "stream" ? {
18
+ method: "GET",
19
+ path: o
20
+ } : n.kind === "mutation" && c === "insert" ? {
21
+ method: "POST",
22
+ path: i
23
+ } : n.kind === "mutation" && c === "update" ? {
24
+ method: a === void 0 ? "POST" : "PATCH",
25
+ path: o
26
+ } : n.kind === "mutation" && c === "delete" ? {
27
+ method: a === void 0 ? "POST" : "DELETE",
28
+ path: o
29
+ } : {
30
+ method: "POST",
31
+ path: `${o}/${s}`
32
+ };
33
+ }, C = (e) => e.replace(/\{([A-Za-z0-9_]+)\}/g, ":$1"), w = (e) => [...e.matchAll(/:([A-Za-z0-9_]+)/g)].map((e) => e[1]), T = (e) => {
34
+ if (_.isUnion(e)) {
35
+ let t = e.types.filter((e) => !_.isUndefined(e));
36
+ if (t.length === 1) return t[0];
37
+ if (t.length !== e.types.length) return new _.Union(t);
38
+ }
39
+ return e;
40
+ }, E = /^-?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i, D = (e, t) => {
41
+ let n = T(t);
42
+ if (n.encoding !== void 0) return D(e, _.toEncoded(n));
43
+ if (_.isUnion(n)) {
44
+ for (let t of n.types) {
45
+ let n = D(e, t);
46
+ if (n !== e) return n;
47
+ }
48
+ return e;
49
+ }
50
+ if (_.isArrays(n)) {
51
+ let t = Array.isArray(e) ? e : e === void 0 ? void 0 : [e];
52
+ if (t === void 0) return e;
53
+ let r = n.rest[0] ?? n.elements[0];
54
+ return r === void 0 ? t : t.map((e) => D(e, r));
55
+ }
56
+ if (typeof e != "string") return e;
57
+ if (_.isNumber(n)) return E.test(e.trim()) ? Number(e) : e;
58
+ if (_.isBoolean(n)) return e === "true" || e !== "false" && e;
59
+ if (_.isLiteral(n)) {
60
+ let t = n.literal;
61
+ return typeof t == "number" && E.test(e.trim()) && Number(e) === t || typeof t == "boolean" && String(t) === e ? t : e;
62
+ }
63
+ return e;
64
+ }, O = (e, t) => {
65
+ if (typeof e != "object" || !e || Array.isArray(e)) return e;
66
+ let n = T(_.toEncoded(t.ast));
67
+ if (!_.isObjects(n)) return e;
68
+ let r = { ...e };
69
+ for (let e of n.propertySignatures) {
70
+ let t = String(e.name);
71
+ t in r && (r[t] = D(r[t], e.type));
72
+ }
73
+ return r;
74
+ }, k = (e) => {
75
+ let t = T(_.toEncoded(e.ast));
76
+ if (!_.isObjects(t)) return (e) => e;
77
+ let n = t.propertySignatures, r = /* @__PURE__ */ new Map(), i = n.length >= 32 && n.every((e, t) => typeof e.name != "string" || e.name in Object.prototype || r.has(e.name) ? !1 : (r.set(e.name, {
78
+ type: e.type,
79
+ order: t
80
+ }), !0));
81
+ return (e) => {
82
+ if (typeof e != "object" || !e || Array.isArray(e)) return e;
83
+ let t = { ...e }, a = i ? Object.keys(t) : void 0;
84
+ if (a !== void 0 && a.length * 4 < n.length) {
85
+ let e = [];
86
+ for (let t of a) {
87
+ let n = r.get(t);
88
+ n !== void 0 && e.push({
89
+ key: t,
90
+ ...n
91
+ });
92
+ }
93
+ e.sort((e, t) => e.order - t.order);
94
+ for (let { key: n, type: r } of e) t[n] = D(t[n], r);
95
+ } else for (let e of n) {
96
+ let n = String(e.name);
97
+ n in t && (t[n] = D(t[n], e.type));
98
+ }
99
+ return t;
100
+ };
101
+ }, A = (e) => {
102
+ let t = /* @__PURE__ */ new Set();
103
+ if (e === void 0) return t;
104
+ let n = (e) => {
105
+ if (_.isUnion(e)) for (let t of e.types) n(t);
106
+ else if (e.encoding !== void 0 && n(_.toEncoded(e)), _.isObjects(e)) {
107
+ let n = e.propertySignatures.find((e) => e.name === "_tag");
108
+ n !== void 0 && _.isLiteral(n.type) && typeof n.type.literal == "string" && t.add(n.type.literal);
109
+ }
110
+ };
111
+ return n(e.ast), t;
112
+ }, j = (e, t, n) => {
113
+ if (n !== void 0) try {
114
+ let r = m.encodeUnknownSync(n)(e);
115
+ if (typeof r == "object" && r) return {
116
+ error: t,
117
+ ...r
118
+ };
119
+ } catch {}
120
+ let r = { error: t };
121
+ for (let [t, n] of Object.entries(e)) t !== "stack" && t !== "_tag" && (r[t] = n);
122
+ return r;
123
+ }, M = (e, t, n, r = []) => N(e, t, n, r, A), N = (e, t, n, r, i) => {
124
+ if (typeof e != "object" || !e) return e;
125
+ let a = e._tag;
126
+ if (typeof a != "string" || a === "ScopeError" || a === "NoSuchResource") return e;
127
+ if (i(t.error).has(a)) {
128
+ let r = n.errorStatus?.[a] ?? 422;
129
+ return new s(r, j(e, a, t.error));
130
+ }
131
+ let o = r.find((e) => i(e.schema).has(a));
132
+ if (o === void 0) return e;
133
+ let c = n.errorStatus?.[a] ?? o.rest?.status ?? 422, l = o.rest?.headers?.(e);
134
+ return new s(c, j(e, a, o.schema), l);
135
+ }, P = (e, t, r, i, a, o, c) => {
136
+ let l = N(e, t, r, i, c);
137
+ if (!(l instanceof s) || a.errors === "tagged" && a.encodeError === void 0) return l;
138
+ let { error: u, _tag: ee, ...d } = l.body, f = typeof d.message == "string" ? d.message : typeof d.reason == "string" ? d.reason : "", p = n(a, {
139
+ status: l.status,
140
+ tag: u,
141
+ message: f,
142
+ fields: d
143
+ }, {
144
+ ...o,
145
+ status: l.status
146
+ });
147
+ return new s(l.status, p.body, {
148
+ ...l.headers ?? {},
149
+ ...p.headers ?? {}
150
+ }, p.contentType);
151
+ }, F = (e, t) => {
152
+ if (e === void 0) return;
153
+ let n = _.isUnion(e.ast) ? e.ast.types : [e.ast];
154
+ for (let e of n) {
155
+ let n = m.make(e);
156
+ if (A(n).has(t)) return n;
157
+ }
158
+ }, I = (e) => e.split(".").map((e, t) => t === 0 ? e : e.charAt(0).toUpperCase() + e.slice(1)).join("").replace(/[^A-Za-z0-9_]/g, "_"), L = (e) => {
159
+ let t = T(_.toEncoded(e.ast));
160
+ return _.isObjects(t) && t.indexSignatures.length === 0 ? new Set(t.propertySignatures.map((e) => String(e.name))) : void 0;
161
+ }, R = (e, t) => e === t || JSON.stringify(e) === JSON.stringify(t), te = (e) => {
162
+ let t = T(_.toEncoded(e.ast)), n = _.isObjects(t) ? new Map(t.propertySignatures.map((e) => [String(e.name), e.type])) : /* @__PURE__ */ new Map();
163
+ return (e) => {
164
+ let t = {};
165
+ for (let [r, i] of Object.entries(e)) {
166
+ let e = n.get(r);
167
+ t[r] = e === void 0 ? i : D(i, e);
168
+ }
169
+ return t;
170
+ };
171
+ }, z = (e, t) => {
172
+ if (e !== void 0) {
173
+ try {
174
+ return m.encodeUnknownSync(t)(e);
175
+ } catch {}
176
+ try {
177
+ return m.decodeUnknownSync(t)(e), e;
178
+ } catch {}
179
+ return e;
180
+ }
181
+ }, B = (e, t, n) => ({
182
+ ...e.request === void 0 ? {} : { request: z(e.request, t) },
183
+ ...e.response === void 0 ? {} : { response: z(e.response, n) }
184
+ }), ne = () => (e) => {
185
+ if (e.subject.type !== "anonymous") return;
186
+ let t = e.subject.credentialRejected;
187
+ return t === void 0 ? void 0 : {
188
+ status: 401,
189
+ message: t
190
+ };
191
+ }, V = (e, t, n, s = {}) => {
192
+ let b = e.publicApi;
193
+ if (b === void 0) throw Error(`publicApiRoute: descriptor '${e.name}' has no publicApi annotation`);
194
+ let x = i(s.profile, b.format), S = s.pluginErrors ?? [], C = /* @__PURE__ */ new WeakMap(), T = (e) => {
195
+ if (e === void 0) return /* @__PURE__ */ new Set();
196
+ let t = C.get(e.ast);
197
+ if (t !== void 0) return t;
198
+ let n = A(e);
199
+ return C.set(e.ast, n), n;
200
+ }, E = /* @__PURE__ */ new WeakMap(), D = (e, t) => {
201
+ if (e === void 0) return;
202
+ let n = E.get(e.ast);
203
+ if (n === void 0) {
204
+ let t = /* @__PURE__ */ new Map();
205
+ for (let n of _.isUnion(e.ast) ? e.ast.types : [e.ast]) {
206
+ let e = m.make(n);
207
+ for (let n of T(e)) t.has(n) || t.set(n, e);
208
+ }
209
+ n = t, E.set(e.ast, n);
210
+ }
211
+ return n.get(t);
212
+ }, O = {
213
+ kind: e.kind,
214
+ input: e.input,
215
+ target: e.target
216
+ }, j = v(e.kind, b, O), M = (e) => x.naming === "snake_case" ? o(e) : e, N = y(e.name, b, O), F = N.replace(/:([A-Za-z0-9_]+)/g, (e, t) => `:${M(t)}`), z = w(F), V = a(e.input, x), H = k(V), U = te(V), W = L(V);
217
+ if (W !== void 0) {
218
+ for (let t of z) if (!W.has(t)) throw Error(`publicApi: descriptor '${e.name}' — path '${N}' binds ':${t}', and the input declares no field of that name` + (W.size > 0 ? ` (fields: ${[...W].join(", ")})` : " (the input declares no fields)"));
219
+ }
220
+ let G = a(e.output, x), re = m.Struct({ value: V }), K = j !== "GET" && W === void 0;
221
+ if (j !== "GET" && W !== void 0) {
222
+ let e = z.length >= 32 ? new Set(z) : void 0;
223
+ for (let t of W) if (!(e?.has(t) ?? z.includes(t))) {
224
+ K = !0;
225
+ break;
226
+ }
227
+ }
228
+ let q = {
229
+ query: m.Unknown,
230
+ params: m.Unknown,
231
+ form: m.Boolean
232
+ }, ie = (K ? m.Struct({
233
+ ...q,
234
+ body: m.Unknown
235
+ }) : m.Struct(q)).pipe(m.decodeTo(re, {
236
+ decode: h.transformEffect((e) => {
237
+ let t = "body" in e ? e.body : void 0, n = j === "GET" ? H(e.query) : e.form ? H(t ?? {}) : t ?? {}, r = typeof e.params == "object" && e.params !== null ? U(e.params) : {};
238
+ if (typeof n != "object" || !n || Array.isArray(n)) return p.succeed({ value: n });
239
+ let i = n, a = Object.keys(r), o = 0;
240
+ for (let e of a) e in i && !R(i[e], r[e]) && (a[o++] = e);
241
+ if (a.length = o, a.length > 0) {
242
+ let t = j === "GET" ? "query string" : "body";
243
+ return p.fail(new g.InvalidValue({ message: `${a.map((e) => `'${e}'`).join(", ")} ${a.length === 1 ? "is" : "are"} bound by the path and sent with another value in the ${t}` }, e));
244
+ }
245
+ return p.succeed({ value: {
246
+ ...i,
247
+ ...r
248
+ } });
249
+ }),
250
+ encode: h.transform((e) => e)
251
+ })), ae = [
252
+ ne(),
253
+ ...e.openAccess === void 0 ? [l()] : [],
254
+ ...(b.scopes ?? []).map((e) => u(e))
255
+ ], J = (() => {
256
+ let t = e.source;
257
+ return typeof t == "string" ? t : Array.isArray(t) ? t[0] : void 0;
258
+ })(), Y = (t, n = 200) => ({
259
+ tag: e.name,
260
+ method: t.method,
261
+ path: t.path,
262
+ query: t.query,
263
+ source: J,
264
+ status: n
265
+ }), X = b.stream === "sse" && e.kind === "query" && n !== void 0, oe = m.encodeUnknownEffect(G), se = X ? (e, t) => d((r) => n(e.value, t, (e) => {
266
+ r(f(String(e._tag ?? "message"), e));
267
+ })) : async (n, i) => {
268
+ let a;
269
+ try {
270
+ a = await t(n.value, i);
271
+ } catch (t) {
272
+ throw P(t, e, b, S, x, Y(i), T);
273
+ }
274
+ let o = await p.runPromise(oe(a)), s = r(x, o, Y(i));
275
+ return ee(s.body, {
276
+ ...s.headers === void 0 ? {} : { headers: s.headers },
277
+ ...s.contentType === void 0 ? {} : { contentType: s.contentType }
278
+ });
279
+ }, Z = e.error, Q = T(Z), ce = [...Q], $ = [];
280
+ for (let e of ce) {
281
+ let t = D(Z, e);
282
+ t !== void 0 && $.push({
283
+ status: b.errorStatus?.[e] ?? 422,
284
+ tag: e,
285
+ schema: t
286
+ });
287
+ }
288
+ for (let e of S) for (let t of T(e.schema)) {
289
+ if (Q.has(t)) continue;
290
+ let n = b.errorStatus?.[t] ?? e.rest?.status;
291
+ if (n === void 0) continue;
292
+ let r = D(e.schema, t) ?? e.schema;
293
+ $.push({
294
+ status: n,
295
+ tag: t,
296
+ schema: r
297
+ });
298
+ }
299
+ let le = b.security === !1 ? "none" : b.security ?? (e.openAccess === void 0 ? "required" : "optional"), ue = {
300
+ tag: e.name,
301
+ kind: e.kind,
302
+ version: b.version ?? "v1",
303
+ security: le,
304
+ idempotent: b.idempotent === !0,
305
+ input: V,
306
+ pathParams: z,
307
+ output: G,
308
+ source: J,
309
+ errorResponses: $,
310
+ format: x
311
+ };
312
+ return c({
313
+ method: j,
314
+ path: F,
315
+ input: ie,
316
+ output: m.Unknown,
317
+ handler: se,
318
+ ...X ? { streaming: !0 } : {},
319
+ guards: ae,
320
+ operationId: b.operationId ?? I(e.name),
321
+ tags: b.tags ?? [e.name.split(".")[0] ?? e.name],
322
+ projection: ue,
323
+ ...x.etag && j === "GET" && !X ? { etag: !0 } : {},
324
+ ...b.cache !== void 0 && j === "GET" && !X ? { cache: b.cache } : {},
325
+ ...b.example === void 0 ? {} : { example: B(b.example, V, G) },
326
+ ...b.summary === void 0 ? {} : { summary: b.summary },
327
+ ...b.description === void 0 ? {} : { description: b.description },
328
+ ...b.deprecated === void 0 ? {} : { deprecated: b.deprecated },
329
+ ...b.sunset === void 0 ? {} : { sunset: b.sunset }
330
+ });
331
+ }, H = (e, t = {}) => e.filter((e) => e.descriptor.publicApi !== void 0).map((e) => V(e.descriptor, e.invoke, e.subscribe, t)).sort((e, t) => e.path.localeCompare(t.path)), U = (n) => t(n.filter((e) => e.projection !== void 0).map((t) => {
332
+ let n = t.projection, r = {
333
+ tag: n.tag,
334
+ method: t.method,
335
+ path: t.path,
336
+ pathParams: n.pathParams,
337
+ version: n.version,
338
+ security: n.security,
339
+ errors: [...n.errorResponses].map((e) => `${e.status}:${e.tag}`).sort()
340
+ };
341
+ return `${JSON.stringify(r)} ${e(n.input.ast)} ${e(n.output.ast)}`;
342
+ }).sort().join("\n"));
343
+ //#endregion
344
+ export { O as coerceQueryObject, D as coerceQueryValue, H as collectPublicApiRoutes, A as declaredErrorTags, v as derivePublicMethod, y as derivePublicPath, S as deriveResource, F as errorMemberSchema, C as normalizePathParams, I as operationIdOf, w as pathParamNames, M as projectedFailure, V as publicApiRoute, U as publicSurfaceDigest, x as resourceIdField, B as wireExample };
@@ -0,0 +1,34 @@
1
+ //#region src/reactivityChannel.ts
2
+ var e = "channel:", t = Symbol.for("@voltro/protocol/reactivityChannels"), n = globalThis, r = n[t] ?? (n[t] = /* @__PURE__ */ new Map()), i = /^[a-z][a-z0-9-]*(\.[a-z0-9-]+)*$/, a = (t) => {
3
+ if (!i.test(t)) throw Error(`reactivityChannel('${t}'): invalid channel name.\n Use lowercase kebab segments separated by dots — \`presence\`, \`job-queue\`,
4
+ \`billing.usage\`. A \`:\` is refused because it is the namespace separator in
5
+ the routing key (\`${e}<name>\`), and an uppercase letter is\n refused because a key that differs only by case reads as one channel and
6
+ routes as two.`);
7
+ let n = r.get(t);
8
+ if (n !== void 0) return n;
9
+ let a = `${e}${t}`, o = Object.freeze({
10
+ kind: "reactivity-channel",
11
+ name: t,
12
+ key: a,
13
+ toString: () => a
14
+ });
15
+ return r.set(t, o), o;
16
+ }, o = (e) => typeof e == "object" && !!e && e.kind === "reactivity-channel", s = (t) => t.startsWith(e), c = () => new Set([...r.values()].map((e) => e.key)), l = (e) => o(e) ? e.key : e, u = (e) => e === void 0 ? [] : Array.isArray(e) ? e.map(l) : [l(e)], d = (e) => e === void 0 ? void 0 : Array.isArray(e) ? e.map(l) : l(e), f = (e) => {
17
+ let t = c(), n = [];
18
+ for (let r of e) for (let e of u(r.source)) s(e) && !t.has(e) && n.push({
19
+ procedure: r.name,
20
+ key: e
21
+ });
22
+ return n;
23
+ }, p = (e, t) => {
24
+ let n = e?.injectExternalChange;
25
+ return n !== void 0 && (n.call(e, {
26
+ table: t.key,
27
+ op: "update",
28
+ new: {},
29
+ old: {},
30
+ origin: "inline"
31
+ }), !0);
32
+ };
33
+ //#endregion
34
+ export { e as REACTIVITY_CHANNEL_PREFIX, c as declaredReactivityChannelKeys, o as isReactivityChannel, s as isReactivityChannelKey, d as normalizeSource, p as publishReactivity, a as reactivityChannel, u as sourceKeys, f as undeclaredChannelKeys };
package/dist/rest.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as AST from 'effect/SchemaAST';
2
2
  import { DataStore } from '@voltro/database';
3
+ import { Effect } from 'effect';
3
4
  import { Schema } from 'effect';
4
5
 
5
6
  declare interface ActionProcedureDescriptor<Name extends string, Input extends Schema.Top, Output extends Schema.Top, Error extends Schema.Top> {
@@ -589,6 +590,9 @@ declare interface PluginHttpRoute {
589
590
  * handler branches on: exempting `/saml` exempts `POST /saml/anything`.
590
591
  */
591
592
  readonly originGuard?: 'exempt';
593
+ readonly cors?: RestCors;
594
+ /** Bearer-only routes must discard cookie authority before authentication. */
595
+ readonly credentials?: 'bearer';
592
596
  }
593
597
 
594
598
  /**
@@ -1225,6 +1229,22 @@ export declare interface RestCache {
1225
1229
  readonly audience?: (output: unknown, input: unknown, subject: Subject) => 'public' | 'private';
1226
1230
  }
1227
1231
 
1232
+ /** Cross-origin browser access for one route. Origins are exact, including opaque `null`. */
1233
+ export declare interface RestCors {
1234
+ readonly origins: ReadonlyArray<string>;
1235
+ readonly headers?: ReadonlyArray<string>;
1236
+ readonly methods?: ReadonlyArray<RestMethod>;
1237
+ /** Preflight cache lifetime in seconds. */
1238
+ readonly maxAge?: number;
1239
+ readonly exposeHeaders?: ReadonlyArray<string>;
1240
+ }
1241
+
1242
+ /** Shared by the descriptor adapter and listener, including early transport refusals. */
1243
+ export declare const restCorsHeaders: (cors: RestCors | undefined, origin: string | undefined) => Record<string, string>;
1244
+
1245
+ /** Preflight never authenticates, consumes quota, reads a body or executes a handler. */
1246
+ export declare const restCorsPreflight: (cors: RestCors, method: string, headers: Readonly<Record<string, string>>) => PluginHttpRouteResult;
1247
+
1228
1248
  export declare type RestDates = 'iso' | 'epoch-ms';
1229
1249
 
1230
1250
  export declare type RestEnvelope = 'none' | 'data' | 'jsonapi';
@@ -1327,6 +1347,13 @@ export declare type RestNaming = 'camelCase' | 'snake_case';
1327
1347
 
1328
1348
  export declare type RestPagination = 'link-header' | 'body-cursor';
1329
1349
 
1350
+ /** Uses the mounted rate-limit plugin's store, algorithms and dynamic policy. */
1351
+ export declare interface RestRateLimit {
1352
+ readonly limit: number;
1353
+ readonly window: string;
1354
+ readonly by?: 'subject' | 'tenant' | 'apiKey' | 'ip' | 'global';
1355
+ }
1356
+
1330
1357
  /** A schema-encoded REST body with an application-selected HTTP status and
1331
1358
  * response headers. The marker keeps ordinary domain objects unambiguous.
1332
1359
  * `output` still validates/encodes `body`; use `Schema.Void` with a 204 when
@@ -1425,6 +1452,10 @@ export declare interface RestRouteDescriptor<I, O> {
1425
1452
  */
1426
1453
  readonly version?: `v${number}`;
1427
1454
  readonly guards?: ReadonlyArray<RestGuard>;
1455
+ readonly cors?: RestCors;
1456
+ /** Only an explicit bearer header may authenticate; cookies are removed before auth and context construction. */
1457
+ readonly credentials?: 'bearer';
1458
+ readonly rateLimit?: RestRateLimit;
1428
1459
  /**
1429
1460
  * This route STREAMS (Server-Sent Events) rather than resolving one value — its
1430
1461
  * handler returns `sse(...)`. Declared on the descriptor, not left implicit in
@@ -1510,6 +1541,8 @@ export declare const restRoutesToHttpRoutes: (routes: ReadonlyArray<RestRouteDes
1510
1541
  * auth subject + the bound DataStore.
1511
1542
  */
1512
1543
  export declare interface RestServeBindings {
1544
+ /** Bound once from the app's mounted rate-limit plugin. */
1545
+ readonly rateLimit?: (policy: RestRateLimit, ctx: RestRouteContext, route: RestRouteDescriptor<any, any>) => Effect.Effect<void, unknown>;
1513
1546
  /** Resolve the calling subject from the request headers. */
1514
1547
  readonly resolveSubject?: (headers: Readonly<Record<string, string>>) => Subject | Promise<Subject>;
1515
1548
  /** The bound framework DataStore. */