@voltro/protocol 0.63.0 → 0.65.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 +157 -0
- package/dist/index.d.ts +430 -29
- package/dist/index.js +532 -473
- package/dist/rest.d.ts +28 -3
- package/dist/rest.js +13 -13
- package/dist/{scopes-B0kGvAnI.js → scopes-C86BDyNJ.js} +120 -77
- package/dist/{serverErrorBus-BeN9pOpY.js → serverErrorBus-QQgyq6fV.js} +15 -7
- package/dist/session.js +6 -6
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Context } from 'effect';
|
|
|
2
2
|
import { DataStore } from '@voltro/database';
|
|
3
3
|
import { Effect } from 'effect';
|
|
4
4
|
import { Layer } from 'effect';
|
|
5
|
+
import { Option } from 'effect';
|
|
5
6
|
import { Rpc } from '@effect/rpc';
|
|
6
7
|
import { RpcMiddleware } from '@effect/rpc';
|
|
7
8
|
import { Schema } from 'effect';
|
|
@@ -88,15 +89,15 @@ export declare interface AnyApprovalPolicy {
|
|
|
88
89
|
readonly reason?: string;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
/** A guard entry is a scope check, a relationship check,
|
|
92
|
-
* no-check-needed decision. */
|
|
93
|
-
export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec | OpenAccessSpec;
|
|
92
|
+
/** A guard entry is a scope check, a relationship check, an identity check, or
|
|
93
|
+
* a declared no-check-needed decision. */
|
|
94
|
+
export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec | AuthenticatedCheckSpec | OpenAccessSpec;
|
|
94
95
|
|
|
95
96
|
/** Any declared event, with the generics erased — for registries and audits. */
|
|
96
97
|
export declare type AnyEventDescriptor = EventDescriptor<string, Schema.Schema.Any, Schema.Schema.Any>;
|
|
97
98
|
|
|
98
|
-
/** A guard is
|
|
99
|
-
export declare type AnyGuardSpec<Input = unknown> = GuardSpec<Input> | PolicyGuardSpec<Input
|
|
99
|
+
/** A guard is a scope check, a relationship check, or an identity check. */
|
|
100
|
+
export declare type AnyGuardSpec<Input = unknown> = GuardSpec<Input> | PolicyGuardSpec<Input> | AuthenticatedGuardSpec;
|
|
100
101
|
|
|
101
102
|
export declare const APIKEY_ISSUE_ORG_SCOPE = "apikeys:issue:org";
|
|
102
103
|
|
|
@@ -447,6 +448,10 @@ declare const ApprovalUnavailable_base: Schema.TaggedErrorClass<ApprovalUnavaila
|
|
|
447
448
|
*/
|
|
448
449
|
export declare const assertAuthenticated: (subject: Subject, reason?: string) => void;
|
|
449
450
|
|
|
451
|
+
/** Attach a resolved decision to a definition — non-enumerable, like the
|
|
452
|
+
* definer's other metadata, so the engine never sees it as a field. */
|
|
453
|
+
export declare const attachWorkflowAccess: (definition: object, access: WorkflowAccess) => void;
|
|
454
|
+
|
|
450
455
|
/** Optional callback-route mount surface for strategies that handle
|
|
451
456
|
* OAuth callbacks. Apps wire these via the framework's HTTP router
|
|
452
457
|
* when the strategy declares `mountRoutes`. */
|
|
@@ -455,6 +460,36 @@ export declare interface AuthCallbackRouter {
|
|
|
455
460
|
readonly post: (path: string, handler: AuthRouteHandler) => void;
|
|
456
461
|
}
|
|
457
462
|
|
|
463
|
+
/**
|
|
464
|
+
* The runtime-erased form of `AuthenticatedGuardSpec` — "somebody must be
|
|
465
|
+
* there, and nothing narrower". See that type for why the vocabulary needed it.
|
|
466
|
+
*/
|
|
467
|
+
export declare interface AuthenticatedCheckSpec {
|
|
468
|
+
readonly authenticated: true;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* "The caller must have an identity" — and nothing narrower.
|
|
473
|
+
*
|
|
474
|
+
* The third case the vocabulary was missing, and the gap was not theoretical.
|
|
475
|
+
* An app whose authority lives per-team in its executors holds NO raw scopes on
|
|
476
|
+
* the identity, so `{ scope }` refuses every real user; a relationship guard
|
|
477
|
+
* cannot help when the resource does not exist yet (the first message of a new
|
|
478
|
+
* thread); and `openAccess` would be a lie whenever the auth fallback is a
|
|
479
|
+
* deliberate anonymous subject. The one thing left to decide — is anybody
|
|
480
|
+
* there — had no spelling, so apps wrote a resource policy whose entire content
|
|
481
|
+
* was "you are logged in".
|
|
482
|
+
*
|
|
483
|
+
* `checkGuards` has always distinguished this internally: its return type is
|
|
484
|
+
* `ScopeError | Unauthenticated | null`. This makes the distinction sayable.
|
|
485
|
+
*
|
|
486
|
+
* It is a real DECISION, so it satisfies `security.defaultDeny` — unlike
|
|
487
|
+
* `openAccess`, it also enforces something.
|
|
488
|
+
*/
|
|
489
|
+
export declare interface AuthenticatedGuardSpec {
|
|
490
|
+
readonly authenticated: true;
|
|
491
|
+
}
|
|
492
|
+
|
|
458
493
|
/**
|
|
459
494
|
* The framework's per-request auth middleware. Attach to an RpcGroup with
|
|
460
495
|
* `group.middleware(AuthMiddleware)` to require every call to flow through
|
|
@@ -587,6 +622,55 @@ export declare interface AuthStrategyWithCallback extends AuthStrategy {
|
|
|
587
622
|
readonly mountRoutes: (router: AuthCallbackRouter) => void;
|
|
588
623
|
}
|
|
589
624
|
|
|
625
|
+
export declare type AuthSubjectGuard = (subject: Subject, ctx: AuthSubjectGuardContext) => Promise<AuthSubjectGuardVerdict> | AuthSubjectGuardVerdict;
|
|
626
|
+
|
|
627
|
+
export declare interface AuthSubjectGuardContext {
|
|
628
|
+
/** `'request'` for a live call, `'workflow'` for a durable run being executed. */
|
|
629
|
+
readonly origin: 'request' | 'workflow';
|
|
630
|
+
/** The request's headers. EMPTY for `origin: 'workflow'`. */
|
|
631
|
+
readonly headers: Readonly<Record<string, string | undefined>>;
|
|
632
|
+
/** `undefined` for `origin: 'workflow'` and for transports with no connection. */
|
|
633
|
+
readonly clientId: number | undefined;
|
|
634
|
+
/** The app's boot store (see `AuthStrategyInput.store`), or `undefined` while
|
|
635
|
+
* it is still being built — a guard that needs it then fails CLOSED. */
|
|
636
|
+
readonly store: DataStore | undefined;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* A post-authentication VETO on a resolved subject — the framework-level form
|
|
641
|
+
* of `@voltro/plugin-auth`'s `subjectGuards`, and the reason it exists here too.
|
|
642
|
+
*
|
|
643
|
+
* The auth plugin runs its guards once, at LOGIN, before a session is issued —
|
|
644
|
+
* the right place when every session comes from its routes. It is the wrong
|
|
645
|
+
* place for an app whose subjects arrive through its own strategy chain (a JWKS
|
|
646
|
+
* IdP, an API-key table, a session it mints itself): those never pass through
|
|
647
|
+
* a login handler, so a `deactivationGuard()` wired onto the auth plugin vetoed
|
|
648
|
+
* nobody, and `users.deactivate` locked no one out. The guard has to sit where
|
|
649
|
+
* EVERY subject passes — after a strategy matches and authority is resolved,
|
|
650
|
+
* on the request path and on a durable workflow's resume alike.
|
|
651
|
+
*
|
|
652
|
+
* A guard receives the subject WITH its resolved scopes (so it may reason about
|
|
653
|
+
* them) and the same context `resolveScopes` gets: `origin` (a durable run has
|
|
654
|
+
* no request behind it), the headers, and the app's store for the row it needs
|
|
655
|
+
* to read. It returns a verdict; the first rejection wins and the request fails
|
|
656
|
+
* closed with `Unauthenticated` carrying the guard's `code` and `message`. It
|
|
657
|
+
* runs only for a MATCHED subject — an anonymous caller has no identity to veto.
|
|
658
|
+
*
|
|
659
|
+
* It is cached WITH the scope decision, under the same key and window: a veto
|
|
660
|
+
* is a fact about an identity, exactly like its roles, and a guard that reads a
|
|
661
|
+
* row per request would otherwise cost what `scopeCache` exists to avoid. The
|
|
662
|
+
* staleness is therefore the one number the docs already give (30s), and
|
|
663
|
+
* `invalidate(scopeCacheKey(subject))` from the mutation that deactivates makes
|
|
664
|
+
* it zero on that process — the same handle, the same call.
|
|
665
|
+
*/
|
|
666
|
+
export declare type AuthSubjectGuardVerdict = {
|
|
667
|
+
readonly ok: true;
|
|
668
|
+
} | {
|
|
669
|
+
readonly ok: false;
|
|
670
|
+
readonly code: string;
|
|
671
|
+
readonly message: string;
|
|
672
|
+
};
|
|
673
|
+
|
|
590
674
|
/**
|
|
591
675
|
* Decide what to do for an incoming (scope, key) in ONE atomic store call. On
|
|
592
676
|
* `fresh` the caller runs the handler then calls `finish`/`fail`; on
|
|
@@ -821,6 +905,13 @@ export declare const composeAuthStrategies: (strategies: ReadonlyArray<AuthStrat
|
|
|
821
905
|
removed: ReadonlyArray<string>;
|
|
822
906
|
granted: ReadonlyArray<string>;
|
|
823
907
|
}) => void;
|
|
908
|
+
/**
|
|
909
|
+
* Post-authentication vetoes on a MATCHED subject — see `AuthSubjectGuard`.
|
|
910
|
+
* Run after `resolveScopes` (so a guard sees the applied scopes), inside the
|
|
911
|
+
* same cache entry, first rejection wins → `Unauthenticated` with the
|
|
912
|
+
* guard's `code` and `message`.
|
|
913
|
+
*/
|
|
914
|
+
readonly subjectGuards?: ReadonlyArray<AuthSubjectGuard>;
|
|
824
915
|
/**
|
|
825
916
|
* Hands every strategy the app's store on `input.store`.
|
|
826
917
|
*
|
|
@@ -2132,6 +2223,15 @@ export declare const getPolicyGuardResolver: () => PolicyGuardResolver | undefin
|
|
|
2132
2223
|
/** The currently-registered resource-scope resolver, or `undefined`. */
|
|
2133
2224
|
export declare const getResourceScopeResolver: () => ResourceScopeResolver | undefined;
|
|
2134
2225
|
|
|
2226
|
+
/**
|
|
2227
|
+
* Read a definition's decision back. A value that never went through
|
|
2228
|
+
* `workflow()` — a bare `Workflow.make`, a test double — reports UNDECIDED and
|
|
2229
|
+
* not internal, so the boot gate judges it exactly like a definer call that
|
|
2230
|
+
* declared nothing: that is the fail-closed reading, and the only one under
|
|
2231
|
+
* which forgetting the definer cannot open a door.
|
|
2232
|
+
*/
|
|
2233
|
+
export declare const getWorkflowAccess: (definition: unknown) => WorkflowAccess;
|
|
2234
|
+
|
|
2135
2235
|
/** Ask for default-deny semantics on one `checkGuards` call. */
|
|
2136
2236
|
export declare interface GuardCheckOptions {
|
|
2137
2237
|
/** Refuse a procedure that declares no access decision at all. */
|
|
@@ -2295,6 +2395,9 @@ export declare interface HttpRequestContext {
|
|
|
2295
2395
|
*/
|
|
2296
2396
|
export declare type HttpRequestInterceptor = (next: () => Promise<HttpInterceptResponse | null>, ctx: HttpRequestContext) => Promise<HttpInterceptResponse | null>;
|
|
2297
2397
|
|
|
2398
|
+
/** Fire-and-forget observer of plugin HTTP route calls — see {@link PluginHttpRouteEvent}. */
|
|
2399
|
+
export declare type HttpRouteObserver = (event: PluginHttpRouteEvent) => void;
|
|
2400
|
+
|
|
2298
2401
|
export declare type IdempotencyOutcome = {
|
|
2299
2402
|
readonly kind: 'fresh';
|
|
2300
2403
|
} | {
|
|
@@ -2415,6 +2518,12 @@ export declare interface InsertTarget<Input = unknown, Row = unknown, Item = Rec
|
|
|
2415
2518
|
readonly shapeItem?: ((input: Input, optimisticId: string) => Item) | undefined;
|
|
2416
2519
|
}
|
|
2417
2520
|
|
|
2521
|
+
/** Runtime narrowing for the identity variant. */
|
|
2522
|
+
export declare const isAuthenticatedCheck: (g: AnyCheckSpec) => g is AuthenticatedCheckSpec;
|
|
2523
|
+
|
|
2524
|
+
/** Narrow a guard entry to the identity variant. */
|
|
2525
|
+
export declare const isAuthenticatedGuard: <I>(g: AnyGuardSpec<I>) => g is AuthenticatedGuardSpec;
|
|
2526
|
+
|
|
2418
2527
|
export declare const isEventDescriptor: (value: unknown) => value is AnyEventDescriptor;
|
|
2419
2528
|
|
|
2420
2529
|
/** True when every row in the set carries an `id`. The dispatcher uses
|
|
@@ -2503,6 +2612,24 @@ export declare const memoryIdempotencyStore: () => IdempotencyStore;
|
|
|
2503
2612
|
* and the call-time path cannot describe the same defect differently. */
|
|
2504
2613
|
export declare const missingAccessDecision: (procedure?: string) => ScopeError;
|
|
2505
2614
|
|
|
2615
|
+
/** The wait a conflicting replay is told to take. Long enough that a handler
|
|
2616
|
+
* in its last second finishes; short enough that a 10-second replay window
|
|
2617
|
+
* fits several tries. */
|
|
2618
|
+
export declare const MUTATION_IN_FLIGHT_RETRY_AFTER_MS = 1000;
|
|
2619
|
+
|
|
2620
|
+
export declare class MutationInFlight extends MutationInFlight_base {
|
|
2621
|
+
get message(): string;
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
declare const MutationInFlight_base: Schema.TaggedErrorClass<MutationInFlight, "MutationInFlight", {
|
|
2625
|
+
readonly _tag: Schema.tag<"MutationInFlight">;
|
|
2626
|
+
} & {
|
|
2627
|
+
traceId: typeof Schema.String;
|
|
2628
|
+
/** How long to wait before replaying the same key — the first call is still
|
|
2629
|
+
* running; a replay before it finishes conflicts again. */
|
|
2630
|
+
retryAfterMs: typeof Schema.Number;
|
|
2631
|
+
}>;
|
|
2632
|
+
|
|
2506
2633
|
export declare interface MutationProcedureDescriptor<Name extends string, Input extends Schema.Schema.Any, Output extends Schema.Schema.Any, Error extends Schema.Schema.All> {
|
|
2507
2634
|
readonly kind: 'mutation';
|
|
2508
2635
|
readonly name: Name;
|
|
@@ -2592,6 +2719,34 @@ export declare const normalizeDescriptor: (descriptor: ProcedureDescriptor) => C
|
|
|
2592
2719
|
*/
|
|
2593
2720
|
export declare const normalizeSource: (source: ReactivitySource | ReadonlyArray<ReactivitySource> | undefined) => string | ReadonlyArray<string> | undefined;
|
|
2594
2721
|
|
|
2722
|
+
/**
|
|
2723
|
+
* `.one()` matched a number of rows other than exactly one.
|
|
2724
|
+
*
|
|
2725
|
+
* `found` distinguishes the two failure modes without a second error type:
|
|
2726
|
+
* `0` is "the row you required is missing", `2` is "your filter is not as
|
|
2727
|
+
* unique as you assumed" (the terminal probes with LIMIT 2, so `2` means
|
|
2728
|
+
* "at least two" — it never counts the whole set just to report a number).
|
|
2729
|
+
*
|
|
2730
|
+
* It lives HERE, not in `@voltro/runtime`, and the reason is a promise the
|
|
2731
|
+
* framework was already making. The agent guide says "`NoRowFound` is
|
|
2732
|
+
* declarable in the descriptor's `error:` union" — and a descriptor is loaded
|
|
2733
|
+
* by the BROWSER, while `@voltro/runtime` is on the browser-safety guard's
|
|
2734
|
+
* server-only list. So following that sentence produced a refused boot. The
|
|
2735
|
+
* class is browser-safe by construction (a `Schema.TaggedError` over two
|
|
2736
|
+
* scalars), so the package it sat in was the only thing making the guidance
|
|
2737
|
+
* false. `@voltro/runtime` re-exports it, so nothing that imported it moves.
|
|
2738
|
+
*/
|
|
2739
|
+
export declare class NoRowFound extends NoRowFound_base {
|
|
2740
|
+
get message(): string;
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
declare const NoRowFound_base: Schema.TaggedErrorClass<NoRowFound, "NoRowFound", {
|
|
2744
|
+
readonly _tag: Schema.tag<"NoRowFound">;
|
|
2745
|
+
} & {
|
|
2746
|
+
table: typeof Schema.String;
|
|
2747
|
+
found: typeof Schema.Number;
|
|
2748
|
+
}>;
|
|
2749
|
+
|
|
2595
2750
|
/**
|
|
2596
2751
|
* What a plugin contributes to the OTel layer via `contributeObservability`.
|
|
2597
2752
|
* The OTel types are intentionally `unknown` so this browser-safe protocol
|
|
@@ -2704,20 +2859,6 @@ export declare type PluginActivateHook = (ctx: PluginLifecycleContext) => Effect
|
|
|
2704
2859
|
*/
|
|
2705
2860
|
export declare const pluginAlias: (pluginName: string) => string;
|
|
2706
2861
|
|
|
2707
|
-
/**
|
|
2708
|
-
* Server-only context delivered to `bindDataStore`, ALONGSIDE the store,
|
|
2709
|
-
* AFTER the framework has opened its store + pool. Gives a plugin the
|
|
2710
|
-
* framework's ALREADY-OPEN handles so it never rebuilds a pg pool from
|
|
2711
|
-
* raw env (the anti-pattern `@voltro/plugin-ratelimit` used to hand-roll)
|
|
2712
|
-
* and never runs an un-coordinated per-replica `setInterval` sweep (what
|
|
2713
|
-
* presence + governance used to do).
|
|
2714
|
-
*
|
|
2715
|
-
* These fields are SERVER-only — they are TYPES only in this browser-safe
|
|
2716
|
-
* protocol package (the `DataStore` / `SqlClient` imports are erased), and
|
|
2717
|
-
* the live values exist only inside the serve pipeline that calls
|
|
2718
|
-
* `bindDataStore`. Nothing in the browser-loaded descriptor/schema graph
|
|
2719
|
-
* touches them.
|
|
2720
|
-
*/
|
|
2721
2862
|
export declare interface PluginBindContext {
|
|
2722
2863
|
/**
|
|
2723
2864
|
* This process's replica identity — the same value the event bus stamps as
|
|
@@ -2754,6 +2895,20 @@ export declare interface PluginBindContext {
|
|
|
2754
2895
|
readonly publish: (channel: string, payload: string) => unknown;
|
|
2755
2896
|
readonly subscribe: (channel: string, handler: (payload: string) => void) => unknown;
|
|
2756
2897
|
};
|
|
2898
|
+
/**
|
|
2899
|
+
* The framework's cache — the SAME backend `ctx.cache` and query snapshots
|
|
2900
|
+
* use (`CACHE_BACKEND`: in-process memory, or Redis shared across replicas).
|
|
2901
|
+
* A plugin that memoises reference data (a board's configuration, a field
|
|
2902
|
+
* catalogue) reads and writes it here instead of asking the app for a
|
|
2903
|
+
* hand-built store object; the keys a plugin writes are its own to namespace.
|
|
2904
|
+
*
|
|
2905
|
+
* A handle rather than a value: `voltro dev` builds the cache after plugins
|
|
2906
|
+
* are bound and `voltro serve` before, so the two boot paths hand over the
|
|
2907
|
+
* same object whose methods resolve the live cache at call time. Before it
|
|
2908
|
+
* exists a read misses and a write is dropped — best-effort, never a boot
|
|
2909
|
+
* ordering a plugin has to know about.
|
|
2910
|
+
*/
|
|
2911
|
+
readonly cache?: PluginCacheHandle;
|
|
2757
2912
|
/**
|
|
2758
2913
|
* The framework's already-open `SqlClient` (from `@effect/sql`) — the
|
|
2759
2914
|
* SAME pool the app's store uses. A plugin runs raw SQL through this
|
|
@@ -2805,6 +2960,28 @@ export declare interface PluginBindContext {
|
|
|
2805
2960
|
readonly claimChange: (scope: string, key: string) => Promise<boolean>;
|
|
2806
2961
|
}
|
|
2807
2962
|
|
|
2963
|
+
/**
|
|
2964
|
+
* Server-only context delivered to `bindDataStore`, ALONGSIDE the store,
|
|
2965
|
+
* AFTER the framework has opened its store + pool. Gives a plugin the
|
|
2966
|
+
* framework's ALREADY-OPEN handles so it never rebuilds a pg pool from
|
|
2967
|
+
* raw env (the anti-pattern `@voltro/plugin-ratelimit` used to hand-roll)
|
|
2968
|
+
* and never runs an un-coordinated per-replica `setInterval` sweep (what
|
|
2969
|
+
* presence + governance used to do).
|
|
2970
|
+
*
|
|
2971
|
+
* These fields are SERVER-only — they are TYPES only in this browser-safe
|
|
2972
|
+
* protocol package (the `DataStore` / `SqlClient` imports are erased), and
|
|
2973
|
+
* the live values exist only inside the serve pipeline that calls
|
|
2974
|
+
* `bindDataStore`. Nothing in the browser-loaded descriptor/schema graph
|
|
2975
|
+
* touches them.
|
|
2976
|
+
*/
|
|
2977
|
+
/** The slice of the framework cache handed to a plugin at bind time. */
|
|
2978
|
+
export declare interface PluginCacheHandle {
|
|
2979
|
+
readonly get: (key: string) => Effect.Effect<Option.Option<unknown>, unknown>;
|
|
2980
|
+
readonly set: (key: string, value: unknown, options?: {
|
|
2981
|
+
readonly ttlMs?: number;
|
|
2982
|
+
}) => Effect.Effect<void, unknown>;
|
|
2983
|
+
}
|
|
2984
|
+
|
|
2808
2985
|
/**
|
|
2809
2986
|
* Post-commit change event delivered to a plugin's `onChangeEvent` tap.
|
|
2810
2987
|
* Minimal, browser-safe shape (protocol must not depend on the database
|
|
@@ -3113,6 +3290,25 @@ export declare interface PluginHttpRouteByteStream {
|
|
|
3113
3290
|
readonly contentDisposition?: string;
|
|
3114
3291
|
}
|
|
3115
3292
|
|
|
3293
|
+
/**
|
|
3294
|
+
* One plugin HTTP route call, after it answered — what an audit trail records.
|
|
3295
|
+
*
|
|
3296
|
+
* `onHttpRequest` runs BEFORE auth and before routing and sees no status, so a
|
|
3297
|
+
* login, a logout or an avatar fetch served by a plugin's `httpRoutes` left no
|
|
3298
|
+
* trace in a trail that recorded every rpc. This is the other end of the same
|
|
3299
|
+
* pipeline: the method, the path, the status and how long it took, once the
|
|
3300
|
+
* route has answered. No subject — a plugin HTTP route is not part of the rpc
|
|
3301
|
+
* pipeline and nothing upstream authenticated its caller; a route that resolves
|
|
3302
|
+
* one itself is the only party that knows, and can put it in its own log.
|
|
3303
|
+
*/
|
|
3304
|
+
export declare interface PluginHttpRouteEvent {
|
|
3305
|
+
readonly method: string;
|
|
3306
|
+
/** Path WITHOUT query string. */
|
|
3307
|
+
readonly path: string;
|
|
3308
|
+
readonly status: number;
|
|
3309
|
+
readonly durationMs: number;
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3116
3312
|
export declare interface PluginHttpRouteRequest {
|
|
3117
3313
|
readonly method: string;
|
|
3118
3314
|
/** Path WITHOUT query string. */
|
|
@@ -3220,7 +3416,10 @@ export declare interface PluginHttpRouteResult {
|
|
|
3220
3416
|
readonly status: number;
|
|
3221
3417
|
readonly body?: string | Uint8Array;
|
|
3222
3418
|
readonly contentType?: string;
|
|
3223
|
-
|
|
3419
|
+
/** Response headers. A header that repeats — two `Set-Cookie` lines, a
|
|
3420
|
+
* session and a CSRF cookie from one login — is an ARRAY; `set-cookie`
|
|
3421
|
+
* arrays become separate lines, every other array joins with `, `. */
|
|
3422
|
+
readonly headers?: Record<string, string | ReadonlyArray<string>>;
|
|
3224
3423
|
/** Stream the response (SSE) instead of sending `body`. See
|
|
3225
3424
|
* {@link PluginHttpRouteStream}. */
|
|
3226
3425
|
readonly stream?: PluginHttpRouteStream;
|
|
@@ -3444,7 +3643,7 @@ export declare type PluginMigrationSqlClient = SqlClient.SqlClient;
|
|
|
3444
3643
|
* boot log + `/_voltro/inspect/plugins` so operators audit what each
|
|
3445
3644
|
* plugin asks for before installing.
|
|
3446
3645
|
*/
|
|
3447
|
-
export declare type PluginPermission = 'rpc:intercept:mutation' | 'rpc:intercept:query' | 'rpc:intercept:action' | '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}`;
|
|
3646
|
+
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}`;
|
|
3448
3647
|
|
|
3449
3648
|
/**
|
|
3450
3649
|
* Per-call context handed to a plugin-contributed rpc route's
|
|
@@ -3458,6 +3657,15 @@ export declare interface PluginRouteContext {
|
|
|
3458
3657
|
};
|
|
3459
3658
|
}
|
|
3460
3659
|
|
|
3660
|
+
/**
|
|
3661
|
+
* The kinds a plugin can CONTRIBUTE a route as. A plugin declares procedures,
|
|
3662
|
+
* never workflows (a workflow is a `*.workflow.tsx` pair the app authors and the
|
|
3663
|
+
* engine runs), so this is the interceptor union minus `'workflow'` — kept as
|
|
3664
|
+
* its own name so a route's `kind` cannot select a runtime path that does not
|
|
3665
|
+
* exist for it.
|
|
3666
|
+
*/
|
|
3667
|
+
export declare type PluginRouteKind = 'mutation' | 'query' | 'action';
|
|
3668
|
+
|
|
3461
3669
|
/**
|
|
3462
3670
|
* Client-facing declaration of a plugin RPC route's descriptor, so the codegen
|
|
3463
3671
|
* can emit it into the generated client rpc group (`appGroup` + `appDescriptors`)
|
|
@@ -3474,7 +3682,7 @@ export declare interface PluginRouteContext {
|
|
|
3474
3682
|
*/
|
|
3475
3683
|
export declare interface PluginRpcClientDescriptor {
|
|
3476
3684
|
readonly tag: string;
|
|
3477
|
-
readonly kind:
|
|
3685
|
+
readonly kind: PluginRouteKind;
|
|
3478
3686
|
readonly import: {
|
|
3479
3687
|
readonly module: string;
|
|
3480
3688
|
readonly name: string;
|
|
@@ -3497,7 +3705,7 @@ export declare interface PluginRpcClientDescriptor {
|
|
|
3497
3705
|
*/
|
|
3498
3706
|
export declare interface PluginRpcRoute {
|
|
3499
3707
|
/** 'mutation' | 'query' | 'action' — determines the runtime path. */
|
|
3500
|
-
readonly kind:
|
|
3708
|
+
readonly kind: PluginRouteKind;
|
|
3501
3709
|
/** RPC tag relative to the plugin (e.g. `'list'`, `'admin.events'`). */
|
|
3502
3710
|
readonly name: string;
|
|
3503
3711
|
/** Optional human-readable description; surfaced in the dashboard. */
|
|
@@ -4042,6 +4250,14 @@ export declare const requireScope: (subject: Subject, scope: string) => Effect.E
|
|
|
4042
4250
|
/** Drop every registration. Tests only — production registers once at module load. */
|
|
4043
4251
|
export declare const resetPluginAliases: () => void;
|
|
4044
4252
|
|
|
4253
|
+
/**
|
|
4254
|
+
* Validate an author's access options and normalise them into the array every
|
|
4255
|
+
* enforcement path reads. Throws at DEFINITION time — module load, which is
|
|
4256
|
+
* boot — with the same wording the event and procedure definers use, so the
|
|
4257
|
+
* three refusals read as one rule.
|
|
4258
|
+
*/
|
|
4259
|
+
export declare const resolveWorkflowAccess: (name: string, options: WorkflowAccessOptions<never>) => WorkflowAccess;
|
|
4260
|
+
|
|
4045
4261
|
/** The question the runtime asks a registered resource-scope resolver. */
|
|
4046
4262
|
export declare interface ResourceScopeRequest {
|
|
4047
4263
|
/** The authenticated caller. */
|
|
@@ -4219,7 +4435,7 @@ export declare type RpcInterceptor = (next: Effect.Effect<unknown, unknown>, con
|
|
|
4219
4435
|
export declare interface RpcInterceptorContext {
|
|
4220
4436
|
/** Procedure tag, e.g. `todos.create`. */
|
|
4221
4437
|
readonly tag: string;
|
|
4222
|
-
/** Discriminator — `'mutation' | 'query' | 'action'`. */
|
|
4438
|
+
/** Discriminator — `'mutation' | 'query' | 'action' | 'workflow'`. */
|
|
4223
4439
|
readonly kind: RpcKind;
|
|
4224
4440
|
/** The validated input the executor is about to (or already did) receive. */
|
|
4225
4441
|
readonly input: unknown;
|
|
@@ -4236,10 +4452,11 @@ export declare interface RpcInterceptorContext {
|
|
|
4236
4452
|
/**
|
|
4237
4453
|
* Which kind of rpc the interceptor is wrapping. The runtime passes
|
|
4238
4454
|
* `kind: 'mutation'` to interceptors registered as `interceptMutation`,
|
|
4239
|
-
* `kind: 'query'` to `interceptQuery`,
|
|
4240
|
-
* `interceptAction
|
|
4455
|
+
* `kind: 'query'` to `interceptQuery`, `kind: 'action'` to
|
|
4456
|
+
* `interceptAction`, and `kind: 'workflow'` to `interceptWorkflow` — one
|
|
4457
|
+
* plugin can implement any combination.
|
|
4241
4458
|
*/
|
|
4242
|
-
export declare type RpcKind = 'mutation' | 'query' | 'action';
|
|
4459
|
+
export declare type RpcKind = 'mutation' | 'query' | 'action' | 'workflow';
|
|
4243
4460
|
|
|
4244
4461
|
/**
|
|
4245
4462
|
* Per-firing context passed to schedule-fire interceptors. Shape
|
|
@@ -4351,6 +4568,19 @@ export declare type ScopeDecision =
|
|
|
4351
4568
|
| {
|
|
4352
4569
|
readonly kind: 'unavailable';
|
|
4353
4570
|
readonly reason: string;
|
|
4571
|
+
}
|
|
4572
|
+
/**
|
|
4573
|
+
* A subject guard REFUSED this identity (see `AuthSubjectGuard`). Not a
|
|
4574
|
+
* resolver's answer — a resolver returns one of the three above — but the
|
|
4575
|
+
* composer's, produced after the guards ran, and cached like a grant: a veto
|
|
4576
|
+
* is a durable fact about the identity until whatever changed it invalidates
|
|
4577
|
+
* the key. `applyScopeDecision` THROWS on it, so a caller that forgot the
|
|
4578
|
+
* explicit branch fails closed rather than applying "no scopes" and carrying on.
|
|
4579
|
+
*/
|
|
4580
|
+
| {
|
|
4581
|
+
readonly kind: 'vetoed';
|
|
4582
|
+
readonly code: string;
|
|
4583
|
+
readonly message: string;
|
|
4354
4584
|
};
|
|
4355
4585
|
|
|
4356
4586
|
export declare class ScopeError extends ScopeError_base {
|
|
@@ -4466,7 +4696,12 @@ declare const StoreOperationFailed_base: Schema.TaggedErrorClass<StoreOperationF
|
|
|
4466
4696
|
operation: typeof Schema.String;
|
|
4467
4697
|
/** The table the op targeted. */
|
|
4468
4698
|
table: typeof Schema.String;
|
|
4469
|
-
/**
|
|
4699
|
+
/**
|
|
4700
|
+
* A BOUNDED description: the constraint kind and its constraint/column
|
|
4701
|
+
* NAME, or the driver's code. Never the driver's sentence and never a
|
|
4702
|
+
* stack — `wireSafeCause` builds it, and the reasoning is there. The full
|
|
4703
|
+
* detail is logged server-side instead.
|
|
4704
|
+
*/
|
|
4470
4705
|
cause: typeof Schema.String;
|
|
4471
4706
|
}>;
|
|
4472
4707
|
|
|
@@ -5380,6 +5615,22 @@ export declare interface VoltroPlugin {
|
|
|
5380
5615
|
* tenant-scoped IO quotas).
|
|
5381
5616
|
*/
|
|
5382
5617
|
readonly interceptAction?: RpcInterceptor;
|
|
5618
|
+
/**
|
|
5619
|
+
* Wraps every WORKFLOW START call — the unary rpc a `*.workflow.tsx`
|
|
5620
|
+
* becomes on the wire. Same composition semantics as `interceptMutation`.
|
|
5621
|
+
*
|
|
5622
|
+
* A start is a write-class call: it records a run row, it may cost money
|
|
5623
|
+
* (a workflow that calls a model or a paid API), and it runs as the caller's
|
|
5624
|
+
* subject. So the plugins that wrap mutations — audit, rate limiting,
|
|
5625
|
+
* entitlement, rbac's scope publication — want to wrap this too, and before
|
|
5626
|
+
* this hook existed none of them did: the start handler was bound outside
|
|
5627
|
+
* every interceptor chain, so an audit trail that covered every mutation
|
|
5628
|
+
* recorded no workflow start, and a `guards:` on a workflow could not see
|
|
5629
|
+
* the scopes rbac publishes inside the chain. Declaring `interceptAction`
|
|
5630
|
+
* without this hook leaves that gap open for your plugin; the first-party
|
|
5631
|
+
* plugins carry a parity test that fails when one does.
|
|
5632
|
+
*/
|
|
5633
|
+
readonly interceptWorkflow?: RpcInterceptor;
|
|
5383
5634
|
/**
|
|
5384
5635
|
* Effect Layer that contributes services into the per-request
|
|
5385
5636
|
* context. Anything declared here is yieldable from any handler in
|
|
@@ -5515,6 +5766,14 @@ export declare interface VoltroPlugin {
|
|
|
5515
5766
|
* the hook is declared without the matching permission.
|
|
5516
5767
|
*/
|
|
5517
5768
|
readonly onHttpRequest?: HttpRequestInterceptor;
|
|
5769
|
+
/**
|
|
5770
|
+
* Post-response observer of every plugin HTTP route call — method, path,
|
|
5771
|
+
* status, duration. The audit plugin's way of recording a login or an avatar
|
|
5772
|
+
* fetch, which no rpc interceptor ever sees. Fire-and-forget: it cannot
|
|
5773
|
+
* change the response and a throw is swallowed. Same `'http:intercept'`
|
|
5774
|
+
* permission as `onHttpRequest` — it is the same pipeline, the other end.
|
|
5775
|
+
*/
|
|
5776
|
+
readonly onHttpRoute?: HttpRouteObserver;
|
|
5518
5777
|
/**
|
|
5519
5778
|
* Dashboard mount contributions — remote-loaded ESM modules the
|
|
5520
5779
|
* dashboard host renders alongside framework pages. See
|
|
@@ -5735,6 +5994,61 @@ export declare const withRpcTag: <D extends {
|
|
|
5735
5994
|
readonly name: T;
|
|
5736
5995
|
};
|
|
5737
5996
|
|
|
5997
|
+
/** The resolved decision the runtime reads. */
|
|
5998
|
+
export declare interface WorkflowAccess {
|
|
5999
|
+
/** The author's guards, or the erased `openAccess` entry — never both. */
|
|
6000
|
+
readonly guards: DeclaredAccess<unknown> | undefined;
|
|
6001
|
+
readonly openAccess: string | undefined;
|
|
6002
|
+
readonly internal: boolean;
|
|
6003
|
+
}
|
|
6004
|
+
|
|
6005
|
+
/**
|
|
6006
|
+
* The definition as the boot gate, the codegen and the dispatch spine read it:
|
|
6007
|
+
* the structural `{ name, internal, guards, openAccess }` every other kind
|
|
6008
|
+
* presents, so `isWireReachable` and `hasAccessDecision` apply unchanged and a
|
|
6009
|
+
* workflow rides the same walk as the five procedure kinds.
|
|
6010
|
+
*/
|
|
6011
|
+
export declare const workflowAccessDescriptor: (definition: {
|
|
6012
|
+
readonly name: string;
|
|
6013
|
+
}) => {
|
|
6014
|
+
readonly name: string;
|
|
6015
|
+
readonly internal: boolean;
|
|
6016
|
+
readonly guards: DeclaredAccess<unknown> | undefined;
|
|
6017
|
+
readonly openAccess: string | undefined;
|
|
6018
|
+
};
|
|
6019
|
+
|
|
6020
|
+
/** What an author writes on `workflow({ … })`. */
|
|
6021
|
+
export declare interface WorkflowAccessOptions<Payload = unknown> {
|
|
6022
|
+
/**
|
|
6023
|
+
* WHO MAY START this workflow over the wire — the same vocabulary as a
|
|
6024
|
+
* mutation's `guards:`. A resource-scoped guard reads its id from the start
|
|
6025
|
+
* payload: `{ action: 'export', resourceType: 'team', resource: (p) => p.teamId }`.
|
|
6026
|
+
*
|
|
6027
|
+
* Checked ONCE, at the start call, before the run row is written or the
|
|
6028
|
+
* engine is asked for anything. The run itself then executes as the subject
|
|
6029
|
+
* that started it (`ctx.subject` inside the workflow) — the guard decides
|
|
6030
|
+
* whether it may begin, not what it may read; row filters do that.
|
|
6031
|
+
*/
|
|
6032
|
+
readonly guards?: Guards<Payload>;
|
|
6033
|
+
/**
|
|
6034
|
+
* Anyone who can open the socket may start this workflow — and WHY. A
|
|
6035
|
+
* reason, never a boolean; an empty string is refused at definition time.
|
|
6036
|
+
* Read by the boot gate and printed by `voltro doctor` beside the tag.
|
|
6037
|
+
*/
|
|
6038
|
+
readonly openAccess?: string;
|
|
6039
|
+
/**
|
|
6040
|
+
* No start rpc at all. The workflow is still discovered, registered with the
|
|
6041
|
+
* engine and startable from server code — `ctx.workflows.start`, a
|
|
6042
|
+
* `schedule:` block, an event trigger, a parent workflow — but the codegen
|
|
6043
|
+
* emits no client entry and neither boot path binds a handler for it.
|
|
6044
|
+
* Cannot be combined with `openAccess` (there is no wire to decide about).
|
|
6045
|
+
*/
|
|
6046
|
+
readonly internal?: boolean;
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
/** The non-enumerable property a `workflow()` definition carries its decision under. */
|
|
6050
|
+
export declare const WorkflowAccessProperty: unique symbol;
|
|
6051
|
+
|
|
5738
6052
|
export declare const workflowCancelDescriptor: ActionProcedureDescriptor<"__voltro.workflow.cancel", Schema.Struct<{
|
|
5739
6053
|
workflowName: typeof Schema.String;
|
|
5740
6054
|
executionId: typeof Schema.String;
|
|
@@ -5880,7 +6194,14 @@ export declare const WorkflowRunHandleSchema: Schema.Struct<{
|
|
|
5880
6194
|
* `skipped` — a `singleton: { mode: 'skip' }` key was held; `executionId`
|
|
5881
6195
|
* names the run that holds it.
|
|
5882
6196
|
*/
|
|
5883
|
-
|
|
6197
|
+
/**
|
|
6198
|
+
* `replayed` — the idempotency key was already SPENT: a run under it had
|
|
6199
|
+
* finished, so nothing started and the handle addresses that old run. It
|
|
6200
|
+
* used to come back `running` with the old run's id, indistinguishable from
|
|
6201
|
+
* a fresh start; a schedule keyed per day ran once and reported forty-seven
|
|
6202
|
+
* "starts" that were replays. See `workflow()`'s `idempotencyKey` doc.
|
|
6203
|
+
*/
|
|
6204
|
+
status: Schema.Literal<["running", "queued", "dropped", "skipped", "replayed"]>;
|
|
5884
6205
|
deferral: Schema.optional<Schema.Struct<{
|
|
5885
6206
|
/** `debounce` | `batch` | `throttle` | `concurrency` | `paused` |
|
|
5886
6207
|
* `rateLimit` | `singleton`. */
|
|
@@ -6094,6 +6415,71 @@ export declare interface WorkflowStepContext {
|
|
|
6094
6415
|
|
|
6095
6416
|
export declare type WorkflowStepInterceptor = <Success, ErrorE, R>(next: Effect.Effect<Success, ErrorE, R>, ctx: WorkflowStepContext) => Effect.Effect<Success, ErrorE, R>;
|
|
6096
6417
|
|
|
6418
|
+
/**
|
|
6419
|
+
* The fifth lifter — a `workflow()` definition to the unary START rpc it
|
|
6420
|
+
* answers to on the wire. Browser-safe, like the four procedure lifters, and
|
|
6421
|
+
* for the same reason: the generated client group and both boot paths call
|
|
6422
|
+
* THIS, so the three cannot disagree about the payload strictness or the error
|
|
6423
|
+
* union. It used to be spelled three times (a runtime helper for serve, an
|
|
6424
|
+
* inline `Rpc.make` in dev without `strictInput`, another inline one in the
|
|
6425
|
+
* codegen without the guard errors), and the three had already drifted.
|
|
6426
|
+
*
|
|
6427
|
+
* Reads the access decision off the definition: an enforced guard widens the
|
|
6428
|
+
* error union with `ScopeError` / `Unauthenticated`, exactly as it does for a
|
|
6429
|
+
* mutation, and labels the strict-input refusal as a guarded procedure's.
|
|
6430
|
+
*/
|
|
6431
|
+
export declare const workflowToRpc: (workflow: {
|
|
6432
|
+
readonly name: string;
|
|
6433
|
+
readonly payloadSchema: Schema.Schema.Any;
|
|
6434
|
+
readonly errorSchema: Schema.Schema.All;
|
|
6435
|
+
}, extraErrors?: ExtraErrors) => Rpc.Rpc<string, Schema.Schema.Any, Schema.Struct<{
|
|
6436
|
+
/** Stable handle callers pass to `useWorkflowRun(...)`. The durable execution
|
|
6437
|
+
* id for a started run; the pending-intent id for a queued one. */
|
|
6438
|
+
id: typeof Schema.String;
|
|
6439
|
+
/** Workflow tag, e.g. `notes.summarise`. */
|
|
6440
|
+
workflowName: typeof Schema.String;
|
|
6441
|
+
/**
|
|
6442
|
+
* Deterministic durable id derived from workflow name + idempotency key.
|
|
6443
|
+
*
|
|
6444
|
+
* NULL when flow control deferred, dropped, or skipped the start — because
|
|
6445
|
+
* there is no execution yet, and there may never be one. It used to be a
|
|
6446
|
+
* required string, and keeping it that way would have meant inventing a value:
|
|
6447
|
+
* either an empty string or the id the run WOULD have had. Both are polls that
|
|
6448
|
+
* return `status: 'unknown'` forever, which is the failure mode this whole
|
|
6449
|
+
* feature set exists to remove.
|
|
6450
|
+
*
|
|
6451
|
+
* For a `skipped` singleton it is the INCUMBENT's execution id — a real,
|
|
6452
|
+
* pollable run, which is the entire point of `mode: 'skip'`.
|
|
6453
|
+
*/
|
|
6454
|
+
executionId: Schema.NullOr<typeof Schema.String>;
|
|
6455
|
+
/**
|
|
6456
|
+
* `running` — the engine has it; starting is fire-and-return.
|
|
6457
|
+
* `queued` — flow control deferred it; see `deferral.dueAt`.
|
|
6458
|
+
* `dropped` — over a `rateLimit` cap. It will NOT run.
|
|
6459
|
+
* `skipped` — a `singleton: { mode: 'skip' }` key was held; `executionId`
|
|
6460
|
+
* names the run that holds it.
|
|
6461
|
+
*/
|
|
6462
|
+
/**
|
|
6463
|
+
* `replayed` — the idempotency key was already SPENT: a run under it had
|
|
6464
|
+
* finished, so nothing started and the handle addresses that old run. It
|
|
6465
|
+
* used to come back `running` with the old run's id, indistinguishable from
|
|
6466
|
+
* a fresh start; a schedule keyed per day ran once and reported forty-seven
|
|
6467
|
+
* "starts" that were replays. See `workflow()`'s `idempotencyKey` doc.
|
|
6468
|
+
*/
|
|
6469
|
+
status: Schema.Literal<["running", "queued", "dropped", "skipped", "replayed"]>;
|
|
6470
|
+
deferral: Schema.optional<Schema.Struct<{
|
|
6471
|
+
/** `debounce` | `batch` | `throttle` | `concurrency` | `paused` |
|
|
6472
|
+
* `rateLimit` | `singleton`. */
|
|
6473
|
+
mode: typeof Schema.String;
|
|
6474
|
+
/** Epoch ms this may first be admitted. Null for a drop or a skip. */
|
|
6475
|
+
dueAt: Schema.NullOr<typeof Schema.Number>;
|
|
6476
|
+
/** How long until the rate window reopens. Null unless dropped. */
|
|
6477
|
+
retryAfterMs: Schema.NullOr<typeof Schema.Number>;
|
|
6478
|
+
/** The `_voltro_workflow_pending` row holding this start. Null unless queued. */
|
|
6479
|
+
intentId: Schema.NullOr<typeof Schema.String>;
|
|
6480
|
+
}>>;
|
|
6481
|
+
}>, Schema.Schema.All, never>;
|
|
6482
|
+
|
|
6097
6483
|
export declare const workflowUpdateDescriptor: ActionProcedureDescriptor<"__voltro.workflow.update", Schema.Struct<{
|
|
6098
6484
|
id: typeof Schema.String;
|
|
6099
6485
|
updateName: typeof Schema.String;
|
|
@@ -6122,6 +6508,21 @@ export declare const WorkflowUpdateResultSchema: Schema.Struct<{
|
|
|
6122
6508
|
result: typeof Schema.Unknown;
|
|
6123
6509
|
}>;
|
|
6124
6510
|
|
|
6511
|
+
/**
|
|
6512
|
+
* The error union a WORKFLOW START puts on the wire — the workflow's own
|
|
6513
|
+
* `error:` plus what the framework can refuse the start with before the engine
|
|
6514
|
+
* is asked: the guard refusals (when it declares an enforced guard) and any
|
|
6515
|
+
* plugin error the `interceptWorkflow` chain may fail with. Read by the two
|
|
6516
|
+
* boot paths (the rpc group AND `bindMutation`'s declared set) and by
|
|
6517
|
+
* `workflowToRpc` for the generated client — one function, so a `ScopeError`
|
|
6518
|
+
* on a start decodes as the typed failure it is rather than crossing as the
|
|
6519
|
+
* `ExitEncoded` defect tree a mutation used to ship for the same reason.
|
|
6520
|
+
*/
|
|
6521
|
+
export declare const workflowWireErrorUnion: (workflow: {
|
|
6522
|
+
readonly errorSchema: Schema.Schema.All;
|
|
6523
|
+
readonly guards?: DeclaredAccess | undefined;
|
|
6524
|
+
}, extraErrors?: ExtraErrors) => Schema.Schema.All;
|
|
6525
|
+
|
|
6125
6526
|
/**
|
|
6126
6527
|
* Scope for a WS-rpc MUTATION. Unlike the REST scope (tenant + method + path),
|
|
6127
6528
|
* this includes the acting SUBJECT id: a WS app is often single-tenant with many
|