@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.
- package/CHANGELOG.md +5987 -0
- package/README.md +14 -0
- package/THIRD-PARTY-NOTICES.md +1122 -31
- package/dist/apiRoleEndpoints.js +108 -0
- package/dist/apiRoleObservation.js +68 -0
- package/dist/apikey.d.ts +10 -0
- package/dist/approval.js +64 -0
- package/dist/approvalDescriptors.js +26 -0
- package/dist/auth.js +389 -0
- package/dist/boundedRing.js +27 -0
- package/dist/businessRule.js +11 -0
- package/dist/canonicalJson.js +14 -0
- package/dist/connection.js +73 -0
- package/dist/devActivity.js +4 -0
- package/dist/errorSchema.js +24 -0
- package/dist/errorValue.js +21 -0
- package/dist/event.js +110 -0
- package/dist/formCapture.js +12 -0
- package/dist/httpStreamMaxDuration.js +9 -0
- package/dist/idempotency.js +121 -0
- package/dist/idempotencyInput.js +123 -0
- package/dist/identity.js +5 -9
- package/dist/index.d.ts +1095 -139
- package/dist/index.js +54 -1700
- package/dist/inspect.d.ts +1 -1
- 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/jwt.d.ts +10 -0
- package/dist/jwt.js +1 -1
- package/dist/outbox.js +10 -0
- package/dist/pairing.js +75 -0
- package/dist/plugin.js +87 -0
- package/dist/pluginAccountMerge.js +88 -0
- package/dist/pluginNaming.js +32 -0
- package/dist/pluginPrivacy.js +166 -0
- package/dist/procedure.js +280 -0
- package/dist/publicApi.js +325 -0
- package/dist/queryOptionalSources.js +14 -0
- package/dist/reactivityChannel.js +34 -0
- package/dist/rest.d.ts +260 -64
- package/dist/rest.js +345 -499
- package/dist/restFormat.js +163 -0
- package/dist/restReceipt.js +43 -0
- package/dist/revalidate.js +13 -0
- package/dist/rowEquality.js +31 -0
- package/dist/rowPatch.js +182 -0
- package/dist/schemaShape.js +67 -0
- package/dist/scopes.js +273 -0
- package/dist/secretWindows.js +71 -0
- package/dist/serverErrorBus.js +15 -0
- package/dist/serviceCall.js +14 -0
- package/dist/session.d.ts +89 -6
- package/dist/session.js +62 -63
- package/dist/{sessionHeader-DriTZhmv.js → sessionHeader.js} +1 -1
- package/dist/ssrfPolicy.js +30 -0
- 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 +16 -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 +5 -3
- package/dist/restFormat-BlgTLxm1.js +0 -298
- package/dist/scopes-ClywBij8.js +0 -548
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> {
|
|
@@ -76,7 +77,12 @@ declare interface AnyApprovalPolicy {
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
/** A guard is a scope check, a relationship check, or an identity check. */
|
|
79
|
-
declare type AnyGuardSpec<Input = unknown> = GuardSpec<Input> | PolicyGuardSpec<Input> | AuthenticatedGuardSpec;
|
|
80
|
+
declare type AnyGuardSpec<Input = unknown> = GuardSpec<Input> | PolicyGuardSpec<Input> | AuthenticatedGuardSpec | UpgradedGuardSpec;
|
|
81
|
+
|
|
82
|
+
/** Work performed by an API process; schema and descriptor loading is separate. */
|
|
83
|
+
declare const API_WORKLOADS: readonly ["rpc", "subscriptions", "events", "presence", "workflows", "schedules", "directors", "drainers", "reapers", "retention", "rest", "webhooks", "inspect", "cdc"];
|
|
84
|
+
|
|
85
|
+
declare type ApiWorkload = typeof API_WORKLOADS[number];
|
|
80
86
|
|
|
81
87
|
/**
|
|
82
88
|
* "The caller must have an identity" — and nothing narrower.
|
|
@@ -208,6 +214,13 @@ export declare const encodeRestError: (format: RestFormat, error: RestFormatErro
|
|
|
208
214
|
*/
|
|
209
215
|
export declare const encodeRestSuccess: (format: RestFormat, value: unknown, ctx: RestFormatContext) => RestFormatResponse;
|
|
210
216
|
|
|
217
|
+
declare interface EnqueueOptions {
|
|
218
|
+
/** Deduplicate while an earlier intent is still pending, not permanently. */
|
|
219
|
+
readonly idempotencyKey?: string;
|
|
220
|
+
readonly maxAttempts?: number;
|
|
221
|
+
readonly delayMs?: number;
|
|
222
|
+
}
|
|
223
|
+
|
|
211
224
|
/** The member of an error union that declares `tag`, as a schema of its own —
|
|
212
225
|
* for the spec's per-status responses. */
|
|
213
226
|
export declare const errorMemberSchema: (error: Schema.Top | undefined, tag: string) => Schema.Top | undefined;
|
|
@@ -238,40 +251,43 @@ declare interface GuardSpec<Input = unknown> {
|
|
|
238
251
|
/** How an array of scopes combines. `'all'` (default) = AND (hold every
|
|
239
252
|
* scope); `'any'` = OR (hold at least one). Ignored for a single scope. */
|
|
240
253
|
readonly mode?: 'all' | 'any';
|
|
241
|
-
/**
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
* carried for logging and for a future subject-scope resolver that narrows by
|
|
249
|
-
* resource. It does not, on its own, make the check per-resource.
|
|
250
|
-
*
|
|
251
|
-
* **If your authority is per-resource, you want {@link PolicyGuardSpec}, not
|
|
252
|
-
* this field** — `guards: [{ action, resourceType, resource }]`, backed by
|
|
253
|
-
* `defineResourcePolicy` + a tuple source you register. That is built, wired
|
|
254
|
-
* on both boot paths, fail-closed without a resolver, and documented under
|
|
255
|
-
* *Authentication → Authorization*. An app whose relationships already live
|
|
256
|
-
* in its own tables (a `teamMembers` row, say) registers its own tuple source
|
|
257
|
-
* rather than copying data across; see `policyGuardResolver.ts`.
|
|
258
|
-
*
|
|
259
|
-
* That paragraph is here because its absence cost a deployment their access
|
|
260
|
-
* gate. This comment used to describe the resolver as "a future ReBAC /
|
|
261
|
-
* `accessPolicy()` resolver" — written before the ReBAC path shipped and
|
|
262
|
-
* never updated. They read the type, quoted the sentence, concluded there was
|
|
263
|
-
* "nothing in between" declaring an untruth and turning the gate off, and set
|
|
264
|
-
* `security: { defaultDeny: false }` on an app with 565 undecided procedures.
|
|
265
|
-
* The capability they needed was two fields away. A doc comment that says
|
|
266
|
-
* "future" about something shipped is not a small inaccuracy: it is the only
|
|
267
|
-
* thing a careful reader has, and it argued them out of a feature.
|
|
268
|
-
*/
|
|
254
|
+
/** Pure browser-safe input → target resource id selector. With a registered
|
|
255
|
+
* setResourceScopeResolver, scopes can be checked on this resource. Without
|
|
256
|
+
* resourceOnly, an existing global scope or admin:full may satisfy the guard
|
|
257
|
+
* without consulting that resolver. For per-resource authority over relationships use
|
|
258
|
+
* PolicyGuardSpec: `defineResourcePolicy` plus a registered tuple source. That is built, wired
|
|
259
|
+
* on both boot paths and fail-closed without a resolver — an earlier comment called it "future"
|
|
260
|
+
* and argued a deployment out of its access gate. */
|
|
269
261
|
readonly resource?: (input: Input) => string | undefined;
|
|
262
|
+
/** Require an explicit current resource verdict, including for globally held
|
|
263
|
+
* scopes or admin:full. Missing resolver/resource refuses access. */
|
|
264
|
+
readonly resourceOnly?: boolean;
|
|
270
265
|
}
|
|
271
266
|
|
|
267
|
+
/** Identity of one acquired execution, never reused when a key is reclaimed.
|
|
268
|
+
* This is receipt ownership, not authority to perform a business side effect. */
|
|
269
|
+
declare interface IdempotencyClaim {
|
|
270
|
+
readonly scope: string;
|
|
271
|
+
readonly key: string;
|
|
272
|
+
readonly id: string;
|
|
273
|
+
readonly inputHash: string;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
declare type IdempotencyClaimResult = {
|
|
277
|
+
readonly kind: 'claimed';
|
|
278
|
+
readonly claim: IdempotencyClaim;
|
|
279
|
+
} | {
|
|
280
|
+
readonly kind: 'existing';
|
|
281
|
+
readonly record: IdempotencyRecord;
|
|
282
|
+
} | {
|
|
283
|
+
readonly kind: 'conflict';
|
|
284
|
+
};
|
|
285
|
+
|
|
272
286
|
declare interface IdempotencyRecord {
|
|
273
287
|
readonly scope: string;
|
|
274
288
|
readonly key: string;
|
|
289
|
+
/** Null means a retained receipt predates declared-input binding. */
|
|
290
|
+
readonly inputHash: string | null;
|
|
275
291
|
readonly status: 'in_flight' | 'completed';
|
|
276
292
|
readonly response: IdempotencyResponse | null;
|
|
277
293
|
/** epoch ms */
|
|
@@ -281,34 +297,24 @@ declare interface IdempotencyRecord {
|
|
|
281
297
|
declare interface IdempotencyResponse {
|
|
282
298
|
readonly status: number;
|
|
283
299
|
readonly body: unknown;
|
|
300
|
+
/** Framework-owned REST qualification metadata. Custom stores preserve it
|
|
301
|
+
* unchanged, separately from the arbitrary application output in body. */
|
|
302
|
+
readonly rest?: unknown;
|
|
284
303
|
}
|
|
285
304
|
|
|
286
305
|
declare interface IdempotencyStore {
|
|
287
|
-
/** Current record for (scope, key), or null. Read-side (inspect/dashboard). */
|
|
288
306
|
readonly get: (scope: string, key: string) => Promise<IdempotencyRecord | null>;
|
|
289
|
-
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
*
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
readonly
|
|
300
|
-
/**
|
|
301
|
-
* Extend a RUNNING claim's lease: bump its timestamp while it is still
|
|
302
|
-
* `in_flight` (a completed or released claim is left alone). The server calls
|
|
303
|
-
* it while the handler runs; a process that dies stops calling it, and its
|
|
304
|
-
* claim goes stale after `inFlightLeaseMs` instead of after the whole replay
|
|
305
|
-
* window.
|
|
306
|
-
*/
|
|
307
|
-
readonly renew: (scope: string, key: string, now: number) => Promise<void>;
|
|
308
|
-
/** Flip the claim to `completed` and store the response. */
|
|
309
|
-
readonly complete: (scope: string, key: string, response: IdempotencyResponse, now: number) => Promise<void>;
|
|
310
|
-
/** Drop the claim (handler errored → a retry may re-process). */
|
|
311
|
-
readonly release: (scope: string, key: string) => Promise<void>;
|
|
307
|
+
/** Atomically acquire an absent/stale key with a new, never-reused identity.
|
|
308
|
+
* Otherwise return the existing receipt, or conflict if contention prevents
|
|
309
|
+
* observing a stable record. Expiry does not prove an old executor stopped. */
|
|
310
|
+
readonly claim: (scope: string, key: string, inputHash: string, now: number, ttlMs: number, inFlightLeaseMs: number) => Promise<IdempotencyClaimResult>;
|
|
311
|
+
/** Atomically match original scope/key/id AND in_flight status. False means
|
|
312
|
+
* this execution no longer owns a running receipt; never touch its successor. */
|
|
313
|
+
readonly renew: (claim: IdempotencyClaim, now: number) => Promise<boolean>;
|
|
314
|
+
/** Complete only the original running claim and start its replay window now. */
|
|
315
|
+
readonly complete: (claim: IdempotencyClaim, response: IdempotencyResponse, now: number) => Promise<boolean>;
|
|
316
|
+
/** Release only the original running claim. Completed receipts are retained. */
|
|
317
|
+
readonly release: (claim: IdempotencyClaim) => Promise<boolean>;
|
|
312
318
|
}
|
|
313
319
|
|
|
314
320
|
declare interface InsertTarget<Input = unknown, Row = unknown, Item = Record<string, unknown>> extends NestedTargetFields<Input> {
|
|
@@ -371,6 +377,12 @@ declare interface MutationProcedureDescriptor<Name extends string, Input extends
|
|
|
371
377
|
readonly idempotencyScope?: MutationIdempotencyScope | undefined;
|
|
372
378
|
/** Same-key calls run in invocation order on the client — see `defineMutation`. */
|
|
373
379
|
readonly serialize?: MutationSerialization<Schema.Schema.Type<Input>> | undefined;
|
|
380
|
+
/** Exclude this invocation from automatic row-image undo capture, with an
|
|
381
|
+
* explicit domain reason. Does not disable transactions, guards or audit. */
|
|
382
|
+
readonly undo?: {
|
|
383
|
+
readonly capture: false;
|
|
384
|
+
readonly reason: string;
|
|
385
|
+
} | undefined;
|
|
374
386
|
/** Declarative target(s) — drives auto-optimistic on the client AND
|
|
375
387
|
* surfaces which tables this mutation touches (debug, future
|
|
376
388
|
* query-invalidation analytics). Mutations without a target run
|
|
@@ -478,6 +490,12 @@ declare interface OpenAccessSpec {
|
|
|
478
490
|
/** The rpc tag as an `operationId`: `teams.list` → `teamsList`. */
|
|
479
491
|
export declare const operationIdOf: (tag: string) => string;
|
|
480
492
|
|
|
493
|
+
/** Enqueue-only capability bound to the caller's actual transaction store. */
|
|
494
|
+
declare interface OutboxEnqueueFacade {
|
|
495
|
+
/** Returns the stable delivery identity. Delivery happens after commit. */
|
|
496
|
+
enqueue(effect: string, payload: Record<string, unknown>, options?: EnqueueOptions): Promise<string>;
|
|
497
|
+
}
|
|
498
|
+
|
|
481
499
|
/** The parameter names a route pattern binds, in order. */
|
|
482
500
|
export declare const pathParamNames: (path: string) => ReadonlyArray<string>;
|
|
483
501
|
|
|
@@ -521,6 +539,12 @@ declare interface PluginErrorSchema {
|
|
|
521
539
|
|
|
522
540
|
/** A public raw-HTTP route a plugin serves on the framework listener. */
|
|
523
541
|
declare interface PluginHttpRoute {
|
|
542
|
+
/** Executable API workload owning this route. Omitted means `rpc`.
|
|
543
|
+
* Discovery and descriptor generation remain independent of role selection. */
|
|
544
|
+
readonly workload?: ApiWorkload;
|
|
545
|
+
/** The already-mounted REST projection, retained for SDK/schema inspection.
|
|
546
|
+
* Do not remount it through generic REST auth or idempotency bindings. */
|
|
547
|
+
readonly restContract?: RestRouteDescriptor<any, any>;
|
|
524
548
|
/** HTTP method, or `'*'` for any (the handler decides). The server mounts the
|
|
525
549
|
* path for exactly the methods its routes declare; `'*'` mounts it for every
|
|
526
550
|
* method the router knows (34), so keep that for a route that really serves
|
|
@@ -549,6 +573,13 @@ declare interface PluginHttpRoute {
|
|
|
549
573
|
*/
|
|
550
574
|
readonly match?: 'prefix' | 'exact';
|
|
551
575
|
readonly handle: (req: PluginHttpRouteRequest) => Promise<PluginHttpRouteResult>;
|
|
576
|
+
/** Lazy, backpressured input instead of a buffered rawBody on real HTTP.
|
|
577
|
+
* Requires match: 'exact' and exclusive ownership of the router-equivalent
|
|
578
|
+
* path (including other methods). The required handle remains the buffered
|
|
579
|
+
* in-process entry. Both entries must apply the same authorization/codec.
|
|
580
|
+
* maxBodyBytes counts consumed bytes; overflow overrides the handler's
|
|
581
|
+
* response with 413 even when the handler catches the read error. */
|
|
582
|
+
readonly handleStream?: (req: PluginHttpRouteStreamRequest) => Promise<PluginHttpRouteResult>;
|
|
552
583
|
/** Per-route body cap override (bytes) — wins over the listener's shared
|
|
553
584
|
* `maxBodyBytes`. NOTE: routes SHARING a path share one body read, so the
|
|
554
585
|
* widest override on the path's group applies to the whole group. */
|
|
@@ -589,6 +620,9 @@ declare interface PluginHttpRoute {
|
|
|
589
620
|
* handler branches on: exempting `/saml` exempts `POST /saml/anything`.
|
|
590
621
|
*/
|
|
591
622
|
readonly originGuard?: 'exempt';
|
|
623
|
+
readonly cors?: RestCors;
|
|
624
|
+
/** Bearer-only routes must discard cookie authority before authentication. */
|
|
625
|
+
readonly credentials?: 'bearer';
|
|
592
626
|
}
|
|
593
627
|
|
|
594
628
|
/**
|
|
@@ -748,11 +782,23 @@ declare interface PluginHttpRouteResult {
|
|
|
748
782
|
*/
|
|
749
783
|
declare interface PluginHttpRouteStream {
|
|
750
784
|
readonly subscribe: (emit: (chunk: string) => void) => () => void;
|
|
785
|
+
/** Per-connection lifetime in milliseconds (1..2147483647). Ending the
|
|
786
|
+
* response releases the subscription and keep-alive timer. Absent: no cap. */
|
|
787
|
+
readonly maxDurationMs?: number;
|
|
751
788
|
/** Keep-alive comment interval in ms (default 15000; `0` disables). Without
|
|
752
789
|
* it an idle SSE connection is dropped by proxies after ~30–60s. */
|
|
753
790
|
readonly keepAliveMs?: number;
|
|
754
791
|
}
|
|
755
792
|
|
|
793
|
+
/** A lazy binary request on the framework listener. No body bytes are read
|
|
794
|
+
* before the handler runs. Consume once, through EOF, before accepting an
|
|
795
|
+
* upload; returning early drains the unread body without validating it.
|
|
796
|
+
* The signal ends on disconnect, interruption or handler completion. */
|
|
797
|
+
declare interface PluginHttpRouteStreamRequest extends Omit<PluginHttpRouteRequest, 'rawBody'> {
|
|
798
|
+
readonly body: AsyncIterable<Uint8Array>;
|
|
799
|
+
readonly signal: AbortSignal;
|
|
800
|
+
}
|
|
801
|
+
|
|
756
802
|
/**
|
|
757
803
|
* A relationship (ReBAC) guard — "may this subject perform ACTION on THIS row?"
|
|
758
804
|
*
|
|
@@ -945,6 +991,10 @@ declare interface PublicApiSpec {
|
|
|
945
991
|
/** Streaming queries over request/response: 'snapshot' (default — first
|
|
946
992
|
* snapshot, like POST /rpc) | 'sse' (Server-Sent Events of snapshot+deltas). */
|
|
947
993
|
readonly stream?: 'snapshot' | 'sse';
|
|
994
|
+
/** Per-connection SSE lifetime in milliseconds (1..2147483647). Requires
|
|
995
|
+
* an SSE query projection. Clients may reconnect for a freshly authorized
|
|
996
|
+
* snapshot; expiry does not cancel a domain operation. */
|
|
997
|
+
readonly streamMaxDurationMs?: number;
|
|
948
998
|
/**
|
|
949
999
|
* HTTP status per DECLARED typed error tag, for the projection. A typed
|
|
950
1000
|
* error the handler fails with — one of the descriptor's `error:` union —
|
|
@@ -956,6 +1006,11 @@ declare interface PublicApiSpec {
|
|
|
956
1006
|
* publicApi: { errorStatus: { AccessDeniedError: 403, NotFoundError: 404 } }
|
|
957
1007
|
*/
|
|
958
1008
|
readonly errorStatus?: Readonly<Record<string, number>>;
|
|
1009
|
+
/** Additional HTTP failure statuses for the same declared error schema.
|
|
1010
|
+
* Documents dedicated handlers that choose a status from the error's fields;
|
|
1011
|
+
* does not change `errorStatus` or the generic projection's runtime mapping.
|
|
1012
|
+
* Values must be integer HTTP error statuses (400–599); duplicates are omitted. */
|
|
1013
|
+
readonly additionalErrorStatuses?: Readonly<Record<string, ReadonlyArray<number>>>;
|
|
959
1014
|
/**
|
|
960
1015
|
* The spec's tag group(s) for this operation. Default: the rpc tag's prefix
|
|
961
1016
|
* (`teams.list` → `teams`) — the first path segment, what a consumer looks
|
|
@@ -1059,6 +1114,16 @@ declare interface QueryCacheConfig {
|
|
|
1059
1114
|
* cardinality of a cache the author sized per org.
|
|
1060
1115
|
*/
|
|
1061
1116
|
readonly scope: 'subject' | 'tenant' | 'global';
|
|
1117
|
+
/**
|
|
1118
|
+
* The handler's result also depends on something no table write
|
|
1119
|
+
* invalidates — the clock, randomness, `ctx.connections`, an outgoing HTTP
|
|
1120
|
+
* call, `ctx.kv`. Applies to a COMPUTED query (a handler that returns a
|
|
1121
|
+
* value): without it such a result is computed but NOT kept, and the dev log
|
|
1122
|
+
* names what was used. With it, `ttl` is the bound on how stale that part may
|
|
1123
|
+
* be. `Date.now()`, `Math.random()` and a `fetch` outside the framework's
|
|
1124
|
+
* HTTP client are not observable — a handler that uses them must say so here.
|
|
1125
|
+
*/
|
|
1126
|
+
readonly volatile?: boolean | undefined;
|
|
1062
1127
|
}
|
|
1063
1128
|
|
|
1064
1129
|
declare interface QueryProcedureDescriptor<Name extends string, Input extends Schema.Top, Output extends Schema.Top, Error extends Schema.Top> {
|
|
@@ -1073,6 +1138,9 @@ declare interface QueryProcedureDescriptor<Name extends string, Input extends Sc
|
|
|
1073
1138
|
* listed table changes (pass an array to depend on several). Optional:
|
|
1074
1139
|
* queries without a source never receive auto-patches (joins, aggregates). */
|
|
1075
1140
|
readonly source: string | ReadonlyArray<string> | undefined;
|
|
1141
|
+
/** Optional integration observers, all also present in source. They keep
|
|
1142
|
+
* invalidation active when installed; absence is a visible graph warning. */
|
|
1143
|
+
readonly optionalSource?: ReadonlyArray<string>;
|
|
1076
1144
|
/** Server-side snapshot cache config. Absent → never cached (the
|
|
1077
1145
|
* default; the dispatcher already keeps live subscriptions fresh). */
|
|
1078
1146
|
readonly cache: QueryCacheConfig | undefined;
|
|
@@ -1134,13 +1202,9 @@ export declare const requireAnyScope: (scopes: ReadonlyArray<string>) => RestGua
|
|
|
1134
1202
|
* guards remain authorization failures (403). */
|
|
1135
1203
|
export declare const requireAuthenticated: () => RestGuard;
|
|
1136
1204
|
|
|
1137
|
-
/**
|
|
1138
|
-
*
|
|
1139
|
-
*
|
|
1140
|
-
* (`ADMIN_SCOPE` = `'admin:full'`) satisfies any required scope — the SAME
|
|
1141
|
-
* bypass the rpc-handler `requireScope`/`hasScope` (`./scopes`) honours, so
|
|
1142
|
-
* a subject granted admin is admin on both surfaces.
|
|
1143
|
-
*/
|
|
1205
|
+
/** Require an effective scope, including derived roles. Callers without it
|
|
1206
|
+
* receive 403; add requireAuthenticated when authenticated identity is required.
|
|
1207
|
+
* The admin:full bypass is the same one used by native scope guards. */
|
|
1144
1208
|
export declare const requireScope: (scope: string) => RestGuard;
|
|
1145
1209
|
|
|
1146
1210
|
/**
|
|
@@ -1225,6 +1289,22 @@ export declare interface RestCache {
|
|
|
1225
1289
|
readonly audience?: (output: unknown, input: unknown, subject: Subject) => 'public' | 'private';
|
|
1226
1290
|
}
|
|
1227
1291
|
|
|
1292
|
+
/** Cross-origin browser access for one route. Origins are exact, including opaque `null`. */
|
|
1293
|
+
export declare interface RestCors {
|
|
1294
|
+
readonly origins: ReadonlyArray<string>;
|
|
1295
|
+
readonly headers?: ReadonlyArray<string>;
|
|
1296
|
+
readonly methods?: ReadonlyArray<RestMethod>;
|
|
1297
|
+
/** Preflight cache lifetime in seconds. */
|
|
1298
|
+
readonly maxAge?: number;
|
|
1299
|
+
readonly exposeHeaders?: ReadonlyArray<string>;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
/** Shared by the descriptor adapter and listener, including early transport refusals. */
|
|
1303
|
+
export declare const restCorsHeaders: (cors: RestCors | undefined, origin: string | undefined) => Record<string, string>;
|
|
1304
|
+
|
|
1305
|
+
/** Preflight never authenticates, consumes quota, reads a body or executes a handler. */
|
|
1306
|
+
export declare const restCorsPreflight: (cors: RestCors, method: string, headers: Readonly<Record<string, string>>) => PluginHttpRouteResult;
|
|
1307
|
+
|
|
1228
1308
|
export declare type RestDates = 'iso' | 'epoch-ms';
|
|
1229
1309
|
|
|
1230
1310
|
export declare type RestEnvelope = 'none' | 'data' | 'jsonapi';
|
|
@@ -1304,7 +1384,7 @@ export declare interface RestFormatResponse {
|
|
|
1304
1384
|
readonly contentType?: string;
|
|
1305
1385
|
}
|
|
1306
1386
|
|
|
1307
|
-
export declare type RestGuard = (ctx: RestRouteContext) => RestGuardRejection | undefined | Promise<RestGuardRejection | undefined>;
|
|
1387
|
+
export declare type RestGuard = (ctx: RestRouteContext, request?: RestGuardRequest) => RestGuardRejection | undefined | Promise<RestGuardRejection | undefined>;
|
|
1308
1388
|
|
|
1309
1389
|
/**
|
|
1310
1390
|
* A guard runs after input decode + ctx resolution, before the handler.
|
|
@@ -1316,6 +1396,13 @@ export declare interface RestGuardRejection {
|
|
|
1316
1396
|
readonly message: string;
|
|
1317
1397
|
}
|
|
1318
1398
|
|
|
1399
|
+
/** Guard input is the decoded execution snapshot. Guards are read-only
|
|
1400
|
+
* admission checks and may run again after awaited admission/storage work. */
|
|
1401
|
+
export declare interface RestGuardRequest {
|
|
1402
|
+
readonly input: unknown;
|
|
1403
|
+
readonly defaultDeny: boolean;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1319
1406
|
/** What a REST handler may return: the decoded output, a `response(...)` with
|
|
1320
1407
|
* its own status and headers, an `sse(...)` stream, or `bytes(...)` for a
|
|
1321
1408
|
* non-JSON body (a file, an export, an XML document). */
|
|
@@ -1327,6 +1414,13 @@ export declare type RestNaming = 'camelCase' | 'snake_case';
|
|
|
1327
1414
|
|
|
1328
1415
|
export declare type RestPagination = 'link-header' | 'body-cursor';
|
|
1329
1416
|
|
|
1417
|
+
/** Uses the mounted rate-limit plugin's store, algorithms and dynamic policy. */
|
|
1418
|
+
export declare interface RestRateLimit {
|
|
1419
|
+
readonly limit: number;
|
|
1420
|
+
readonly window: string;
|
|
1421
|
+
readonly by?: 'subject' | 'tenant' | 'apiKey' | 'ip' | 'global';
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1330
1424
|
/** A schema-encoded REST body with an application-selected HTTP status and
|
|
1331
1425
|
* response headers. The marker keeps ordinary domain objects unambiguous.
|
|
1332
1426
|
* `output` still validates/encodes `body`; use `Schema.Void` with a 204 when
|
|
@@ -1348,6 +1442,8 @@ export declare interface RestResponse<O> {
|
|
|
1348
1442
|
* stays free of the database dep).
|
|
1349
1443
|
*/
|
|
1350
1444
|
export declare interface RestRouteContext {
|
|
1445
|
+
/** Server-owned invocation trace, stable through readmission and projection. */
|
|
1446
|
+
readonly traceId: string;
|
|
1351
1447
|
readonly subject: Subject;
|
|
1352
1448
|
readonly headers: Readonly<Record<string, string>>;
|
|
1353
1449
|
/** Request metadata and the byte-exact body as received by the HTTP
|
|
@@ -1372,6 +1468,9 @@ export declare interface RestRouteContext {
|
|
|
1372
1468
|
}
|
|
1373
1469
|
|
|
1374
1470
|
export declare interface RestRouteDescriptor<I, O> {
|
|
1471
|
+
/** Original schemas for client generation. Metadata only: never mounts a
|
|
1472
|
+
* second handler or selects authentication/idempotency bindings. */
|
|
1473
|
+
readonly publicApiDescriptor?: PublicApiDescriptor;
|
|
1375
1474
|
readonly method: RestMethod;
|
|
1376
1475
|
/** Absolute path, e.g. `/v1/customers`. Matches that path exactly. */
|
|
1377
1476
|
readonly path: string;
|
|
@@ -1385,6 +1484,12 @@ export declare interface RestRouteDescriptor<I, O> {
|
|
|
1385
1484
|
* encodes it to JSON for the `200` response. */
|
|
1386
1485
|
readonly output: Schema.Codec<O, any, never, never>;
|
|
1387
1486
|
readonly handler: (input: I, ctx: RestRouteContext) => Promise<RestHandlerResult<O>> | RestHandlerResult<O>;
|
|
1487
|
+
/** Pure synchronous normalization of thrown admission/handler failures.
|
|
1488
|
+
* Return a mapped thrown value (for example RestRouteError), not an HTTP
|
|
1489
|
+
* response; undefined keeps the original failure. Available before authentication,
|
|
1490
|
+
* so only request metadata is given.
|
|
1491
|
+
* Public projections supply their existing typed/profile error mapper here. */
|
|
1492
|
+
readonly mapFailure?: (error: unknown, request: Pick<PluginHttpRouteRequest, 'method' | 'path' | 'query'>) => unknown;
|
|
1388
1493
|
readonly summary?: string;
|
|
1389
1494
|
readonly description?: string;
|
|
1390
1495
|
readonly example?: RestRouteExample;
|
|
@@ -1425,6 +1530,10 @@ export declare interface RestRouteDescriptor<I, O> {
|
|
|
1425
1530
|
*/
|
|
1426
1531
|
readonly version?: `v${number}`;
|
|
1427
1532
|
readonly guards?: ReadonlyArray<RestGuard>;
|
|
1533
|
+
readonly cors?: RestCors;
|
|
1534
|
+
/** Only an explicit bearer header may authenticate; cookies are removed before auth and context construction. */
|
|
1535
|
+
readonly credentials?: 'bearer';
|
|
1536
|
+
readonly rateLimit?: RestRateLimit;
|
|
1428
1537
|
/**
|
|
1429
1538
|
* This route STREAMS (Server-Sent Events) rather than resolving one value — its
|
|
1430
1539
|
* handler returns `sse(...)`. Declared on the descriptor, not left implicit in
|
|
@@ -1510,12 +1619,23 @@ export declare const restRoutesToHttpRoutes: (routes: ReadonlyArray<RestRouteDes
|
|
|
1510
1619
|
* auth subject + the bound DataStore.
|
|
1511
1620
|
*/
|
|
1512
1621
|
export declare interface RestServeBindings {
|
|
1513
|
-
/**
|
|
1514
|
-
readonly
|
|
1622
|
+
/** Pure scope-refresher registration, shared with native RPC admission. */
|
|
1623
|
+
readonly bindReplayAuthority?: RpcReplayAuthorityBinding;
|
|
1624
|
+
/** Actual host policy for projected procedure guards; raw routes own guards. */
|
|
1625
|
+
readonly defaultDeny?: boolean;
|
|
1626
|
+
/** Full runtime hosts supply their existing trace generator. */
|
|
1627
|
+
readonly traceId?: () => string;
|
|
1628
|
+
/** Bound once from the app's mounted rate-limit plugin. */
|
|
1629
|
+
readonly rateLimit?: (policy: RestRateLimit, ctx: RestRouteContext, route: RestRouteDescriptor<any, any>) => Effect.Effect<void, unknown>;
|
|
1630
|
+
/** Resolve the calling subject and request-local selection before guards. */
|
|
1631
|
+
readonly resolveSubject?: (headers: Readonly<Record<string, string>>, request: {
|
|
1632
|
+
readonly path: string;
|
|
1633
|
+
readonly query: string;
|
|
1634
|
+
}) => Subject | Promise<Subject>;
|
|
1515
1635
|
/** The bound framework DataStore. */
|
|
1516
1636
|
readonly store?: unknown;
|
|
1517
1637
|
/** Build the full per-request app context after authentication. */
|
|
1518
|
-
readonly buildContext?: (subject: Subject, headers: Readonly<Record<string, string
|
|
1638
|
+
readonly buildContext?: (subject: Subject, headers: Readonly<Record<string, string>>, request: Pick<RestRouteContext, 'traceId' | 'remoteAddr'>) => unknown | Promise<unknown>;
|
|
1519
1639
|
/**
|
|
1520
1640
|
* HTTP idempotency — when set, a mutating request (POST/PUT/PATCH/DELETE)
|
|
1521
1641
|
* carrying the `header` is deduplicated: the first call runs + caches its
|
|
@@ -1570,6 +1690,72 @@ export declare const routeAcceptsPath: (route: {
|
|
|
1570
1690
|
readonly match?: "prefix" | "exact" | undefined;
|
|
1571
1691
|
}, path: string) => boolean;
|
|
1572
1692
|
|
|
1693
|
+
/**
|
|
1694
|
+
* Per-call context passed to every rpc interceptor. The fields are
|
|
1695
|
+
* deliberately shallow — plugins shouldn't need the full AppContext +
|
|
1696
|
+
* DataStore to do their job. For runtime services (DataStore, custom
|
|
1697
|
+
* Tags, etc.) declare them via `definePlugin({ services })` and read
|
|
1698
|
+
* via `yield* MyTag` in the interceptor body; the framework provides
|
|
1699
|
+
* them automatically.
|
|
1700
|
+
*/
|
|
1701
|
+
declare interface RpcInterceptorContext {
|
|
1702
|
+
/** Enqueue a settled invocation outcome on the host's root store, outside
|
|
1703
|
+
* the business transaction. Required transactional success capture must use
|
|
1704
|
+
* MutationCommitContext.outbox instead. Older/custom hosts may omit this. */
|
|
1705
|
+
readonly enqueueOutcome?: OutboxEnqueueFacade['enqueue'];
|
|
1706
|
+
/** Procedure tag, e.g. `todos.create`. */
|
|
1707
|
+
readonly tag: string;
|
|
1708
|
+
/** Procedure kind, including finite stream admission. */
|
|
1709
|
+
readonly kind: RpcKind;
|
|
1710
|
+
/** The validated input the executor is about to (or already did) receive. */
|
|
1711
|
+
readonly input: unknown;
|
|
1712
|
+
/** The resolved subject (user / apiKey / serviceAccount / anonymous). */
|
|
1713
|
+
readonly subject: Subject;
|
|
1714
|
+
/** Per-call trace id (matches OTel spans + inspect events). */
|
|
1715
|
+
readonly traceId: string;
|
|
1716
|
+
/** Active OTel span id (16-hex), when tracing is on. Lets observability
|
|
1717
|
+
* plugins (Sentry/Datadog) attach an error to the exact span, not just
|
|
1718
|
+
* the trace. Absent when no tracer is installed. */
|
|
1719
|
+
readonly spanId?: string;
|
|
1720
|
+
/**
|
|
1721
|
+
* `'rest'` when the call came through the `publicApi` projection (`/v1`);
|
|
1722
|
+
* absent on the rpc wire. A per-endpoint policy on the projection — the
|
|
1723
|
+
* descriptor's `publicApi.rateLimit` — applies to these calls only.
|
|
1724
|
+
*/
|
|
1725
|
+
readonly surface?: 'rest';
|
|
1726
|
+
/** The descriptor's `publicApi` spec, on a projected call. */
|
|
1727
|
+
readonly publicApi?: PublicApiSpec;
|
|
1728
|
+
/**
|
|
1729
|
+
* The caller's address — the same value the pre-auth IP shield counts, resolved
|
|
1730
|
+
* by the listener through `security.trustedProxies` (and
|
|
1731
|
+
* `security.clientAddressHeader`). A client cannot set it: the listener
|
|
1732
|
+
* writes it, and any copy the client sends is discarded.
|
|
1733
|
+
*
|
|
1734
|
+
* Absent when the call did not arrive over the network (a workflow step, a
|
|
1735
|
+
* schedule, an in-process invocation) or the socket address is unknown.
|
|
1736
|
+
*/
|
|
1737
|
+
readonly remoteAddr?: string;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
/**
|
|
1741
|
+
* Which kind of rpc the interceptor is wrapping. The runtime passes
|
|
1742
|
+
* `kind: 'mutation'` to interceptors registered as `interceptMutation`,
|
|
1743
|
+
* `kind: 'query'` to `interceptQuery`, `kind: 'action'` to
|
|
1744
|
+
* `interceptAction`, `kind: 'workflow'` to `interceptWorkflow`, and
|
|
1745
|
+
* `kind: 'stream'` to `interceptStream` — one
|
|
1746
|
+
* plugin can implement any combination.
|
|
1747
|
+
*/
|
|
1748
|
+
declare type RpcKind = 'mutation' | 'query' | 'action' | 'workflow' | 'stream';
|
|
1749
|
+
|
|
1750
|
+
declare type RpcReplayAuthorityBinding = (context: RpcReplayAuthorityContext) => undefined;
|
|
1751
|
+
|
|
1752
|
+
/** Pure synchronous authority-refresher binding for RPC and REST admission.
|
|
1753
|
+
* No business work, I/O, grants or side effects belong in this hook. The host
|
|
1754
|
+
* calls the existing guard rechecker after every binding. */
|
|
1755
|
+
declare interface RpcReplayAuthorityContext extends Pick<RpcInterceptorContext, 'tag' | 'input' | 'subject' | 'traceId' | 'surface' | 'publicApi' | 'remoteAddr'> {
|
|
1756
|
+
readonly kind: RpcKind | 'rest';
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1573
1759
|
/**
|
|
1574
1760
|
* The runtime-erased form of a procedure's `service: { callers }` — "only these
|
|
1575
1761
|
* apps of the project may call this, and nobody else".
|
|
@@ -1613,6 +1799,7 @@ declare interface ServiceSpec {
|
|
|
1613
1799
|
*/
|
|
1614
1800
|
export declare const sse: (subscribe: (emit: (chunk: string) => void) => () => void, options?: {
|
|
1615
1801
|
readonly keepAliveMs?: number;
|
|
1802
|
+
readonly maxDurationMs?: number;
|
|
1616
1803
|
}) => RestStreamResponse;
|
|
1617
1804
|
|
|
1618
1805
|
/** Frame ONE SSE event. Newlines in `data` are split across `data:` lines, as
|
|
@@ -1621,6 +1808,9 @@ export declare const sseFrame: (event: string, data: unknown) => string;
|
|
|
1621
1808
|
|
|
1622
1809
|
declare const Subject: Schema.Union<readonly [Schema.Struct<{
|
|
1623
1810
|
readonly type: Schema.Literal<"user">;
|
|
1811
|
+
/** A guest has a stable identity but has not linked a registered account.
|
|
1812
|
+
* This identity mark survives session signing; it never grants authority. */
|
|
1813
|
+
readonly kind: Schema.optional<Schema.Literal<"guest">>;
|
|
1624
1814
|
readonly id: Schema.String;
|
|
1625
1815
|
readonly tenantId: Schema.String;
|
|
1626
1816
|
readonly scopes: Schema.optional<Schema.$Array<Schema.String>>;
|
|
@@ -1749,6 +1939,12 @@ declare interface UpdateTarget<Input = unknown, Row = unknown, Item = Record<str
|
|
|
1749
1939
|
readonly shapeItem?: ((input: Input, current: Item) => Item) | undefined;
|
|
1750
1940
|
}
|
|
1751
1941
|
|
|
1942
|
+
/** Require a registered human identity. Guest and machine credentials cannot
|
|
1943
|
+
* satisfy this requirement, including credentials holding admin scopes. */
|
|
1944
|
+
declare interface UpgradedGuardSpec {
|
|
1945
|
+
readonly upgraded: true;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1752
1948
|
export declare const wireExample: (example: {
|
|
1753
1949
|
readonly request?: unknown;
|
|
1754
1950
|
readonly response?: unknown;
|