@voltro/protocol 0.81.0 → 0.82.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/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ import { RpcMiddleware } from '@effect/rpc';
8
8
  import { Schema } from 'effect';
9
9
  import { SqlClient } from '@effect/sql';
10
10
  import { Stream } from '@effect/rpc/RpcSchema';
11
+ import { VersionConflict } from '@voltro/database/wire';
11
12
 
12
13
  export declare interface ActionProcedureDescriptor<Name extends string, Input extends Schema.Schema.Any, Output extends Schema.Schema.Any, Error extends Schema.Schema.All> {
13
14
  readonly kind: 'action';
@@ -83,6 +84,46 @@ export declare const ADMIN_SCOPE = "admin:full";
83
84
  * so the wording lives beside the semantics it describes. */
84
85
  export declare const advisoryResourceGuardWarning: (tags: ReadonlyArray<string>) => string;
85
86
 
87
+ /**
88
+ * What an UNAUTHENTICATED caller is — the app's decision, never the request's.
89
+ *
90
+ * A request that matches no auth strategy still resolves to a Subject, and
91
+ * that Subject's `tenantId` decides what every tenant-scoped table and every
92
+ * `openAccess` procedure answers. Left to the request, the `x-tenant` header
93
+ * would choose it: any client could read any tenant's public-per-tenant data,
94
+ * and a procedure that was `openAccess` for the tenant's MEMBERS would answer
95
+ * for whichever tenant the header named. So the policy is declared once, in
96
+ * `app.config.ts` (`auth.anonymous`), and `voltro serve` refuses to boot
97
+ * without it.
98
+ *
99
+ * - `'refuse'` — no unauthenticated callers at all: `Unauthenticated`.
100
+ * - `'no-tenant'` — the caller carries the empty tenant `''`: every
101
+ * tenant-scoped read is empty (fails closed), tables without `tenant()` and
102
+ * procedures that need no tenant answer. The default of the composer, and
103
+ * the right choice for an api whose public surface is token-shaped (an
104
+ * invite preview, a signed link).
105
+ * - `'header'` — the tenant is what the request's `x-tenant` header names; a
106
+ * request without it is refused. For public-per-tenant data (a marketing
107
+ * site per tenant, public listings by slug) — and ONLY that: every
108
+ * `openAccess` procedure and every table without `tenant()` is then public
109
+ * for a client-chosen tenant.
110
+ * - `{ tenantId }` — every unauthenticated caller is that ONE tenant, chosen
111
+ * here; the header is not consulted.
112
+ *
113
+ * NEVER `null`: a null tenant is a SYSTEM subject to the store and reads
114
+ * unscoped, across every tenant.
115
+ */
116
+ export declare type AnonymousPolicy = 'refuse' | 'no-tenant' | 'header' | {
117
+ readonly tenantId: string;
118
+ };
119
+
120
+ /** The development boot's convenience, which no app config can name: the
121
+ * header's tenant, else the dev tenant. `voltro dev` runs an undecided app
122
+ * with it and says so; `voltro serve` refuses instead. */
123
+ export declare type AnonymousPolicyOrDevTenant = AnonymousPolicy | {
124
+ readonly headerOrTenant: string;
125
+ };
126
+
86
127
  export declare const anonymousSubject: (tenantId: string | null, credentialRejected?: string) => Subject;
87
128
 
88
129
  /** The erased policy a descriptor carries (input generic dropped). */
@@ -155,6 +196,9 @@ export declare interface AppliedScopes {
155
196
  readonly removed: ReadonlyArray<string>;
156
197
  }
157
198
 
199
+ /** Keep a pairing for the next boot; the app reads it when it builds its api config. */
200
+ export declare const applyPairing: (storage: PairingStorage, pairing: Pairing) => Promise<void>;
201
+
158
202
  export declare const applyRowPatch: (prev: ReadonlyArray<PatchRow>, patch: RowPatch, options?: ApplyRowPatchOptions) => ReadonlyArray<PatchRow>;
159
203
 
160
204
  /**
@@ -574,7 +618,7 @@ export declare interface AuthStrategy {
574
618
  readonly resolve: (input: AuthStrategyInput) => Promise<StrategyResolution> | StrategyResolution;
575
619
  /**
576
620
  * The bearer-token PREFIX this strategy claims, when it gates on one
577
- * (`'sk_'`, `'awb_'`). Declared so a collision is DETECTABLE.
621
+ * (`'sk_'`, `'acme_'`). Declared so a collision is DETECTABLE.
578
622
  *
579
623
  * Two strategies claiming the same prefix is not a harmless duplicate: the
580
624
  * chain is first-match-wins, so whichever runs first decides the Subject —
@@ -772,9 +816,14 @@ export declare const checkGuards: (subject: Subject, guards: ReadonlyArray<AnyCh
772
816
  *
773
817
  * Returns a typed `ScopeError` naming the first unmet scope, or `null` when
774
818
  * every guard passes. The runtime calls this in the dispatch spine BEFORE the
775
- * executor (mutations: before the transaction opens).
819
+ * executor (mutations: before the transaction opens). A resolver or tuple
820
+ * source that answers `'missing'` turns the refusal into `NoSuchResource` for
821
+ * a signed-in caller — the first failing guard decides, so a scope refusal
822
+ * ahead of it is never upgraded into a disclosure.
776
823
  */
777
- export declare const checkGuardsEffect: (subject: Subject, guards: ReadonlyArray<AnyCheckSpec> | undefined, input: unknown, options?: GuardCheckOptions) => Effect.Effect<ScopeError | Unauthenticated | null>;
824
+ export declare const checkGuardsEffect: (subject: Subject, guards: ReadonlyArray<AnyCheckSpec> | undefined, input: unknown, options?: GuardCheckOptions) => Effect.Effect<ScopeError | Unauthenticated | NoSuchResource | null>;
825
+
826
+ export declare const clearPairing: (storage: PairingStorage) => Promise<void>;
778
827
 
779
828
  export declare interface ClientDescriptor {
780
829
  readonly kind: 'query' | 'mutation' | 'action' | 'stream' | 'workflow';
@@ -835,42 +884,17 @@ export declare interface ClientTarget {
835
884
  export declare const compareSurfaces: (mine: Readonly<Record<string, string>>, theirs: Readonly<Record<string, string>>) => SurfaceMismatch;
836
885
 
837
886
  export declare const composeAuthStrategies: (strategies: ReadonlyArray<AuthStrategy>, options?: {
838
- /** Fallback when no strategy matched. Default: returns the input's
839
- * tenant from `x-tenant` header or null. */
887
+ /** Fallback when no strategy matched. Replaces `anonymous` entirely: the
888
+ * function owns the anonymous subject AND the rejected-credential decision. */
840
889
  readonly fallback?: (input: AuthStrategyInput) => Subject;
841
890
  /** Hook for logging — called on every `failed` resolution. */
842
891
  readonly onStrategyFailed?: (event: {
843
892
  strategyId: string;
844
893
  reason: string;
845
894
  }) => void;
846
- /**
847
- * Reject anonymous callers that carry no tenant. When `true` and no
848
- * strategy matched AND no `x-tenant` header is present, the resolver
849
- * throws `Unauthenticated` instead of returning a null-tenant
850
- * anonymous Subject. Use it on multi-tenant apps where every call —
851
- * even an anonymous one — must be scoped to a tenant; without it a
852
- * tenant-less anonymous Subject can read across the whole DB on
853
- * tables that aren't `tenant()`-scoped. Ignored when a custom
854
- * `fallback` is supplied (the fallback owns that decision).
855
- */
856
- readonly anonymousTenantRequired?: boolean;
857
- /**
858
- * The ONE tenant every anonymous caller resolves to — chosen by the server,
859
- * never by the request. When set, the `x-tenant` header is not consulted:
860
- * a client naming its own tenant is exactly the thing a fixed anonymous
861
- * tenant exists to rule out. Ignored when `fallback` is supplied (that
862
- * function owns the anonymous subject).
863
- */
864
- readonly anonymousTenantId?: string;
865
- /**
866
- * The tenant an anonymous caller resolves to when its request names none
867
- * in `x-tenant`. The header still wins when present — this only replaces
868
- * the `null` tenant. It exists so a boot path can supply a default tenant
869
- * without a `fallback`: a `fallback` owns the rejected-credential decision,
870
- * this does not, so a presented-and-rejected credential is still refused.
871
- * Ignored when `anonymousTenantId` or `fallback` is set.
872
- */
873
- readonly anonymousTenantDefault?: string;
895
+ /** What an unauthenticated caller is — see {@link AnonymousPolicy}.
896
+ * Default `'no-tenant'`. Ignored when `fallback` is supplied. */
897
+ readonly anonymous?: AnonymousPolicyOrDevTenant;
874
898
  /**
875
899
  * The subject's memberships — the ONE source, strategy-independent. Read
876
900
  * only when a stored tenant selection exists for the subject and names a
@@ -1366,8 +1390,8 @@ export declare interface CoordinatedScheduleHandle {
1366
1390
  */
1367
1391
  export declare interface CoordinatedTaskOptions {
1368
1392
  /**
1369
- * Stop ticking entirely on an idle tick with no pending deadline, and come
1370
- * back only on `wake()`. Default `false`.
1393
+ * Stop ticking entirely on an idle tick that reports `nextDueInMs: null`
1394
+ * (nothing scheduled), and come back only on `wake()`. Default `false`.
1371
1395
  *
1372
1396
  * Pass `true` only when an arrival is GUARANTEED to call `wake()` — a change
1373
1397
  * subscription on the table your task drains, on a deployment where remote
@@ -1381,10 +1405,17 @@ export declare interface CoordinatedTaskOptions {
1381
1405
  export declare interface CoordinatedTickOutcome {
1382
1406
  /** `true` when the tick found nothing to do. Only an idle tick backs off. */
1383
1407
  readonly idle: boolean;
1384
- /** Milliseconds until the earliest deadline this task already knows about.
1385
- * Caps the backoff, so a task that is idle now but has something due in
1386
- * 400 ms is armed for 400 ms rather than for the idle ceiling. */
1387
- readonly nextDueInMs?: number;
1408
+ /** Milliseconds until the earliest deadline this task already knows about —
1409
+ * a retry, a re-poll, a lease running out. Caps the backoff, so a task that
1410
+ * is idle now but has something due in 400 ms is armed for 400 ms rather
1411
+ * than for the idle ceiling.
1412
+ *
1413
+ * `null` says "nothing is scheduled; new work can only arrive as a write",
1414
+ * and is the ONLY idle report that lets a `disarmWhenIdle` task stop
1415
+ * ticking. Leaving the field out backs off instead: work that comes due on
1416
+ * a clock writes nothing, so nothing would ever wake a task that disarmed
1417
+ * while holding it. */
1418
+ readonly nextDueInMs?: number | null;
1388
1419
  }
1389
1420
 
1390
1421
  /**
@@ -1402,6 +1433,14 @@ export declare type DeclaredAccess<Input = unknown> = ReadonlyArray<AnyGuardSpec
1402
1433
  /** Every channel declared in this process, by routing key. */
1403
1434
  export declare const declaredReactivityChannelKeys: () => ReadonlySet<string>;
1404
1435
 
1436
+ /**
1437
+ * Does this guard list name a resource — a relationship guard, or a scope guard
1438
+ * with a `resource` extractor? Exactly the guards whose resolver may answer
1439
+ * `'missing'`, so exactly the procedures whose wire union carries
1440
+ * {@link NoSuchResource}.
1441
+ */
1442
+ export declare const declaresResourceGuard: (guards: ReadonlyArray<AnyCheckSpec> | undefined) => boolean;
1443
+
1405
1444
  /**
1406
1445
  * How long a RUNNING claim holds without renewal.
1407
1446
  *
@@ -2025,6 +2064,20 @@ export declare const encodeRestError: (format: RestFormat, error: RestFormatErro
2025
2064
  */
2026
2065
  export declare const encodeRestSuccess: (format: RestFormat, value: unknown, ctx: RestFormatContext) => RestFormatResponse;
2027
2066
 
2067
+ /**
2068
+ * The text to SHOW for a failure value — its `message`, else its `_tag`, else
2069
+ * `String(value)`.
2070
+ *
2071
+ * Its partner above, for the same reason: every client surface hands over the
2072
+ * declared error as the plain `{ _tag, …fields, message }` object the wire
2073
+ * carries, and `String()` of a plain object is `[object Object]`. A
2074
+ * subscription used to hand over an Effect `Cause`, whose `String()` happened
2075
+ * to read, so `String(error)` in an error banner or an `error.tsx` looked right
2076
+ * until the value became what the docs always said it was. Works on an `Error`,
2077
+ * a `Schema.TaggedError` instance, the wire object and a bare string alike.
2078
+ */
2079
+ export declare const errorMessage: (err: unknown) => string;
2080
+
2028
2081
  /**
2029
2082
  * Read the `_tag` off a thrown error value, or `undefined` when it has none.
2030
2083
  *
@@ -2400,9 +2453,10 @@ export declare const finishIdempotent: (store: IdempotencyStore, scope: string,
2400
2453
  *
2401
2454
  * A query's success type is the three-member union above, and a one-shot read
2402
2455
  * takes only its first event: a `snapshot` resolves to its `data`; an `error`
2403
- * REJECTS — with the typed error on `cause` and `voltroError`, so a caller can
2404
- * still branch on its `_tag` — exactly as a failed exit would; a `delta` cannot
2405
- * be first and says so. Anything else is a mutation or action's own result and
2456
+ * REJECTS with the typed error ITSELF — exactly as a failed exit does, and the
2457
+ * same value a live subscription's `error` and the api's error bus hand over,
2458
+ * so one `_tag` switch serves every surface; a `delta` cannot be first and says
2459
+ * so. Anything else is a mutation or action's own result and
2406
2460
  * passes through, which may legitimately be a tagged object of the app's.
2407
2461
  *
2408
2462
  * Shared because there are two such reads — the server loader's `ctx.query`
@@ -2418,6 +2472,27 @@ export declare const firstEventValue: (tag: string, value: unknown, label: strin
2418
2472
  /** A route rendered for humans — logs, the inspect surface, the dashboard. */
2419
2473
  export declare const formatEventRoute: (route: string) => string;
2420
2474
 
2475
+ /**
2476
+ * The framework errors EVERY procedure of a kind carries on its wire union —
2477
+ * the unconditional members `wireErrorUnion` adds. The conditional ones (a
2478
+ * resource guard's `NoSuchResource`, `requiresApproval`'s refusals,
2479
+ * `MutationConnectionRequired`) are left out on purpose: a type that cannot see
2480
+ * the condition must not promise them.
2481
+ *
2482
+ * It exists for the TYPE-level reader, `defineExecutor`, which checks an
2483
+ * executor's failure channel against the union the server will judge it by. A
2484
+ * test pins it against `wireErrorUnion` per kind, so the two cannot drift.
2485
+ */
2486
+ export declare const FRAMEWORK_WIRE_ERRORS: {
2487
+ readonly query: readonly [typeof ScopeError, typeof Unauthenticated];
2488
+ readonly stream: readonly [typeof ScopeError, typeof Unauthenticated];
2489
+ readonly action: readonly [typeof ScopeError, typeof Unauthenticated, typeof ValidationError, typeof ValidationErrors];
2490
+ readonly mutation: readonly [typeof ScopeError, typeof Unauthenticated, typeof BusinessRuleViolation, typeof MutationInFlight, typeof VersionConflict, typeof ValidationError, typeof ValidationErrors];
2491
+ };
2492
+
2493
+ /** The error values {@link FRAMEWORK_WIRE_ERRORS} admits for `Kind`. */
2494
+ export declare type FrameworkWireError<Kind extends WireProcedureKind> = Schema.Schema.Type<(typeof FRAMEWORK_WIRE_ERRORS)[Kind][number]>;
2495
+
2421
2496
  /** How long a record of this status stays fresh — the replay window for a
2422
2497
  * completed response, the lease for a running one (never longer than the window). */
2423
2498
  export declare const freshFor: (status: IdempotencyRecord["status"], ttlMs: number, inFlightLeaseMs: number) => number;
@@ -3008,7 +3083,7 @@ export declare interface NestedTargetFields<Input = unknown> {
3008
3083
  /** Guard WHICH cached query entries this target patches: only entries whose
3009
3084
  * CURRENT value satisfies the predicate. Pure + browser-safe. Prevents a
3010
3085
  * patch from bleeding across sibling subscriptions that share a source table
3011
- * (the guard AWB hand-writes as `roadmap.id === input.roadmapId`). Absent →
3086
+ * (the guard an app would otherwise hand-write as `roadmap.id === input.roadmapId`). Absent →
3012
3087
  * every entry matching the target `table` is patched. */
3013
3088
  readonly match?: ((value: unknown, input: Input) => boolean) | undefined;
3014
3089
  }
@@ -3063,6 +3138,32 @@ declare const NoRowFound_base: Schema.TaggedErrorClass<NoRowFound, "NoRowFound",
3063
3138
  found: typeof Schema.Number;
3064
3139
  }>;
3065
3140
 
3141
+ /**
3142
+ * A resource guard's refusal when the resource it names does not exist — as
3143
+ * far as THIS caller may know.
3144
+ *
3145
+ * Only an app can say that. The guard never infers absence from an empty tuple
3146
+ * set or a `false` answer: "no relation" and "no row" look identical from the
3147
+ * outside, and telling them apart is an existence oracle. So this is raised
3148
+ * only when a tuple source or resolver answers `'missing'`
3149
+ * ({@link ResourceMissing}), and only for a signed-in caller — an anonymous one
3150
+ * gets the ordinary refusal whatever the source said.
3151
+ *
3152
+ * `resourceType` is the relationship guard's type, or `null` for a scope guard
3153
+ * with a `resource` extractor (which names no type). Merged into the wire union
3154
+ * of every procedure that declares a resource-bearing guard.
3155
+ */
3156
+ export declare class NoSuchResource extends NoSuchResource_base {
3157
+ }
3158
+
3159
+ declare const NoSuchResource_base: Schema.TaggedErrorClass<NoSuchResource, "NoSuchResource", {
3160
+ readonly _tag: Schema.tag<"NoSuchResource">;
3161
+ } & {
3162
+ resourceType: Schema.NullOr<typeof Schema.String>;
3163
+ resourceId: typeof Schema.String;
3164
+ message: typeof Schema.String;
3165
+ }>;
3166
+
3066
3167
  /**
3067
3168
  * What a plugin contributes to the OTel layer via `contributeObservability`.
3068
3169
  * The OTel types are intentionally `unknown` so this browser-safe protocol
@@ -3147,6 +3248,38 @@ export declare const openAccessSpec: (reason: string) => OpenAccessSpec;
3147
3248
  */
3148
3249
  export declare const orderByBearerPrefix: (strategies: ReadonlyArray<AuthStrategy>, headers: Readonly<Record<string, string | undefined>>) => ReadonlyArray<AuthStrategy>;
3149
3250
 
3251
+ export declare interface Pairing {
3252
+ /** The api's base url as the DEVICE reaches it — a LAN address, never `localhost`. */
3253
+ readonly api: string;
3254
+ /** The api process name (`main`), for the client's api binding. */
3255
+ readonly name: string;
3256
+ /** The dev inspect token, when the CLI knows it. */
3257
+ readonly token?: string;
3258
+ }
3259
+
3260
+ export declare const PAIRING_HOST = "pair";
3261
+
3262
+ export declare const PAIRING_SCHEME = "voltro-dev";
3263
+
3264
+ /** Where `applyPairing` keeps the record — the platform's plain storage. */
3265
+ export declare const PAIRING_STORAGE_KEY = "voltro:pairing";
3266
+
3267
+ /** The link text. Refuses a loopback api: a phone cannot reach the laptop's `localhost`. */
3268
+ export declare const pairingLink: (pairing: Pairing) => string;
3269
+
3270
+ /** A key/value store in either spelling: the platform's `StoreStorage`
3271
+ * (`getItem`/`setItem`, what `platform.storage('local')` returns) or a
3272
+ * `get`/`set` pair (a secure store, a test fake). */
3273
+ export declare type PairingStorage = {
3274
+ readonly get: (key: string) => Promise<string | null> | string | null;
3275
+ readonly set: (key: string, value: string) => Promise<void> | void;
3276
+ readonly remove?: (key: string) => Promise<void> | void;
3277
+ } | {
3278
+ readonly getItem: (key: string) => Promise<string | null> | string | null;
3279
+ readonly setItem: (key: string, value: string) => Promise<void> | void;
3280
+ readonly removeItem?: (key: string) => Promise<void> | void;
3281
+ };
3282
+
3150
3283
  /** Split a route back into its three parts. */
3151
3284
  export declare const parseEventRoute: (route: string) => {
3152
3285
  readonly tenantId: string | null;
@@ -3154,6 +3287,11 @@ export declare const parseEventRoute: (route: string) => {
3154
3287
  readonly key: string;
3155
3288
  };
3156
3289
 
3290
+ /** The record from a link, or `null` for anything that is not a pairing link. */
3291
+ export declare const parsePairingLink: (url: string) => Pairing | null;
3292
+
3293
+ export declare const patchCellSchema: Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>;
3294
+
3157
3295
  /** A single row in a subscription result. Must carry an `id`; everything
3158
3296
  * else is opaque to the patch layer. */
3159
3297
  export declare type PatchRow = Readonly<Record<string, unknown>> & {
@@ -3524,16 +3662,29 @@ export declare interface PluginDashboardMount {
3524
3662
  export declare type PluginDeactivateHook = (ctx: PluginLifecycleContext) => Effect.Effect<void, unknown> | Promise<void> | void;
3525
3663
 
3526
3664
  /**
3527
- * An environment variable a plugin reads. DECLARATION ONLY — metadata, not a
3528
- * read path: the plugin still reads the value itself (typically
3529
- * `options.X ?? process.env.X`). Declaring it makes the plugin's env needs
3530
- * visible to the env manifest (`/_voltro/inspect/env`), the generated
3531
- * `.env.example`, and the dashboard Env panel — so an operator can see, before
3532
- * running, exactly what each plugin expects.
3533
- *
3534
- * Structurally identical to `@voltro/env`'s `DeclaredEnvVar` (kept local here
3535
- * to avoid a protocol → env package dependency).
3665
+ * One place a plugin sends data outside the installation — folded by
3666
+ * `voltro compliance generate` into `egress.json`, the machine-readable list an
3667
+ * operator reviews before opening a firewall, and answers an air-gap question
3668
+ * from. Declare what the plugin CAN reach with the options it was given: a
3669
+ * storage plugin on the database provider reaches nothing; on s3 it reaches
3670
+ * the bucket's endpoint.
3536
3671
  */
3672
+ export declare interface PluginEgressDeclaration {
3673
+ /**
3674
+ * Where: a host (`api.example.com`), a URL, or — when the address is data,
3675
+ * not configuration — what decides it, prefixed `runtime:`
3676
+ * (`runtime:webhook targets`).
3677
+ */
3678
+ readonly destination: string;
3679
+ /** Why the data goes there, in one line. */
3680
+ readonly purpose: string;
3681
+ /** What leaves: `content` (what users write), `personal` (who someone is),
3682
+ * `files`, `metadata` (ids, names, timestamps), `telemetry`, `credentials`. */
3683
+ readonly data: ReadonlyArray<'content' | 'personal' | 'files' | 'metadata' | 'telemetry' | 'credentials'>;
3684
+ /** How an operator turns it off, and what the installation does then. */
3685
+ readonly disable: string;
3686
+ }
3687
+
3537
3688
  export declare interface PluginEnvVar {
3538
3689
  /** The env var name, e.g. `DD_API_KEY`. */
3539
3690
  readonly name: string;
@@ -4099,6 +4250,14 @@ export declare type PluginMigrationSqlClient = SqlClient.SqlClient;
4099
4250
  */
4100
4251
  export declare type PluginPermission = 'rpc:intercept:mutation' | 'rpc:intercept:query' | 'rpc:intercept:action' | 'rpc:intercept:workflow' | 'schedule:fire' | 'schedule:read' | 'workflow:step' | 'http:intercept' | 'inspect:read' | 'inspect:write' | 'dashboard:mount' | 'store:write' | 'migration:run' | 'store:changes:read' | `secrets:read:${string}` | `network:outbound:${string}` | `plugin:hook:${string}`;
4101
4252
 
4253
+ /** What a plugin collects and uses — folded by `voltro compliance generate`
4254
+ * into PrivacyInfo.xcprivacy and the Play Data-safety answers. A plugin that
4255
+ * moves data (routes, tables) and declares nothing is listed by name. */
4256
+ export declare interface PluginPrivacyDeclaration {
4257
+ readonly collects?: ReadonlyArray<PrivacyCollectedData>;
4258
+ readonly requiredReasonApis?: ReadonlyArray<PrivacyRequiredReasonApi>;
4259
+ }
4260
+
4102
4261
  /**
4103
4262
  * Per-call context handed to a plugin-contributed rpc route's
4104
4263
  * executor. Mirror of the user-handler `AppContext` shape but typed
@@ -4373,9 +4532,10 @@ export declare interface PolicyGuardRequest {
4373
4532
  * Answers "does `subject` have `action` on `<resourceType>:<resourceId>`".
4374
4533
  * Registered once at boot via `setPolicyGuardResolver`. A failed Effect is
4375
4534
  * treated as a DENIAL — a resolver wanting another posture catches its own
4376
- * errors.
4535
+ * errors. Only `true` allows; `'missing'` refuses with {@link NoSuchResource}
4536
+ * for a signed-in caller.
4377
4537
  */
4378
- export declare type PolicyGuardResolver = (req: PolicyGuardRequest) => Effect.Effect<boolean, unknown>;
4538
+ export declare type PolicyGuardResolver = (req: PolicyGuardRequest) => Effect.Effect<ResourceVerdict, unknown>;
4379
4539
 
4380
4540
  /**
4381
4541
  * A relationship (ReBAC) guard — "may this subject perform ACTION on THIS row?"
@@ -4407,6 +4567,38 @@ export declare interface PolicyGuardSpec<Input = unknown> {
4407
4567
  readonly resource: (input: Input) => string | undefined;
4408
4568
  }
4409
4569
 
4570
+ /**
4571
+ * An environment variable a plugin reads. DECLARATION ONLY — metadata, not a
4572
+ * read path: the plugin still reads the value itself (typically
4573
+ * `options.X ?? process.env.X`). Declaring it makes the plugin's env needs
4574
+ * visible to the env manifest (`/_voltro/inspect/env`), the generated
4575
+ * `.env.example`, and the dashboard Env panel — so an operator can see, before
4576
+ * running, exactly what each plugin expects.
4577
+ *
4578
+ * Structurally identical to `@voltro/env`'s `DeclaredEnvVar` (kept local here
4579
+ * to avoid a protocol → env package dependency).
4580
+ */
4581
+ /** One kind of data an app or plugin collects, in the vocabulary both stores
4582
+ * understand (`voltro compliance generate` maps it to Apple's and Google's). */
4583
+ export declare interface PrivacyCollectedData {
4584
+ readonly category: 'name' | 'email' | 'phone' | 'physical-address' | 'identifiers' | 'device-id' | 'payment-info' | 'purchase-history' | 'precise-location' | 'coarse-location' | 'photos-or-videos' | 'audio' | 'user-content' | 'browsing-history' | 'search-history' | 'product-interaction' | 'crash-data' | 'performance-data' | 'other-diagnostic-data' | 'other-data';
4585
+ /** Linked to the user's identity. */
4586
+ readonly linked: boolean;
4587
+ /** Used to track the user across apps or sites (advertising). */
4588
+ readonly tracking: boolean;
4589
+ readonly purposes: ReadonlyArray<'app-functionality' | 'analytics' | 'product-personalization' | 'developer-advertising' | 'third-party-advertising' | 'other'>;
4590
+ /** Play: shared with third parties. Default false. */
4591
+ readonly shared?: boolean;
4592
+ /** Play: the user can decline it. Default false. */
4593
+ readonly optional?: boolean;
4594
+ }
4595
+
4596
+ /** Apple's required-reason API use (`NSPrivacyAccessedAPICategory…` + reason codes). */
4597
+ export declare interface PrivacyRequiredReasonApi {
4598
+ readonly api: string;
4599
+ readonly reasons: ReadonlyArray<string>;
4600
+ }
4601
+
4410
4602
  export declare const PROBLEM_JSON = "application/problem+json; charset=utf-8";
4411
4603
 
4412
4604
  /** RFC 9457 Problem Details for one failure: `type` from the tag, `title` from
@@ -4435,6 +4627,16 @@ export declare interface ProcedureLike {
4435
4627
 
4436
4628
  export declare const procedureShapeDigest: (rpc: ProcedureLike) => string;
4437
4629
 
4630
+ /**
4631
+ * The error values a procedure's wire union can carry, as far as types can
4632
+ * tell: its own `error:`, the framework's unconditional members for its kind,
4633
+ * and every plugin-registered error.
4634
+ */
4635
+ export declare type ProcedureWireError<D extends {
4636
+ readonly kind: WireProcedureKind;
4637
+ readonly error: Schema.Schema.All;
4638
+ }> = Schema.Schema.Type<D['error']> | FrameworkWireError<D['kind']> | RegisteredPluginError;
4639
+
4438
4640
  export declare const PROTOCOL_VERSION: 1;
4439
4641
 
4440
4642
  export declare type ProtocolVersion = typeof PROTOCOL_VERSION;
@@ -4577,6 +4779,13 @@ export declare interface PublicApiSpec {
4577
4779
  * optional, the rest inherited. See `PublicApiProfileConfig`.
4578
4780
  */
4579
4781
  readonly format?: PublicApiProfileConfig;
4782
+ /**
4783
+ * GET only: how long a CDN may keep a successful answer, and the tags it can
4784
+ * purge it by (`Cache-Tag`, `Surrogate-Key`). A request with a credential is
4785
+ * always answered `private` — see `RestCache`. `tags` receives the answer as
4786
+ * it goes on the wire.
4787
+ */
4788
+ readonly cache?: RestCache;
4580
4789
  }
4581
4790
 
4582
4791
  /**
@@ -4739,7 +4948,7 @@ export declare const queryToRpc: <Name extends string, Input extends Schema.Sche
4739
4948
  }>, Schema.Struct<{
4740
4949
  op: Schema.Literal<["mergeCells"]>;
4741
4950
  path: typeof Schema.String;
4742
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
4951
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
4743
4952
  }>]>>;
4744
4953
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
4745
4954
  }>;
@@ -4824,6 +5033,11 @@ export declare type ReactivitySource = TableName | ReactivityChannel;
4824
5033
  */
4825
5034
  export declare type ReactivitySourceValue = string | ReactivityChannel;
4826
5035
 
5036
+ export declare const readPairing: (storage: PairingStorage) => Promise<Pairing | null>;
5037
+
5038
+ /** Every error a plugin registers, as one union (`never` before codegen). */
5039
+ export declare type RegisteredPluginError = VoltroPluginErrors[keyof VoltroPluginErrors];
5040
+
4827
5041
  /**
4828
5042
  * Declare, for this realm, what namespace each installed plugin actually
4829
5043
  * answers to. Emitted into `rpcGroup.generated.ts` by the codegen — one call
@@ -4840,6 +5054,19 @@ export declare type ReactivitySourceValue = string | ReactivityChannel;
4840
5054
  */
4841
5055
  export declare const registerPluginAliases: (aliases: Readonly<Record<string, string>>) => void;
4842
5056
 
5057
+ /**
5058
+ * The failure value as an `Error`, for a reporter that needs one — the value
5059
+ * itself when it already is one, else an `Error` named by its `_tag` (or
5060
+ * `NonErrorThrown`) with `errorMessage(value)` as its message and the value as
5061
+ * its `cause`.
5062
+ *
5063
+ * A loader that does not catch a refusal throws the declared error, which in a
5064
+ * web process is the plain object decoded from the wire. Handed to an error
5065
+ * reporter as it is, that becomes "Object captured as exception": no name to
5066
+ * group by, no stack, no message.
5067
+ */
5068
+ export declare const reportableError: (err: unknown) => Error;
5069
+
4843
5070
  /** Gate a handler on a scope; fails with a typed `ScopeError` if missing.
4844
5071
  * Checks the EFFECTIVE set so role-derived scopes count. */
4845
5072
  export declare const requireScope: (subject: Subject, scope: string) => Effect.Effect<void, ScopeError>;
@@ -4868,6 +5095,14 @@ export declare const resolveRestFormat: (config: PublicApiProfileConfig | undefi
4868
5095
  */
4869
5096
  export declare const resolveWorkflowAccess: (name: string, options: WorkflowAccessOptions<never>) => WorkflowAccess;
4870
5097
 
5098
+ /**
5099
+ * The answer a tuple source or resolver gives when the resource a guard names
5100
+ * does not exist for this caller. Return it only when disclosing that is
5101
+ * acceptable — typically after a TENANT-SCOPED read, so a row in another tenant
5102
+ * reads as missing rather than as "exists, not yours".
5103
+ */
5104
+ export declare type ResourceMissing = 'missing';
5105
+
4871
5106
  /** The question the runtime asks a registered resource-scope resolver. */
4872
5107
  export declare interface ResourceScopeRequest {
4873
5108
  /** The authenticated caller. */
@@ -4884,11 +5119,53 @@ export declare interface ResourceScopeRequest {
4884
5119
  * an Effect so it can read the app's role tables. It should NOT leak errors —
4885
5120
  * `checkGuardsEffect` treats a failed resolver as a DENIAL (fail-closed), so a
4886
5121
  * resolver that wants a different posture must catch its own errors.
5122
+ *
5123
+ * Only `true` allows. `'missing'` refuses with {@link NoSuchResource} (for a
5124
+ * signed-in caller); anything else refuses with `ScopeError`.
4887
5125
  */
4888
- export declare type ResourceScopeResolver = (req: ResourceScopeRequest) => Effect.Effect<boolean, unknown>;
5126
+ export declare type ResourceScopeResolver = (req: ResourceScopeRequest) => Effect.Effect<ResourceVerdict, unknown>;
5127
+
5128
+ /** A resource resolver's verdict: allowed, refused, or no such resource. */
5129
+ export declare type ResourceVerdict = boolean | ResourceMissing;
4889
5130
 
4890
5131
  export declare const REST_PROFILE_NAMES: ReadonlyArray<PublicApiProfileName>;
4891
5132
 
5133
+ /** A BINARY streaming response (download/export). The serve layer pipes the
5134
+ * ReadableStream without buffering — a body larger than the heap is fine.
5135
+ * The LAZY thunk form defers opening the source until the response streams. */
5136
+ declare interface RestByteResponse {
5137
+ readonly __voltroRestBytes: true;
5138
+ readonly byteStream: {
5139
+ readonly stream: ReadableStream<Uint8Array> | (() => ReadableStream<Uint8Array>);
5140
+ readonly contentLength?: number;
5141
+ readonly contentDisposition?: string;
5142
+ };
5143
+ readonly contentType?: string;
5144
+ readonly status?: number;
5145
+ readonly headers?: Readonly<Record<string, string | ReadonlyArray<string>>>;
5146
+ }
5147
+
5148
+ /**
5149
+ * Caching for a GET route's successful (200) answers.
5150
+ *
5151
+ * An anonymous request's answer is `public` for `maxAge` seconds (`max-age`
5152
+ * and `s-maxage`), `stale-while-revalidate` when set, and carries `tags` as
5153
+ * `Cache-Tag` (comma-separated) and `Surrogate-Key` (space-separated) — the
5154
+ * headers CDNs purge by. A request that carries a credential (`Authorization`
5155
+ * or a cookie) gets `private, no-cache` and no tags, whatever is declared:
5156
+ * what a key or a session may see must never land in a shared cache. Every
5157
+ * such answer says `Vary: Authorization, Cookie`. Errors are not affected.
5158
+ */
5159
+ declare interface RestCache {
5160
+ /** Seconds a cache may serve the answer without asking again. */
5161
+ readonly maxAge: number;
5162
+ /** Seconds after that a stale answer may still be served while the cache refetches. */
5163
+ readonly staleWhileRevalidate?: number;
5164
+ /** Purge tags from the answer as it goes on the wire, and the input.
5165
+ * A tag with whitespace or a comma is dropped (no header can carry it). */
5166
+ readonly tags?: (output: unknown, input: unknown) => ReadonlyArray<string>;
5167
+ }
5168
+
4892
5169
  export declare type RestDates = 'iso' | 'epoch-ms';
4893
5170
 
4894
5171
  export declare type RestEnvelope = 'none' | 'data' | 'jsonapi';
@@ -4978,6 +5255,11 @@ declare interface RestGuardRejection {
4978
5255
  readonly message: string;
4979
5256
  }
4980
5257
 
5258
+ /** What a REST handler may return: the decoded output, a `response(...)` with
5259
+ * its own status and headers, an `sse(...)` stream, or `bytes(...)` for a
5260
+ * non-JSON body (a file, an export, an XML document). */
5261
+ declare type RestHandlerResult<O> = O | RestResponse<O> | RestStreamResponse | RestByteResponse;
5262
+
4981
5263
  declare type RestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
4982
5264
 
4983
5265
  export declare type RestNaming = 'camelCase' | 'snake_case';
@@ -5041,7 +5323,7 @@ declare interface RestRouteDescriptor<I, O> {
5041
5323
  /** Output schema. The handler returns the decoded `Type`; the desugar
5042
5324
  * encodes it to JSON for the `200` response. */
5043
5325
  readonly output: Schema.Schema<O, any, never>;
5044
- readonly handler: (input: I, ctx: RestRouteContext) => Promise<O | RestResponse<O>> | O | RestResponse<O>;
5326
+ readonly handler: (input: I, ctx: RestRouteContext) => Promise<RestHandlerResult<O>> | RestHandlerResult<O>;
5045
5327
  readonly summary?: string;
5046
5328
  readonly description?: string;
5047
5329
  readonly example?: RestRouteExample;
@@ -5098,6 +5380,9 @@ declare interface RestRouteDescriptor<I, O> {
5098
5380
  * of the JSON), so it is correct across content-encodings — the
5099
5381
  * transport's compression varies the bytes, not the representation. */
5100
5382
  readonly etag?: boolean;
5383
+ /** GET only: how long a shared cache (a CDN) may keep a successful answer,
5384
+ * and the tags it can be purged by. See {@link RestCache}. */
5385
+ readonly cache?: RestCache;
5101
5386
  }
5102
5387
 
5103
5388
  declare interface RestRouteExample {
@@ -5138,6 +5423,18 @@ declare interface RestRouteProjection {
5138
5423
  readonly format: RestFormat;
5139
5424
  }
5140
5425
 
5426
+ /**
5427
+ * What a REST handler returns to stream its response instead of resolving one
5428
+ * value: `sse((emit) => unsubscribe)`. The route's schema `output` is not used
5429
+ * for a stream (each frame is framed as it is produced), and everything the
5430
+ * desugar does BEFORE the handler — method gate, sunset, input decode, guards,
5431
+ * subject resolution — still applies.
5432
+ */
5433
+ declare interface RestStreamResponse {
5434
+ readonly __voltroRestStream: true;
5435
+ readonly stream: PluginHttpRouteStream;
5436
+ }
5437
+
5141
5438
  /**
5142
5439
  * Rewrite a descriptor's schema into the shape the profile puts on the wire:
5143
5440
  * `timestampMs` (`DateFromNumber`) becomes an RFC 3339 string under
@@ -5227,7 +5524,7 @@ export declare const rowPatchOpSchema: Schema.Union<[Schema.Struct<{
5227
5524
  }>, Schema.Struct<{
5228
5525
  op: Schema.Literal<["mergeCells"]>;
5229
5526
  path: typeof Schema.String;
5230
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
5527
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
5231
5528
  }>]>;
5232
5529
 
5233
5530
  export declare const rowPatchSchema: Schema.Struct<{
@@ -5261,7 +5558,7 @@ export declare const rowPatchSchema: Schema.Struct<{
5261
5558
  }>, Schema.Struct<{
5262
5559
  op: Schema.Literal<["mergeCells"]>;
5263
5560
  path: typeof Schema.String;
5264
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
5561
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
5265
5562
  }>]>>;
5266
5563
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
5267
5564
  }>;
@@ -5525,8 +5822,18 @@ export declare interface ServerErrorEvent {
5525
5822
  readonly name?: string;
5526
5823
  /** Distributed-trace id when one is in scope (workflows carry it). */
5527
5824
  readonly traceId?: string;
5528
- /** Extra context — op, runId, executionId, http status, etc. */
5825
+ /** Extra context — op, runId, executionId, http status, etc. Identity only:
5826
+ * never the primitive's data. */
5529
5827
  readonly fields?: Record<string, unknown>;
5828
+ /**
5829
+ * The error's CONTENT may carry data of a primitive declared encrypted
5830
+ * (`workflow({ encrypted: true })`): its message, its cause, a typed
5831
+ * failure's fields. A reporter that sends the event out of the process sends
5832
+ * {@link withheldServerError} instead — the class, the stack frames and the
5833
+ * identity fields, not the text. Encrypted at rest and then mailed to a
5834
+ * third party in plaintext would make the declaration decorative.
5835
+ */
5836
+ readonly contentSensitive?: boolean;
5530
5837
  }
5531
5838
 
5532
5839
  declare type ServerErrorListener = (event: ServerErrorEvent) => void;
@@ -5672,6 +5979,20 @@ export declare interface ServiceSubscription {
5672
5979
  /** The first segment of a dotted rpc tag. */
5673
5980
  export declare type ServiceTagHead<K extends string> = K extends `${infer H}.${string}` ? H : K;
5674
5981
 
5982
+ /** The request header carrying a framework session value outside a cookie. */
5983
+ export declare const SESSION_HEADER_NAME: "x-voltro-session";
5984
+
5985
+ /**
5986
+ * The session value a request presents: the dedicated header when set,
5987
+ * otherwise the named cookie. Pure — no verification, no IO.
5988
+ *
5989
+ * The header wins when both are present. A client that sets the header did so
5990
+ * deliberately; a cookie is ambient and may belong to a different app on the
5991
+ * same host (the reason `sessionExpiryFromHeaders` tolerates one that does
5992
+ * not verify). Explicit beats ambient.
5993
+ */
5994
+ export declare const sessionValueFromHeaders: (headers: Readonly<Record<string, string | undefined>>, cookieName: string, readCookie: (header: string | undefined, name: string) => string | undefined) => string | undefined;
5995
+
5675
5996
  /**
5676
5997
  * Publish the caller's fully-resolved scope set for this request (raw subject
5677
5998
  * scopes ∪ role-derived scopes ∪ any extra grants). Called by the rbac
@@ -5709,6 +6030,12 @@ export declare const shapeDigest: (text: string) => string;
5709
6030
  */
5710
6031
  export declare const sourceKeys: (source: ReactivitySourceValue | ReadonlyArray<ReactivitySourceValue> | undefined) => ReadonlyArray<string>;
5711
6032
 
6033
+ export declare const STORE_SCREENSHOT_PRESET_NAMES: [string, ...string[]];
6034
+
6035
+ export declare const STORE_SCREENSHOT_PRESETS: ReadonlyArray<StoreScreenshotPreset>;
6036
+
6037
+ export declare const STORE_SCREENSHOT_STORES: readonly ["app-store", "play", "mac-app-store", "microsoft-store"];
6038
+
5712
6039
  /**
5713
6040
  * Discriminated union of all framework-owned store errors. Use it on
5714
6041
  * a mutation's `error:` schema when you want every kind surfaced
@@ -5762,6 +6089,33 @@ declare const StoreOperationFailed_base: Schema.TaggedErrorClass<StoreOperationF
5762
6089
  cause: typeof Schema.String;
5763
6090
  }>;
5764
6091
 
6092
+ /** The PNG size the store receives: viewport × scale factor. */
6093
+ export declare const storeScreenshotPixels: (preset: StoreScreenshotPreset) => {
6094
+ readonly width: number;
6095
+ readonly height: number;
6096
+ };
6097
+
6098
+ export declare interface StoreScreenshotPreset {
6099
+ readonly name: string;
6100
+ readonly label: string;
6101
+ readonly kind: 'phone' | 'tablet' | 'desktop';
6102
+ /** The CSS viewport the page is laid out in. */
6103
+ readonly viewport: {
6104
+ readonly width: number;
6105
+ readonly height: number;
6106
+ };
6107
+ readonly deviceScaleFactor: number;
6108
+ /** Which stores accept this size. */
6109
+ readonly stores: ReadonlyArray<StoreScreenshotStore>;
6110
+ }
6111
+
6112
+ export declare const storeScreenshotPreset: (name: string) => StoreScreenshotPreset | undefined;
6113
+
6114
+ /** The presets a store takes, in table order. */
6115
+ export declare const storeScreenshotPresetsFor: (store: StoreScreenshotStore) => ReadonlyArray<StoreScreenshotPreset>;
6116
+
6117
+ export declare type StoreScreenshotStore = (typeof STORE_SCREENSHOT_STORES)[number];
6118
+
5765
6119
  export declare type StrategyResolution = {
5766
6120
  readonly kind: 'matched';
5767
6121
  readonly subject: Subject;
@@ -6095,7 +6449,7 @@ export declare const subscriptionEvent: <D extends Schema.Schema.Any>(data: D) =
6095
6449
  }>, Schema.Struct<{
6096
6450
  op: Schema.Literal<["mergeCells"]>;
6097
6451
  path: typeof Schema.String;
6098
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
6452
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
6099
6453
  }>]>>;
6100
6454
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
6101
6455
  }>;
@@ -6398,7 +6752,7 @@ export declare const toRpc: <Name extends string, Input extends Schema.Schema.An
6398
6752
  }>, Schema.Struct<{
6399
6753
  op: Schema.Literal<["mergeCells"]>;
6400
6754
  path: typeof Schema.String;
6401
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
6755
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
6402
6756
  }>]>>;
6403
6757
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
6404
6758
  }>;
@@ -6709,6 +7063,10 @@ export declare interface VoltroPlugin {
6709
7063
  * merge to the stricter `required` and the safer `secret`.
6710
7064
  */
6711
7065
  readonly declaredEnv?: ReadonlyArray<PluginEnvVar>;
7066
+ /** What this plugin collects and uses — see `PluginPrivacyDeclaration`. */
7067
+ readonly privacy?: PluginPrivacyDeclaration;
7068
+ /** Where this plugin sends data outside the installation — see `PluginEgressDeclaration`. Omit when it sends nothing. */
7069
+ readonly egress?: ReadonlyArray<PluginEgressDeclaration>;
6712
7070
  /**
6713
7071
  * The authorization scopes this plugin DEFINES for the app — its scope
6714
7072
  * vocabulary. Distinct from `permissions` above, which is what the plugin
@@ -6844,8 +7202,10 @@ export declare interface VoltroPlugin {
6844
7202
  * that wants the enablers declares `(store, ctx)` and reads
6845
7203
  * `ctx?.sql` / `ctx?.scheduleCoordinated(...)`; one that ignores `ctx`
6846
7204
  * keeps working unchanged.
7205
+ * A returned Promise is awaited before the app begins serving requests.
7206
+ * Store-dependent initialization may run here when activation happened first.
6847
7207
  */
6848
- readonly bindDataStore?: (store: DataStore, ctx?: PluginBindContext) => void;
7208
+ readonly bindDataStore?: (store: DataStore, ctx?: PluginBindContext) => void | Promise<void>;
6849
7209
  /**
6850
7210
  * Capabilities this plugin offers to OTHER framework machinery, by name.
6851
7211
  *
@@ -7055,6 +7415,26 @@ export declare interface VoltroPlugin {
7055
7415
  readonly onProcedureSurface?: (procedures: ReadonlyArray<ProcedureDescriptor>) => void;
7056
7416
  }
7057
7417
 
7418
+ /**
7419
+ * The plugins' cross-cutting errors, at the TYPE level.
7420
+ *
7421
+ * A plugin's `errorSchemas` reach every procedure's wire union at boot — a
7422
+ * runtime fact the type system cannot see. The codegen closes that gap: it
7423
+ * augments this interface in `rpcGroup.generated.ts` with one entry per
7424
+ * registered schema, so `defineExecutor` accepts a failure a plugin registers
7425
+ * exactly where the server will deliver it typed. Empty until the first
7426
+ * codegen run, which makes an unregistered plugin error a compile error rather
7427
+ * than a silent `InternalError`.
7428
+ *
7429
+ * declare module '@voltro/protocol' {
7430
+ * interface VoltroPluginErrors {
7431
+ * readonly '@acme/errors#NotFoundError': NotFoundError
7432
+ * }
7433
+ * }
7434
+ */
7435
+ export declare interface VoltroPluginErrors {
7436
+ }
7437
+
7058
7438
  /**
7059
7439
  * The table names THIS app declares — filled in by codegen, empty here.
7060
7440
  *
@@ -7168,7 +7548,7 @@ export declare const wireAudienceOf: (descriptor: {
7168
7548
  /**
7169
7549
  * The error union a procedure ACTUALLY puts on the wire — `descriptor.error`
7170
7550
  * plus everything the framework can produce for it before or around the
7171
- * executor.
7551
+ * executor, plus the plugins' cross-cutting errors (`extraErrors`).
7172
7552
  *
7173
7553
  * ── Why this is exported rather than inlined in the lifters ────────────────
7174
7554
  *
@@ -7198,13 +7578,34 @@ export declare const wireAudienceOf: (descriptor: {
7198
7578
  * `ScopeError` themselves — is what made the two sets agree by hand.
7199
7579
  *
7200
7580
  * One function now, so they cannot disagree again.
7581
+ *
7582
+ * The plugins' errors are part of that answer for the same reason. A plugin's
7583
+ * `errorSchemas` entry is on every lifted union, and the judgement above was
7584
+ * handed the union WITHOUT them — so an interceptor's `RateLimited`, or an
7585
+ * app-wide error a local plugin registers, decoded typed on a query and was
7586
+ * collapsed to `InternalError` on every mutation and action, on both boot
7587
+ * paths. Pass the same `extraErrors` the lifter receives.
7201
7588
  */
7202
7589
  export declare const wireErrorUnion: (descriptor: {
7203
7590
  readonly error: Schema.Schema.All;
7204
7591
  readonly guards?: DeclaredAccess | undefined;
7205
7592
  readonly requiresApproval?: AnyApprovalPolicy | undefined;
7206
7593
  readonly idempotencyScope?: MutationIdempotencyScope | undefined;
7207
- }, kind: "query" | "mutation" | "action" | "stream") => Schema.Schema.All;
7594
+ }, kind: "query" | "mutation" | "action" | "stream", extraErrors?: ExtraErrors) => Schema.Schema.All;
7595
+
7596
+ /** A procedure kind that has a wire error union. */
7597
+ export declare type WireProcedureKind = keyof typeof FRAMEWORK_WIRE_ERRORS;
7598
+
7599
+ /**
7600
+ * The error of a {@link ServerErrorEvent} with its content withheld: same
7601
+ * `name` (the class, or a typed failure's tag), the original stack FRAMES —
7602
+ * code locations, which carry no data — and a message saying where the full
7603
+ * error can be read by someone allowed to. No `cause`, no own properties.
7604
+ *
7605
+ * The stack's first line repeats the message, so it is rebuilt from the frames
7606
+ * rather than copied.
7607
+ */
7608
+ export declare const withheldServerError: (event: ServerErrorEvent) => Error;
7208
7609
 
7209
7610
  /**
7210
7611
  * Re-tag a procedure descriptor for the wire.
@@ -7698,6 +8099,147 @@ export declare const WorkflowQueuedControlTargetSchema: Schema.Struct<{
7698
8099
  requestId: typeof Schema.NonEmptyString;
7699
8100
  }>;
7700
8101
 
8102
+ export declare type WorkflowRestartCommand = typeof WorkflowRestartCommandSchema.Type;
8103
+
8104
+ export declare const WorkflowRestartCommandSchema: Schema.Struct<{
8105
+ selection: Schema.Union<[Schema.Struct<{
8106
+ kind: Schema.Literal<["failed-steps"]>;
8107
+ }>, Schema.Struct<{
8108
+ kind: Schema.Literal<["from-step"]>;
8109
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8110
+ }>]>;
8111
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8112
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8113
+ }>;
8114
+
8115
+ export declare type WorkflowRestartObservation = typeof WorkflowRestartObservationSchema.Type;
8116
+
8117
+ /** Pending is a durable reservation, not proof of reset or engine entry.
8118
+ * Entered confirms this command's generation; it does not claim run success. */
8119
+ export declare const WorkflowRestartObservationSchema: Schema.Union<[Schema.Struct<{
8120
+ kind: Schema.Literal<["not-found"]>;
8121
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8122
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8123
+ }>, Schema.Struct<{
8124
+ kind: Schema.Literal<["withdrawn"]>;
8125
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8126
+ withdrawnAt: Schema.filter<typeof Schema.Number>;
8127
+ selection: Schema.Union<[Schema.Struct<{
8128
+ kind: Schema.Literal<["failed-steps"]>;
8129
+ }>, Schema.Struct<{
8130
+ kind: Schema.Literal<["from-step"]>;
8131
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8132
+ }>]>;
8133
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8134
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8135
+ }>, Schema.Struct<{
8136
+ kind: Schema.Literal<["pending"]>;
8137
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8138
+ executionId: Schema.filter<typeof Schema.NonEmptyString>;
8139
+ originalGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8140
+ requestedAt: Schema.filter<typeof Schema.Number>;
8141
+ dispatchAttempts: Schema.refine<number, typeof Schema.NonNegative>;
8142
+ selection: Schema.Union<[Schema.Struct<{
8143
+ kind: Schema.Literal<["failed-steps"]>;
8144
+ }>, Schema.Struct<{
8145
+ kind: Schema.Literal<["from-step"]>;
8146
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8147
+ }>]>;
8148
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8149
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8150
+ }>, Schema.filter<Schema.Struct<{
8151
+ kind: Schema.Literal<["entered"]>;
8152
+ enteredGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8153
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8154
+ executionId: Schema.filter<typeof Schema.NonEmptyString>;
8155
+ originalGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8156
+ requestedAt: Schema.filter<typeof Schema.Number>;
8157
+ dispatchAttempts: Schema.refine<number, typeof Schema.NonNegative>;
8158
+ selection: Schema.Union<[Schema.Struct<{
8159
+ kind: Schema.Literal<["failed-steps"]>;
8160
+ }>, Schema.Struct<{
8161
+ kind: Schema.Literal<["from-step"]>;
8162
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8163
+ }>]>;
8164
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8165
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8166
+ }>>]>;
8167
+
8168
+ export declare type WorkflowRestartResult = typeof WorkflowRestartResultSchema.Type;
8169
+
8170
+ /** A refused reservation never reset the journal. Transport failures instead
8171
+ * require reading the original target; they are not proof of refusal. */
8172
+ export declare const WorkflowRestartResultSchema: Schema.Union<[Schema.Union<[Schema.Struct<{
8173
+ kind: Schema.Literal<["not-found"]>;
8174
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8175
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8176
+ }>, Schema.Struct<{
8177
+ kind: Schema.Literal<["withdrawn"]>;
8178
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8179
+ withdrawnAt: Schema.filter<typeof Schema.Number>;
8180
+ selection: Schema.Union<[Schema.Struct<{
8181
+ kind: Schema.Literal<["failed-steps"]>;
8182
+ }>, Schema.Struct<{
8183
+ kind: Schema.Literal<["from-step"]>;
8184
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8185
+ }>]>;
8186
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8187
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8188
+ }>, Schema.Struct<{
8189
+ kind: Schema.Literal<["pending"]>;
8190
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8191
+ executionId: Schema.filter<typeof Schema.NonEmptyString>;
8192
+ originalGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8193
+ requestedAt: Schema.filter<typeof Schema.Number>;
8194
+ dispatchAttempts: Schema.refine<number, typeof Schema.NonNegative>;
8195
+ selection: Schema.Union<[Schema.Struct<{
8196
+ kind: Schema.Literal<["failed-steps"]>;
8197
+ }>, Schema.Struct<{
8198
+ kind: Schema.Literal<["from-step"]>;
8199
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8200
+ }>]>;
8201
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8202
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8203
+ }>, Schema.filter<Schema.Struct<{
8204
+ kind: Schema.Literal<["entered"]>;
8205
+ enteredGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8206
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8207
+ executionId: Schema.filter<typeof Schema.NonEmptyString>;
8208
+ originalGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8209
+ requestedAt: Schema.filter<typeof Schema.Number>;
8210
+ dispatchAttempts: Schema.refine<number, typeof Schema.NonNegative>;
8211
+ selection: Schema.Union<[Schema.Struct<{
8212
+ kind: Schema.Literal<["failed-steps"]>;
8213
+ }>, Schema.Struct<{
8214
+ kind: Schema.Literal<["from-step"]>;
8215
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8216
+ }>]>;
8217
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8218
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8219
+ }>>]>, Schema.Struct<{
8220
+ kind: Schema.Literal<["not-reserved"]>;
8221
+ reason: Schema.Literal<["admission", "journal"]>;
8222
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8223
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8224
+ }>]>;
8225
+
8226
+ export declare type WorkflowRestartSelection = typeof WorkflowRestartSelectionSchema.Type;
8227
+
8228
+ export declare const WorkflowRestartSelectionSchema: Schema.Union<[Schema.Struct<{
8229
+ kind: Schema.Literal<["failed-steps"]>;
8230
+ }>, Schema.Struct<{
8231
+ kind: Schema.Literal<["from-step"]>;
8232
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8233
+ }>]>;
8234
+
8235
+ export declare type WorkflowRestartTarget = typeof WorkflowRestartTargetSchema.Type;
8236
+
8237
+ /** Persist before submitting; read this same target after an uncertain reply. */
8238
+ export declare const WorkflowRestartTargetSchema: Schema.Struct<{
8239
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8240
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8241
+ }>;
8242
+
7701
8243
  export declare const workflowResumeDescriptor: ActionProcedureDescriptor<"__voltro.workflow.resume", Schema.Struct<{
7702
8244
  workflowName: typeof Schema.String;
7703
8245
  executionId: typeof Schema.String;