@voltro/protocol 0.79.1 → 0.81.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 +169 -0
- package/dist/apikey.d.ts +8 -0
- package/dist/index.d.ts +394 -14
- package/dist/index.js +601 -556
- package/dist/jwt.d.ts +8 -0
- package/dist/rest.d.ts +84 -16
- package/dist/rest.js +228 -218
- package/dist/{restFormat-ChSVyufA.js → restFormat-CzdGVkuB.js} +72 -60
- package/dist/{scopes-BbIyym8y.js → scopes-CvU5QBQ4.js} +110 -79
- package/dist/session.d.ts +20 -0
- package/dist/session.js +6 -6
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -37,6 +37,9 @@ export declare interface ActionProcedureDescriptor<Name extends string, Input ex
|
|
|
37
37
|
* `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
|
|
38
38
|
* the only two shapes `security.defaultDeny` accepts. */
|
|
39
39
|
readonly openAccess: string | undefined;
|
|
40
|
+
/** Only these apps of the project may call this procedure — see `service` on
|
|
41
|
+
* the definer's options. Absent → the app's own clients call it. */
|
|
42
|
+
readonly service: ServiceSpec | undefined;
|
|
40
43
|
/** Opt this action into a public REST endpoint (innovation/11). */
|
|
41
44
|
readonly publicApi: PublicApiSpec | undefined;
|
|
42
45
|
/** Opt this action into the auto-synthesized agent toolset (innovation/07). */
|
|
@@ -89,9 +92,9 @@ export declare interface AnyApprovalPolicy {
|
|
|
89
92
|
readonly reason?: string;
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
/** A guard entry is a scope check, a relationship check, an identity check,
|
|
93
|
-
*
|
|
94
|
-
export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec | AuthenticatedCheckSpec | OpenAccessSpec;
|
|
95
|
+
/** A guard entry is a scope check, a relationship check, an identity check, a
|
|
96
|
+
* declared no-check-needed decision, or a service-only decision. */
|
|
97
|
+
export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec | AuthenticatedCheckSpec | OpenAccessSpec | ServiceAccessSpec;
|
|
95
98
|
|
|
96
99
|
/** Any declared event, with the generics erased — for registries and audits. */
|
|
97
100
|
export declare type AnyEventDescriptor = EventDescriptor<string, Schema.Schema.Any, Schema.Schema.Any>;
|
|
@@ -130,6 +133,21 @@ export declare const APIKEY_ISSUE_OTHER_SCOPE = "apikeys:issue:other";
|
|
|
130
133
|
*/
|
|
131
134
|
export declare const APIKEY_ISSUE_SELF_SCOPE = "apikeys:issue:self";
|
|
132
135
|
|
|
136
|
+
export declare class ApiUnavailable extends ApiUnavailable_base {
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
declare const ApiUnavailable_base: Schema.TaggedErrorClass<ApiUnavailable, "ApiUnavailable", {
|
|
140
|
+
readonly _tag: Schema.tag<"ApiUnavailable">;
|
|
141
|
+
} & {
|
|
142
|
+
/** The binding name on the caller (`apis.<name>`). */
|
|
143
|
+
api: typeof Schema.String;
|
|
144
|
+
/** The rpc tag that was called. */
|
|
145
|
+
tag: typeof Schema.String;
|
|
146
|
+
/** Why no answer arrived. */
|
|
147
|
+
reason: Schema.Literal<["timeout", "transport", "closed"]>;
|
|
148
|
+
message: typeof Schema.String;
|
|
149
|
+
}>;
|
|
150
|
+
|
|
133
151
|
/** The subject a decision produces, plus what the decision took away. `removed`
|
|
134
152
|
* is never non-empty for a `grant`. */
|
|
135
153
|
export declare interface AppliedScopes {
|
|
@@ -677,7 +695,7 @@ export declare type AuthSubjectGuardVerdict = {
|
|
|
677
695
|
* `fresh` the caller runs the handler then calls `finish`/`fail`; on
|
|
678
696
|
* `replay`/`conflict` it returns immediately without touching the handler.
|
|
679
697
|
*/
|
|
680
|
-
export declare const beginIdempotent: (store: IdempotencyStore, scope: string, key: string, ttlMs: number, now: number) => Promise<IdempotencyOutcome>;
|
|
698
|
+
export declare const beginIdempotent: (store: IdempotencyStore, scope: string, key: string, ttlMs: number, now: number, inFlightLeaseMs?: number) => Promise<IdempotencyOutcome>;
|
|
681
699
|
|
|
682
700
|
/**
|
|
683
701
|
* A cross-table business rule was violated inside a mutation's transaction; the
|
|
@@ -1379,11 +1397,25 @@ export declare interface CoordinatedTickOutcome {
|
|
|
1379
1397
|
* because that is where the normalised decision lands, and because every
|
|
1380
1398
|
* enforcement path reads the descriptor's array and nothing else.
|
|
1381
1399
|
*/
|
|
1382
|
-
export declare type DeclaredAccess<Input = unknown> = ReadonlyArray<AnyGuardSpec<Input> | OpenAccessSpec>;
|
|
1400
|
+
export declare type DeclaredAccess<Input = unknown> = ReadonlyArray<AnyGuardSpec<Input> | OpenAccessSpec | ServiceAccessSpec>;
|
|
1383
1401
|
|
|
1384
1402
|
/** Every channel declared in this process, by routing key. */
|
|
1385
1403
|
export declare const declaredReactivityChannelKeys: () => ReadonlySet<string>;
|
|
1386
1404
|
|
|
1405
|
+
/**
|
|
1406
|
+
* How long a RUNNING claim holds without renewal.
|
|
1407
|
+
*
|
|
1408
|
+
* A claim used to go stale only after the replay window — 24 hours by default —
|
|
1409
|
+
* so a process that died inside a handler left its key `in_flight` for a day:
|
|
1410
|
+
* every retry was refused as "still in flight", including the retry of a
|
|
1411
|
+
* workflow step whose callee had simply restarted. A live handler renews its
|
|
1412
|
+
* claim every third of this (`holdIdempotentClaim`); a dead one stops, and the
|
|
1413
|
+
* next retry takes over after at most this long. A handler that blocks its event
|
|
1414
|
+
* loop longer than this can be taken over while still running — the same bound
|
|
1415
|
+
* every lease has.
|
|
1416
|
+
*/
|
|
1417
|
+
export declare const DEFAULT_IN_FLIGHT_LEASE_MS = 30000;
|
|
1418
|
+
|
|
1387
1419
|
/** Default cap on cached subjects before the cache sweeps + trims. */
|
|
1388
1420
|
export declare const DEFAULT_SCOPE_CACHE_MAX_ENTRIES = 10000;
|
|
1389
1421
|
|
|
@@ -1418,6 +1450,26 @@ export declare const defineAction: <const Name extends string, Input extends Sch
|
|
|
1418
1450
|
* openAccess: 'public pricing page — reads no caller data'
|
|
1419
1451
|
*/
|
|
1420
1452
|
readonly openAccess?: string;
|
|
1453
|
+
/**
|
|
1454
|
+
* Make this a SERVICE procedure: callable only by the listed apps of this
|
|
1455
|
+
* project, and by nobody else — no browser session, no API key, no admin.
|
|
1456
|
+
*
|
|
1457
|
+
* service: { callers: ['worker'] }
|
|
1458
|
+
*
|
|
1459
|
+
* The caller is another app that binds this one under `apis:` and calls it as
|
|
1460
|
+
* `ctx.apis.<name>.<tag>(input)`; it authenticates with the service credential
|
|
1461
|
+
* this app accepts under `serviceCallers`. It is its own access decision, so it
|
|
1462
|
+
* satisfies `security.defaultDeny` and cannot sit beside `guards:` or
|
|
1463
|
+
* `openAccess:`.
|
|
1464
|
+
*
|
|
1465
|
+
* The handler runs as the calling SERVICE: it reads across tenants and must
|
|
1466
|
+
* name a tenant to write into one — `ctx.storeForTenant(input.tenantId)` is the
|
|
1467
|
+
* shape for per-tenant work.
|
|
1468
|
+
*
|
|
1469
|
+
* Codegen emits it into `serviceGroup.generated.ts`, never into the browser's
|
|
1470
|
+
* `rpcGroup.generated.ts`.
|
|
1471
|
+
*/
|
|
1472
|
+
readonly service?: ServiceSpec;
|
|
1421
1473
|
/** Table(s) this action READS. Declaring it is what lets `voltro check` know
|
|
1422
1474
|
* the table is alive — without it, a table only an action touches reads as
|
|
1423
1475
|
* an orphan. See `ActionProcedureDescriptor.source`. */
|
|
@@ -1631,6 +1683,26 @@ export declare const defineMutation: <const Name extends string, Input extends S
|
|
|
1631
1683
|
* openAccess: 'public pricing page — reads no caller data'
|
|
1632
1684
|
*/
|
|
1633
1685
|
readonly openAccess?: string;
|
|
1686
|
+
/**
|
|
1687
|
+
* Make this a SERVICE procedure: callable only by the listed apps of this
|
|
1688
|
+
* project, and by nobody else — no browser session, no API key, no admin.
|
|
1689
|
+
*
|
|
1690
|
+
* service: { callers: ['worker'] }
|
|
1691
|
+
*
|
|
1692
|
+
* The caller is another app that binds this one under `apis:` and calls it as
|
|
1693
|
+
* `ctx.apis.<name>.<tag>(input)`; it authenticates with the service credential
|
|
1694
|
+
* this app accepts under `serviceCallers`. It is its own access decision, so it
|
|
1695
|
+
* satisfies `security.defaultDeny` and cannot sit beside `guards:` or
|
|
1696
|
+
* `openAccess:`.
|
|
1697
|
+
*
|
|
1698
|
+
* The handler runs as the calling SERVICE: it reads across tenants and must
|
|
1699
|
+
* name a tenant to write into one — `ctx.storeForTenant(input.tenantId)` is the
|
|
1700
|
+
* shape for per-tenant work.
|
|
1701
|
+
*
|
|
1702
|
+
* Codegen emits it into `serviceGroup.generated.ts`, never into the browser's
|
|
1703
|
+
* `rpcGroup.generated.ts`.
|
|
1704
|
+
*/
|
|
1705
|
+
readonly service?: ServiceSpec;
|
|
1634
1706
|
/** Project this mutation as a public REST endpoint (innovation/11). */
|
|
1635
1707
|
readonly publicApi?: PublicApiSpec;
|
|
1636
1708
|
/** Expose this mutation as an agent tool (innovation/07). */
|
|
@@ -1764,6 +1836,26 @@ export declare const defineQuery: <const Name extends string, Input extends Sche
|
|
|
1764
1836
|
* openAccess: 'public pricing page — reads no caller data'
|
|
1765
1837
|
*/
|
|
1766
1838
|
readonly openAccess?: string;
|
|
1839
|
+
/**
|
|
1840
|
+
* Make this a SERVICE procedure: callable only by the listed apps of this
|
|
1841
|
+
* project, and by nobody else — no browser session, no API key, no admin.
|
|
1842
|
+
*
|
|
1843
|
+
* service: { callers: ['worker'] }
|
|
1844
|
+
*
|
|
1845
|
+
* The caller is another app that binds this one under `apis:` and calls it as
|
|
1846
|
+
* `ctx.apis.<name>.<tag>(input)`; it authenticates with the service credential
|
|
1847
|
+
* this app accepts under `serviceCallers`. It is its own access decision, so it
|
|
1848
|
+
* satisfies `security.defaultDeny` and cannot sit beside `guards:` or
|
|
1849
|
+
* `openAccess:`.
|
|
1850
|
+
*
|
|
1851
|
+
* The handler runs as the calling SERVICE: it reads across tenants and must
|
|
1852
|
+
* name a tenant to write into one — `ctx.storeForTenant(input.tenantId)` is the
|
|
1853
|
+
* shape for per-tenant work.
|
|
1854
|
+
*
|
|
1855
|
+
* Codegen emits it into `serviceGroup.generated.ts`, never into the browser's
|
|
1856
|
+
* `rpcGroup.generated.ts`.
|
|
1857
|
+
*/
|
|
1858
|
+
readonly service?: ServiceSpec;
|
|
1767
1859
|
/** Project this query as a public REST endpoint (innovation/11). */
|
|
1768
1860
|
readonly publicApi?: PublicApiSpec;
|
|
1769
1861
|
/** Expose this query as an agent tool (innovation/07). */
|
|
@@ -2302,9 +2394,34 @@ export declare const findAdvisoryResourceGuards: (procedures: ReadonlyArray<{
|
|
|
2302
2394
|
/** Record a completed response for replay. */
|
|
2303
2395
|
export declare const finishIdempotent: (store: IdempotencyStore, scope: string, key: string, response: IdempotencyResponse, now: number) => Promise<void>;
|
|
2304
2396
|
|
|
2397
|
+
/**
|
|
2398
|
+
* The value a ONE-SHOT read of an rpc resolves to, given the first value it
|
|
2399
|
+
* delivered.
|
|
2400
|
+
*
|
|
2401
|
+
* A query's success type is the three-member union above, and a one-shot read
|
|
2402
|
+
* 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
|
|
2406
|
+
* passes through, which may legitimately be a tagged object of the app's.
|
|
2407
|
+
*
|
|
2408
|
+
* Shared because there are two such reads — the server loader's `ctx.query`
|
|
2409
|
+
* over `POST /rpc` and the browser loader's over the page's socket — and an
|
|
2410
|
+
* unwrap written twice is how an authorization answer once reached a loader as
|
|
2411
|
+
* its return value on one transport and as a rejection on the other.
|
|
2412
|
+
*
|
|
2413
|
+
* `label` prefixes every message (`[loader.query]`), naming the surface that
|
|
2414
|
+
* made the read.
|
|
2415
|
+
*/
|
|
2416
|
+
export declare const firstEventValue: (tag: string, value: unknown, label: string) => unknown;
|
|
2417
|
+
|
|
2305
2418
|
/** A route rendered for humans — logs, the inspect surface, the dashboard. */
|
|
2306
2419
|
export declare const formatEventRoute: (route: string) => string;
|
|
2307
2420
|
|
|
2421
|
+
/** How long a record of this status stays fresh — the replay window for a
|
|
2422
|
+
* completed response, the lease for a running one (never longer than the window). */
|
|
2423
|
+
export declare const freshFor: (status: IdempotencyRecord["status"], ttlMs: number, inFlightLeaseMs: number) => number;
|
|
2424
|
+
|
|
2308
2425
|
/** Close code a gateway connection receives when its credential expires —
|
|
2309
2426
|
* the same session-expiry contract the rpc socket has (SEC-16), spelled as
|
|
2310
2427
|
* an application close code so foreign clients can reauth + reconnect. */
|
|
@@ -2427,6 +2544,9 @@ export declare const hasEnforcedGuard: (guards: DeclaredAccess | undefined) => b
|
|
|
2427
2544
|
* the RAW subject scopes — use `hasEffectiveScope` to include rbac roles. */
|
|
2428
2545
|
export declare const hasScope: (subject: Subject, scope: string) => boolean;
|
|
2429
2546
|
|
|
2547
|
+
/** Renew a fresh claim's lease while its handler runs; returns the stop. */
|
|
2548
|
+
export declare const holdIdempotentClaim: (store: IdempotencyStore, scope: string, key: string, inFlightLeaseMs?: number, now?: () => number) => (() => void);
|
|
2549
|
+
|
|
2430
2550
|
/**
|
|
2431
2551
|
* Short-circuit response shape. Returning a `HttpInterceptResponse`
|
|
2432
2552
|
* from the interceptor halts the pipeline — the framework sends the
|
|
@@ -2526,14 +2646,24 @@ export declare interface IdempotencyStore {
|
|
|
2526
2646
|
/** Current record for (scope, key), or null. Read-side (inspect/dashboard). */
|
|
2527
2647
|
readonly get: (scope: string, key: string) => Promise<IdempotencyRecord | null>;
|
|
2528
2648
|
/**
|
|
2529
|
-
* Atomically claim (scope, key) IF absent OR stale
|
|
2649
|
+
* Atomically claim (scope, key) IF absent OR stale — a COMPLETED record older
|
|
2650
|
+
* than `ttlMs` (the replay window), or an IN-FLIGHT one whose lease
|
|
2651
|
+
* (`inFlightLeaseMs`, renewed by the live handler) lapsed:
|
|
2530
2652
|
* write a fresh `in_flight` row and return `'claimed'`. Otherwise return the
|
|
2531
2653
|
* EXISTING (still-fresh) record. The atomicity here is the whole game — two
|
|
2532
2654
|
* concurrent same-key requests both hit this, exactly one gets `'claimed'`,
|
|
2533
2655
|
* the loser reads the winner's record. In SQL this is one
|
|
2534
2656
|
* `INSERT … ON CONFLICT DO UPDATE … WHERE existing.createdAt < now - ttl`.
|
|
2535
2657
|
*/
|
|
2536
|
-
readonly claim: (scope: string, key: string, now: number, ttlMs: number) => Promise<'claimed' | IdempotencyRecord>;
|
|
2658
|
+
readonly claim: (scope: string, key: string, now: number, ttlMs: number, inFlightLeaseMs: number) => Promise<'claimed' | IdempotencyRecord>;
|
|
2659
|
+
/**
|
|
2660
|
+
* Extend a RUNNING claim's lease: bump its timestamp while it is still
|
|
2661
|
+
* `in_flight` (a completed or released claim is left alone). The server calls
|
|
2662
|
+
* it while the handler runs; a process that dies stops calling it, and its
|
|
2663
|
+
* claim goes stale after `inFlightLeaseMs` instead of after the whole replay
|
|
2664
|
+
* window.
|
|
2665
|
+
*/
|
|
2666
|
+
readonly renew: (scope: string, key: string, now: number) => Promise<void>;
|
|
2537
2667
|
/** Flip the claim to `completed` and store the response. */
|
|
2538
2668
|
readonly complete: (scope: string, key: string, response: IdempotencyResponse, now: number) => Promise<void>;
|
|
2539
2669
|
/** Drop the claim (handler errored → a retry may re-process). */
|
|
@@ -2616,12 +2746,21 @@ export declare interface InsertTarget<Input = unknown, Row = unknown, Item = Rec
|
|
|
2616
2746
|
readonly shapeItem?: ((input: Input, optimisticId: string) => Item) | undefined;
|
|
2617
2747
|
}
|
|
2618
2748
|
|
|
2749
|
+
/** Is this failure a missing answer (retry it) rather than an answer (do not)? */
|
|
2750
|
+
export declare const isApiUnavailable: (error: unknown) => error is ApiUnavailable;
|
|
2751
|
+
|
|
2619
2752
|
/** Runtime narrowing for the identity variant. */
|
|
2620
2753
|
export declare const isAuthenticatedCheck: (g: AnyCheckSpec) => g is AuthenticatedCheckSpec;
|
|
2621
2754
|
|
|
2622
2755
|
/** Narrow a guard entry to the identity variant. */
|
|
2623
2756
|
export declare const isAuthenticatedGuard: <I>(g: AnyGuardSpec<I>) => g is AuthenticatedGuardSpec;
|
|
2624
2757
|
|
|
2758
|
+
/** Belongs in the app's own client group (`rpcGroup.generated.ts`). */
|
|
2759
|
+
export declare const isClientReachable: (descriptor: {
|
|
2760
|
+
readonly internal?: boolean | undefined;
|
|
2761
|
+
readonly service?: ServiceSpec | undefined;
|
|
2762
|
+
}) => boolean;
|
|
2763
|
+
|
|
2625
2764
|
export declare const isEventDescriptor: (value: unknown) => value is AnyEventDescriptor;
|
|
2626
2765
|
|
|
2627
2766
|
/** True when every row in the set carries an `id`. The dispatcher uses
|
|
@@ -2630,6 +2769,17 @@ export declare const isEventDescriptor: (value: unknown) => value is AnyEventDes
|
|
|
2630
2769
|
* diffed by id and falls back to shipping the full data. */
|
|
2631
2770
|
export declare const isIdKeyed: (rows: ReadonlyArray<Readonly<Record<string, unknown>>>) => rows is ReadonlyArray<PatchRow>;
|
|
2632
2771
|
|
|
2772
|
+
/**
|
|
2773
|
+
* A machine actor that works ACROSS tenants: the framework acting as itself
|
|
2774
|
+
* (`system`) or another app of the project calling in (`service`).
|
|
2775
|
+
*
|
|
2776
|
+
* This is a statement about DATA placement only — no row filter, no implicit
|
|
2777
|
+
* tenant, an explicit tenant for writes. It says nothing about authority: a
|
|
2778
|
+
* service holds no scopes, and every place that lets `system` bypass a check
|
|
2779
|
+
* keeps asking `isSystemSubject`.
|
|
2780
|
+
*/
|
|
2781
|
+
export declare const isMachineSubject: (subject: Subject) => boolean;
|
|
2782
|
+
|
|
2633
2783
|
/** Runtime narrowing for the declared-open variant. */
|
|
2634
2784
|
export declare const isOpenAccess: (g: AnyCheckSpec) => g is OpenAccessSpec;
|
|
2635
2785
|
|
|
@@ -2655,6 +2805,20 @@ export declare const isReactivityChannel: (value: unknown) => value is Reactivit
|
|
|
2655
2805
|
*/
|
|
2656
2806
|
export declare const isReactivityChannelKey: (key: string) => boolean;
|
|
2657
2807
|
|
|
2808
|
+
/** Runtime narrowing for the service-only variant. */
|
|
2809
|
+
export declare const isServiceAccess: (g: AnyCheckSpec) => g is ServiceAccessSpec;
|
|
2810
|
+
|
|
2811
|
+
/** Belongs in the group other apps call (`serviceGroup.generated.ts`). */
|
|
2812
|
+
export declare const isServiceReachable: (descriptor: {
|
|
2813
|
+
readonly internal?: boolean | undefined;
|
|
2814
|
+
readonly service?: ServiceSpec | undefined;
|
|
2815
|
+
}) => boolean;
|
|
2816
|
+
|
|
2817
|
+
/** Is this caller another app of the project, and which one? */
|
|
2818
|
+
export declare const isServiceSubject: (subject: Subject) => subject is Extract<Subject, {
|
|
2819
|
+
readonly type: "service";
|
|
2820
|
+
}>;
|
|
2821
|
+
|
|
2658
2822
|
export declare const isSystemSubject: (subject: Subject) => boolean;
|
|
2659
2823
|
|
|
2660
2824
|
export declare const isTenantSelectable: (subject: Subject) => subject is TenantSelectableSubject;
|
|
@@ -2786,6 +2950,9 @@ export declare interface MutationProcedureDescriptor<Name extends string, Input
|
|
|
2786
2950
|
* `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
|
|
2787
2951
|
* the only two shapes `security.defaultDeny` accepts. */
|
|
2788
2952
|
readonly openAccess: string | undefined;
|
|
2953
|
+
/** Only these apps of the project may call this procedure — see `service` on
|
|
2954
|
+
* the definer's options. Absent → the app's own clients call it. */
|
|
2955
|
+
readonly service: ServiceSpec | undefined;
|
|
2789
2956
|
/** Opt this mutation into a public REST endpoint (innovation/11). */
|
|
2790
2957
|
readonly publicApi: PublicApiSpec | undefined;
|
|
2791
2958
|
/** Opt this mutation into the auto-synthesized agent toolset (innovation/07). */
|
|
@@ -3431,11 +3598,17 @@ export declare interface PluginErrorSchema {
|
|
|
3431
3598
|
|
|
3432
3599
|
/** A public raw-HTTP route a plugin serves on the framework listener. */
|
|
3433
3600
|
export declare interface PluginHttpRoute {
|
|
3434
|
-
/** HTTP method, or `'*'` for any (the handler decides).
|
|
3435
|
-
*
|
|
3436
|
-
*
|
|
3437
|
-
*
|
|
3601
|
+
/** HTTP method, or `'*'` for any (the handler decides). The server mounts the
|
|
3602
|
+
* path for exactly the methods its routes declare; `'*'` mounts it for every
|
|
3603
|
+
* method the router knows (34), so keep that for a route that really serves
|
|
3604
|
+
* them — see `answersOtherMethods` for one that only wants to refuse them. */
|
|
3438
3605
|
readonly method: '*' | 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
|
3606
|
+
/** `true`: the handler gates its own method, so a request with a method the
|
|
3607
|
+
* path does not declare is still handed to it — to answer with a precise
|
|
3608
|
+
* `405` and `Allow` instead of the router's `404`. The path is still mounted
|
|
3609
|
+
* only for `method`. A REST route sets it. Without it, a route that declares
|
|
3610
|
+
* one method never sees another. */
|
|
3611
|
+
readonly answersOtherMethods?: boolean | undefined;
|
|
3439
3612
|
/** Absolute path, e.g. `/_voltro/storage`. Matched byte for byte: the
|
|
3440
3613
|
* router folds case and a trailing slash to FIND the route, the dispatcher
|
|
3441
3614
|
* then asks whether the request's path is one the route declared — so
|
|
@@ -4491,6 +4664,9 @@ export declare interface QueryProcedureDescriptor<Name extends string, Input ext
|
|
|
4491
4664
|
* `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
|
|
4492
4665
|
* the only two shapes `security.defaultDeny` accepts. */
|
|
4493
4666
|
readonly openAccess: string | undefined;
|
|
4667
|
+
/** Only these apps of the project may call this procedure — see `service` on
|
|
4668
|
+
* the definer's options. Absent → the app's own clients call it. */
|
|
4669
|
+
readonly service: ServiceSpec | undefined;
|
|
4494
4670
|
/** Opt this query into a public REST endpoint (innovation/11). */
|
|
4495
4671
|
readonly publicApi: PublicApiSpec | undefined;
|
|
4496
4672
|
/** Opt this query into the auto-synthesized agent toolset (innovation/07). */
|
|
@@ -4847,6 +5023,9 @@ declare interface RestRouteContext {
|
|
|
4847
5023
|
* REST handlers on the same capability surface as query/mutation/action
|
|
4848
5024
|
* handlers without creating a protocol-to-runtime package dependency. */
|
|
4849
5025
|
readonly app?: unknown;
|
|
5026
|
+
/** The caller's address, resolved through `security.trustedProxies` — the
|
|
5027
|
+
* value the listener's per-IP shield counts. Absent when unknown. */
|
|
5028
|
+
readonly remoteAddr?: string;
|
|
4850
5029
|
}
|
|
4851
5030
|
|
|
4852
5031
|
declare interface RestRouteDescriptor<I, O> {
|
|
@@ -5149,6 +5328,16 @@ export declare interface RpcInterceptorContext {
|
|
|
5149
5328
|
readonly surface?: 'rest';
|
|
5150
5329
|
/** The descriptor's `publicApi` spec, on a projected call. */
|
|
5151
5330
|
readonly publicApi?: PublicApiSpec;
|
|
5331
|
+
/**
|
|
5332
|
+
* The caller's address — the same value the pre-auth IP shield counts, resolved
|
|
5333
|
+
* by the listener through `security.trustedProxies` (and
|
|
5334
|
+
* `security.clientAddressHeader`). A client cannot set it: the listener
|
|
5335
|
+
* writes it, and any copy the client sends is discarded.
|
|
5336
|
+
*
|
|
5337
|
+
* Absent when the call did not arrive over the network (a workflow step, a
|
|
5338
|
+
* schedule, an in-process invocation) or the socket address is unknown.
|
|
5339
|
+
*/
|
|
5340
|
+
readonly remoteAddr?: string;
|
|
5152
5341
|
}
|
|
5153
5342
|
|
|
5154
5343
|
/**
|
|
@@ -5371,6 +5560,118 @@ declare const ServerOnlyColumnWrite_base: Schema.TaggedErrorClass<ServerOnlyColu
|
|
|
5371
5560
|
columns: Schema.Array$<typeof Schema.String>;
|
|
5372
5561
|
}>;
|
|
5373
5562
|
|
|
5563
|
+
/** A service (app) name: what `serviceCallers`, `service.callers` and a service
|
|
5564
|
+
* credential all spell the same way. At most 56 characters: the caller writes
|
|
5565
|
+
* as `service:<name>`, which is an `actors.id` — an `id()` column, VARCHAR(64)
|
|
5566
|
+
* on three dialects — so a longer name would fail its first audited write. */
|
|
5567
|
+
export declare const SERVICE_NAME_PATTERN: RegExp;
|
|
5568
|
+
|
|
5569
|
+
/**
|
|
5570
|
+
* The runtime-erased form of a procedure's `service: { callers }` — "only these
|
|
5571
|
+
* apps of the project may call this, and nobody else".
|
|
5572
|
+
*
|
|
5573
|
+
* A guard entry for the reason `OpenAccessSpec` is one: every enforcement path
|
|
5574
|
+
* is handed the `guards` array and nothing else, so a decision that lived on a
|
|
5575
|
+
* separate descriptor field would be enforced by the paths somebody remembered
|
|
5576
|
+
* and skipped by the rest.
|
|
5577
|
+
*
|
|
5578
|
+
* It admits exactly a `service` subject whose name is listed. `admin:full` does
|
|
5579
|
+
* NOT bypass it: a service procedure is a machine contract between two apps,
|
|
5580
|
+
* and a browser session holding the admin scope is not one of the parties.
|
|
5581
|
+
*/
|
|
5582
|
+
export declare interface ServiceAccessSpec {
|
|
5583
|
+
/** The calling apps' names, as the callee declares them in `serviceCallers`.
|
|
5584
|
+
* Non-empty by construction — the definers refuse an empty list. */
|
|
5585
|
+
readonly service: ReadonlyArray<string>;
|
|
5586
|
+
}
|
|
5587
|
+
|
|
5588
|
+
/** Build the erased `service:` entry. The definers call this. */
|
|
5589
|
+
export declare const serviceAccessSpec: (callers: ReadonlyArray<string>) => ServiceAccessSpec;
|
|
5590
|
+
|
|
5591
|
+
/**
|
|
5592
|
+
* A procedure map (`ServiceProcedures`) turned into the nested object a caller
|
|
5593
|
+
* writes: `'deployments.activate'` becomes `.deployments.activate(...)`. A tag
|
|
5594
|
+
* that is both a procedure and a namespace prefix is both.
|
|
5595
|
+
*/
|
|
5596
|
+
export declare type ServiceApi<P> = 0 extends (1 & P) ? any : ServiceApiOf<P>;
|
|
5597
|
+
|
|
5598
|
+
declare type ServiceApiOf<P> = {
|
|
5599
|
+
readonly [H in ServiceTagHead<keyof P & string>]: (H extends keyof P ? ServiceCall<P[H]> : unknown) & ServiceApiOf<{
|
|
5600
|
+
[K in keyof P & string as K extends `${H}.${infer R}` ? R : never]: P[K];
|
|
5601
|
+
}>;
|
|
5602
|
+
};
|
|
5603
|
+
|
|
5604
|
+
/** One procedure as the caller sees it — callable, `.effect`, and `.subscribe` for a query. */
|
|
5605
|
+
export declare type ServiceCall<D> = D extends ServiceDescriptorShape ? ((input: ServiceDecoded<D['input']>, options?: ServiceCallOptions) => Promise<ServiceDecoded<D['output']>>) & {
|
|
5606
|
+
/** The same call as an Effect. Inside a workflow step its write key is
|
|
5607
|
+
* derived from the step, so a re-sent step replays instead of writing twice. */
|
|
5608
|
+
readonly effect: (input: ServiceDecoded<D['input']>, options?: ServiceCallOptions) => Effect.Effect<ServiceDecoded<D['output']>, unknown>;
|
|
5609
|
+
} & (D extends {
|
|
5610
|
+
readonly kind: 'query';
|
|
5611
|
+
} ? {
|
|
5612
|
+
/** Hold the live query open; `onValue` gets each materialized value. */
|
|
5613
|
+
readonly subscribe: (input: ServiceDecoded<D['input']>, handlers: ServiceSubscribeHandlers<ServiceDecoded<D['output']>>) => ServiceSubscription;
|
|
5614
|
+
} : unknown) : never;
|
|
5615
|
+
|
|
5616
|
+
/** The apps a guard array admits as service callers, or `undefined` when the
|
|
5617
|
+
* procedure is not a service procedure. */
|
|
5618
|
+
export declare const serviceCallersOf: (guards: ReadonlyArray<AnyCheckSpec> | undefined) => ReadonlyArray<string> | undefined;
|
|
5619
|
+
|
|
5620
|
+
export declare interface ServiceCallOptions {
|
|
5621
|
+
/** Deadline for this call. Default 10 seconds. */
|
|
5622
|
+
readonly timeoutMs?: number;
|
|
5623
|
+
/** Explicit idempotency key for a write. Overrides the ambient and the fresh one. */
|
|
5624
|
+
readonly idempotencyKey?: string;
|
|
5625
|
+
/** Interrupts the call when aborted. */
|
|
5626
|
+
readonly signal?: AbortSignal;
|
|
5627
|
+
}
|
|
5628
|
+
|
|
5629
|
+
/** The decoded (application-side) type of a schema slot. */
|
|
5630
|
+
export declare type ServiceDecoded<S> = S extends {
|
|
5631
|
+
readonly Type: infer T;
|
|
5632
|
+
} ? T : never;
|
|
5633
|
+
|
|
5634
|
+
/** What a service procedure's descriptor type carries that the facade reads. */
|
|
5635
|
+
export declare interface ServiceDescriptorShape {
|
|
5636
|
+
readonly kind: string;
|
|
5637
|
+
readonly input: unknown;
|
|
5638
|
+
readonly output: unknown;
|
|
5639
|
+
}
|
|
5640
|
+
|
|
5641
|
+
/**
|
|
5642
|
+
* A service procedure's declaration: which apps of the project may call it.
|
|
5643
|
+
*
|
|
5644
|
+
* service: { callers: ['worker'] }
|
|
5645
|
+
*
|
|
5646
|
+
* The names are the CALLING apps' names as this app lists them in
|
|
5647
|
+
* `serviceCallers` — the same name the caller's credential resolves to.
|
|
5648
|
+
*/
|
|
5649
|
+
export declare interface ServiceSpec {
|
|
5650
|
+
readonly callers: ReadonlyArray<string>;
|
|
5651
|
+
}
|
|
5652
|
+
|
|
5653
|
+
/**
|
|
5654
|
+
* The subject a verified service credential resolves to. Built by the service
|
|
5655
|
+
* strategy only — an app never constructs one, and nothing grants it scopes.
|
|
5656
|
+
*/
|
|
5657
|
+
export declare const serviceSubject: (service: string) => Extract<Subject, {
|
|
5658
|
+
readonly type: "service";
|
|
5659
|
+
}>;
|
|
5660
|
+
|
|
5661
|
+
export declare interface ServiceSubscribeHandlers<T> {
|
|
5662
|
+
/** Every materialized value of the live query, starting with the first snapshot. */
|
|
5663
|
+
readonly onValue: (value: T) => void;
|
|
5664
|
+
/** The subscription's own in-band error (e.g. an access refusal). */
|
|
5665
|
+
readonly onError?: (error: unknown) => void;
|
|
5666
|
+
}
|
|
5667
|
+
|
|
5668
|
+
export declare interface ServiceSubscription {
|
|
5669
|
+
readonly unsubscribe: () => void;
|
|
5670
|
+
}
|
|
5671
|
+
|
|
5672
|
+
/** The first segment of a dotted rpc tag. */
|
|
5673
|
+
export declare type ServiceTagHead<K extends string> = K extends `${infer H}.${string}` ? H : K;
|
|
5674
|
+
|
|
5374
5675
|
/**
|
|
5375
5676
|
* Publish the caller's fully-resolved scope set for this request (raw subject
|
|
5376
5677
|
* scopes ∪ role-derived scopes ∪ any extra grants). Called by the rbac
|
|
@@ -5583,6 +5884,14 @@ export declare const Subject: Schema.Union<[Schema.Struct<{
|
|
|
5583
5884
|
tenantId: typeof Schema.Null;
|
|
5584
5885
|
scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
5585
5886
|
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
5887
|
+
}>, Schema.Struct<{
|
|
5888
|
+
type: Schema.Literal<["service"]>;
|
|
5889
|
+
id: typeof Schema.String;
|
|
5890
|
+
/** The calling app's name, as the callee lists it in `serviceCallers`. */
|
|
5891
|
+
service: typeof Schema.String;
|
|
5892
|
+
tenantId: typeof Schema.Null;
|
|
5893
|
+
scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
5894
|
+
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
5586
5895
|
}>]>;
|
|
5587
5896
|
|
|
5588
5897
|
export declare type Subject = typeof Subject.Type;
|
|
@@ -5636,6 +5945,12 @@ export declare const SubjectIdentity: Schema.Union<[Schema.Struct<{
|
|
|
5636
5945
|
id: typeof Schema.String;
|
|
5637
5946
|
tenantId: typeof Schema.Null;
|
|
5638
5947
|
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
5948
|
+
}>, Schema.Struct<{
|
|
5949
|
+
type: Schema.Literal<["service"]>;
|
|
5950
|
+
id: typeof Schema.String;
|
|
5951
|
+
tenantId: typeof Schema.Null;
|
|
5952
|
+
metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
|
|
5953
|
+
service: typeof Schema.String;
|
|
5639
5954
|
}>]>;
|
|
5640
5955
|
|
|
5641
5956
|
export declare type SubjectIdentity = typeof SubjectIdentity.Type;
|
|
@@ -5708,6 +6023,15 @@ declare const SubjectService_base: Context.TagClass<SubjectService, "@voltro/Sub
|
|
|
5708
6023
|
readonly metadata?: {
|
|
5709
6024
|
readonly [x: string]: unknown;
|
|
5710
6025
|
} | undefined;
|
|
6026
|
+
} | {
|
|
6027
|
+
readonly type: "service";
|
|
6028
|
+
readonly id: string;
|
|
6029
|
+
readonly tenantId: null;
|
|
6030
|
+
readonly scopes?: readonly string[] | undefined;
|
|
6031
|
+
readonly metadata?: {
|
|
6032
|
+
readonly [x: string]: unknown;
|
|
6033
|
+
} | undefined;
|
|
6034
|
+
readonly service: string;
|
|
5711
6035
|
}>;
|
|
5712
6036
|
|
|
5713
6037
|
/** Subscribe to server-primitive errors. Returns an unsubscribe fn. */
|
|
@@ -6632,6 +6956,29 @@ export declare interface VoltroPlugin {
|
|
|
6632
6956
|
* specific permission name in the error.
|
|
6633
6957
|
*/
|
|
6634
6958
|
readonly extendSchema?: PluginSchemaContribution;
|
|
6959
|
+
/**
|
|
6960
|
+
* A directory of APP-SHAPED primitives this plugin brings with it — the same
|
|
6961
|
+
* file conventions an app uses (`*.query.ts` + `.server.ts`, `*.mutation.ts`,
|
|
6962
|
+
* `*.workflow.tsx`, `*.cron.tsx`, `*.subscribe.ts`, `*.reaction.ts`,
|
|
6963
|
+
* `schema.ts` / `*.entity.ts`, …).
|
|
6964
|
+
*
|
|
6965
|
+
* sources: fileURLToPath(new URL('./app', import.meta.url))
|
|
6966
|
+
*
|
|
6967
|
+
* Every app that installs the plugin discovers those files exactly as it
|
|
6968
|
+
* discovers its own: the same codegen, browser-safety guard, access gate,
|
|
6969
|
+
* migrations and boot paths. That is what makes a reusable BUILDING BLOCK — a
|
|
6970
|
+
* ledger, an approval flow, an audit trail — shareable between apps of a
|
|
6971
|
+
* project without copying it.
|
|
6972
|
+
*
|
|
6973
|
+
* It shares CODE, never data: each installing app gets its own copy of the
|
|
6974
|
+
* block's tables, in its own database. Two apps that need each other's data
|
|
6975
|
+
* call each other (`apis:` + `service:` procedures).
|
|
6976
|
+
*
|
|
6977
|
+
* The block's procedures keep the names their descriptors declare; a name
|
|
6978
|
+
* that collides with the app's own is refused at codegen, as any collision is.
|
|
6979
|
+
* An absolute path to a directory; a missing one refuses the boot.
|
|
6980
|
+
*/
|
|
6981
|
+
readonly sources?: string;
|
|
6635
6982
|
/**
|
|
6636
6983
|
* Cross-cutting error schemas merged into every procedure's wire error
|
|
6637
6984
|
* union. Lets a plugin interceptor fail with a typed error (e.g.
|
|
@@ -6800,6 +7147,24 @@ export declare interface WebSocketGatewayRoute {
|
|
|
6800
7147
|
readonly onConnection: (connection: WebSocketGatewayConnection) => void | (() => void) | Promise<void | (() => void)>;
|
|
6801
7148
|
}
|
|
6802
7149
|
|
|
7150
|
+
/**
|
|
7151
|
+
* Who may call a procedure over the wire: the app's own clients, or only other
|
|
7152
|
+
* apps of the project.
|
|
7153
|
+
*
|
|
7154
|
+
* A `service:` procedure IS on the wire — the server routes it on the same
|
|
7155
|
+
* socket — but it belongs to a different client group. Codegen emits it into
|
|
7156
|
+
* `serviceGroup.generated.ts` (imported by server processes of other apps) and
|
|
7157
|
+
* never into `rpcGroup.generated.ts` (imported by browsers), so no browser
|
|
7158
|
+
* bundle names a machine contract. The server refuses every non-service caller
|
|
7159
|
+
* regardless; keeping it out of the browser group is what keeps it out of the
|
|
7160
|
+
* code people ship.
|
|
7161
|
+
*/
|
|
7162
|
+
export declare type WireAudience = 'client' | 'service';
|
|
7163
|
+
|
|
7164
|
+
export declare const wireAudienceOf: (descriptor: {
|
|
7165
|
+
readonly service?: ServiceSpec | undefined;
|
|
7166
|
+
}) => WireAudience;
|
|
7167
|
+
|
|
6803
7168
|
/**
|
|
6804
7169
|
* The error union a procedure ACTUALLY puts on the wire — `descriptor.error`
|
|
6805
7170
|
* plus everything the framework can produce for it before or around the
|
|
@@ -7506,7 +7871,12 @@ export declare const WorkflowRunHandleSchema: Schema.Struct<{
|
|
|
7506
7871
|
status: Schema.Literal<["running", "succeeded", "queued", "dropped", "skipped", "replayed"]>;
|
|
7507
7872
|
deferral: Schema.optional<Schema.Struct<{
|
|
7508
7873
|
/** `debounce` | `batch` | `throttle` | `concurrency` | `paused` |
|
|
7509
|
-
* `rateLimit` | `singleton`.
|
|
7874
|
+
* `rateLimit` | `singleton` | `delayed` | `transaction` | `unacknowledged`.
|
|
7875
|
+
*
|
|
7876
|
+
* `unacknowledged` — the start was dispatched and no acknowledgement came
|
|
7877
|
+
* back. The engine may have it; recovery retries the dispatch and binds the
|
|
7878
|
+
* receipt (`id`) to whatever run results. It is not a failure: do not
|
|
7879
|
+
* report one to a user, follow the receipt. */
|
|
7510
7880
|
mode: typeof Schema.String;
|
|
7511
7881
|
/** Epoch ms this may first be admitted. Null for a drop or a skip. */
|
|
7512
7882
|
dueAt: Schema.NullOr<typeof Schema.Number>;
|
|
@@ -7694,7 +8064,12 @@ export declare type WorkflowStartDeferral = Schema.Schema.Type<typeof WorkflowSt
|
|
|
7694
8064
|
*/
|
|
7695
8065
|
export declare const WorkflowStartDeferralSchema: Schema.Struct<{
|
|
7696
8066
|
/** `debounce` | `batch` | `throttle` | `concurrency` | `paused` |
|
|
7697
|
-
* `rateLimit` | `singleton`.
|
|
8067
|
+
* `rateLimit` | `singleton` | `delayed` | `transaction` | `unacknowledged`.
|
|
8068
|
+
*
|
|
8069
|
+
* `unacknowledged` — the start was dispatched and no acknowledgement came
|
|
8070
|
+
* back. The engine may have it; recovery retries the dispatch and binds the
|
|
8071
|
+
* receipt (`id`) to whatever run results. It is not a failure: do not
|
|
8072
|
+
* report one to a user, follow the receipt. */
|
|
7698
8073
|
mode: typeof Schema.String;
|
|
7699
8074
|
/** Epoch ms this may first be admitted. Null for a drop or a skip. */
|
|
7700
8075
|
dueAt: Schema.NullOr<typeof Schema.Number>;
|
|
@@ -7812,7 +8187,12 @@ export declare const workflowToRpc: <const Name extends string, Payload extends
|
|
|
7812
8187
|
status: Schema.Literal<["running", "succeeded", "queued", "dropped", "skipped", "replayed"]>;
|
|
7813
8188
|
deferral: Schema.optional<Schema.Struct<{
|
|
7814
8189
|
/** `debounce` | `batch` | `throttle` | `concurrency` | `paused` |
|
|
7815
|
-
* `rateLimit` | `singleton`.
|
|
8190
|
+
* `rateLimit` | `singleton` | `delayed` | `transaction` | `unacknowledged`.
|
|
8191
|
+
*
|
|
8192
|
+
* `unacknowledged` — the start was dispatched and no acknowledgement came
|
|
8193
|
+
* back. The engine may have it; recovery retries the dispatch and binds the
|
|
8194
|
+
* receipt (`id`) to whatever run results. It is not a failure: do not
|
|
8195
|
+
* report one to a user, follow the receipt. */
|
|
7816
8196
|
mode: typeof Schema.String;
|
|
7817
8197
|
/** Epoch ms this may first be admitted. Null for a drop or a skip. */
|
|
7818
8198
|
dueAt: Schema.NullOr<typeof Schema.Number>;
|