@voltro/protocol 0.80.0 → 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 +90 -0
- package/dist/apikey.d.ts +8 -0
- package/dist/index.d.ts +342 -11
- package/dist/index.js +590 -557
- package/dist/jwt.d.ts +8 -0
- package/dist/rest.d.ts +81 -16
- package/dist/rest.js +228 -219
- 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). */
|
|
@@ -2326,6 +2418,10 @@ export declare const firstEventValue: (tag: string, value: unknown, label: strin
|
|
|
2326
2418
|
/** A route rendered for humans — logs, the inspect surface, the dashboard. */
|
|
2327
2419
|
export declare const formatEventRoute: (route: string) => string;
|
|
2328
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
|
+
|
|
2329
2425
|
/** Close code a gateway connection receives when its credential expires —
|
|
2330
2426
|
* the same session-expiry contract the rpc socket has (SEC-16), spelled as
|
|
2331
2427
|
* an application close code so foreign clients can reauth + reconnect. */
|
|
@@ -2448,6 +2544,9 @@ export declare const hasEnforcedGuard: (guards: DeclaredAccess | undefined) => b
|
|
|
2448
2544
|
* the RAW subject scopes — use `hasEffectiveScope` to include rbac roles. */
|
|
2449
2545
|
export declare const hasScope: (subject: Subject, scope: string) => boolean;
|
|
2450
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
|
+
|
|
2451
2550
|
/**
|
|
2452
2551
|
* Short-circuit response shape. Returning a `HttpInterceptResponse`
|
|
2453
2552
|
* from the interceptor halts the pipeline — the framework sends the
|
|
@@ -2547,14 +2646,24 @@ export declare interface IdempotencyStore {
|
|
|
2547
2646
|
/** Current record for (scope, key), or null. Read-side (inspect/dashboard). */
|
|
2548
2647
|
readonly get: (scope: string, key: string) => Promise<IdempotencyRecord | null>;
|
|
2549
2648
|
/**
|
|
2550
|
-
* 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:
|
|
2551
2652
|
* write a fresh `in_flight` row and return `'claimed'`. Otherwise return the
|
|
2552
2653
|
* EXISTING (still-fresh) record. The atomicity here is the whole game — two
|
|
2553
2654
|
* concurrent same-key requests both hit this, exactly one gets `'claimed'`,
|
|
2554
2655
|
* the loser reads the winner's record. In SQL this is one
|
|
2555
2656
|
* `INSERT … ON CONFLICT DO UPDATE … WHERE existing.createdAt < now - ttl`.
|
|
2556
2657
|
*/
|
|
2557
|
-
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>;
|
|
2558
2667
|
/** Flip the claim to `completed` and store the response. */
|
|
2559
2668
|
readonly complete: (scope: string, key: string, response: IdempotencyResponse, now: number) => Promise<void>;
|
|
2560
2669
|
/** Drop the claim (handler errored → a retry may re-process). */
|
|
@@ -2637,12 +2746,21 @@ export declare interface InsertTarget<Input = unknown, Row = unknown, Item = Rec
|
|
|
2637
2746
|
readonly shapeItem?: ((input: Input, optimisticId: string) => Item) | undefined;
|
|
2638
2747
|
}
|
|
2639
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
|
+
|
|
2640
2752
|
/** Runtime narrowing for the identity variant. */
|
|
2641
2753
|
export declare const isAuthenticatedCheck: (g: AnyCheckSpec) => g is AuthenticatedCheckSpec;
|
|
2642
2754
|
|
|
2643
2755
|
/** Narrow a guard entry to the identity variant. */
|
|
2644
2756
|
export declare const isAuthenticatedGuard: <I>(g: AnyGuardSpec<I>) => g is AuthenticatedGuardSpec;
|
|
2645
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
|
+
|
|
2646
2764
|
export declare const isEventDescriptor: (value: unknown) => value is AnyEventDescriptor;
|
|
2647
2765
|
|
|
2648
2766
|
/** True when every row in the set carries an `id`. The dispatcher uses
|
|
@@ -2651,6 +2769,17 @@ export declare const isEventDescriptor: (value: unknown) => value is AnyEventDes
|
|
|
2651
2769
|
* diffed by id and falls back to shipping the full data. */
|
|
2652
2770
|
export declare const isIdKeyed: (rows: ReadonlyArray<Readonly<Record<string, unknown>>>) => rows is ReadonlyArray<PatchRow>;
|
|
2653
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
|
+
|
|
2654
2783
|
/** Runtime narrowing for the declared-open variant. */
|
|
2655
2784
|
export declare const isOpenAccess: (g: AnyCheckSpec) => g is OpenAccessSpec;
|
|
2656
2785
|
|
|
@@ -2676,6 +2805,20 @@ export declare const isReactivityChannel: (value: unknown) => value is Reactivit
|
|
|
2676
2805
|
*/
|
|
2677
2806
|
export declare const isReactivityChannelKey: (key: string) => boolean;
|
|
2678
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
|
+
|
|
2679
2822
|
export declare const isSystemSubject: (subject: Subject) => boolean;
|
|
2680
2823
|
|
|
2681
2824
|
export declare const isTenantSelectable: (subject: Subject) => subject is TenantSelectableSubject;
|
|
@@ -2807,6 +2950,9 @@ export declare interface MutationProcedureDescriptor<Name extends string, Input
|
|
|
2807
2950
|
* `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
|
|
2808
2951
|
* the only two shapes `security.defaultDeny` accepts. */
|
|
2809
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;
|
|
2810
2956
|
/** Opt this mutation into a public REST endpoint (innovation/11). */
|
|
2811
2957
|
readonly publicApi: PublicApiSpec | undefined;
|
|
2812
2958
|
/** Opt this mutation into the auto-synthesized agent toolset (innovation/07). */
|
|
@@ -3452,11 +3598,17 @@ export declare interface PluginErrorSchema {
|
|
|
3452
3598
|
|
|
3453
3599
|
/** A public raw-HTTP route a plugin serves on the framework listener. */
|
|
3454
3600
|
export declare interface PluginHttpRoute {
|
|
3455
|
-
/** HTTP method, or `'*'` for any (the handler decides).
|
|
3456
|
-
*
|
|
3457
|
-
*
|
|
3458
|
-
*
|
|
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. */
|
|
3459
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;
|
|
3460
3612
|
/** Absolute path, e.g. `/_voltro/storage`. Matched byte for byte: the
|
|
3461
3613
|
* router folds case and a trailing slash to FIND the route, the dispatcher
|
|
3462
3614
|
* then asks whether the request's path is one the route declared — so
|
|
@@ -4512,6 +4664,9 @@ export declare interface QueryProcedureDescriptor<Name extends string, Input ext
|
|
|
4512
4664
|
* `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
|
|
4513
4665
|
* the only two shapes `security.defaultDeny` accepts. */
|
|
4514
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;
|
|
4515
4670
|
/** Opt this query into a public REST endpoint (innovation/11). */
|
|
4516
4671
|
readonly publicApi: PublicApiSpec | undefined;
|
|
4517
4672
|
/** Opt this query into the auto-synthesized agent toolset (innovation/07). */
|
|
@@ -5405,6 +5560,118 @@ declare const ServerOnlyColumnWrite_base: Schema.TaggedErrorClass<ServerOnlyColu
|
|
|
5405
5560
|
columns: Schema.Array$<typeof Schema.String>;
|
|
5406
5561
|
}>;
|
|
5407
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
|
+
|
|
5408
5675
|
/**
|
|
5409
5676
|
* Publish the caller's fully-resolved scope set for this request (raw subject
|
|
5410
5677
|
* scopes ∪ role-derived scopes ∪ any extra grants). Called by the rbac
|
|
@@ -5617,6 +5884,14 @@ export declare const Subject: Schema.Union<[Schema.Struct<{
|
|
|
5617
5884
|
tenantId: typeof Schema.Null;
|
|
5618
5885
|
scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
|
|
5619
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>>;
|
|
5620
5895
|
}>]>;
|
|
5621
5896
|
|
|
5622
5897
|
export declare type Subject = typeof Subject.Type;
|
|
@@ -5670,6 +5945,12 @@ export declare const SubjectIdentity: Schema.Union<[Schema.Struct<{
|
|
|
5670
5945
|
id: typeof Schema.String;
|
|
5671
5946
|
tenantId: typeof Schema.Null;
|
|
5672
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;
|
|
5673
5954
|
}>]>;
|
|
5674
5955
|
|
|
5675
5956
|
export declare type SubjectIdentity = typeof SubjectIdentity.Type;
|
|
@@ -5742,6 +6023,15 @@ declare const SubjectService_base: Context.TagClass<SubjectService, "@voltro/Sub
|
|
|
5742
6023
|
readonly metadata?: {
|
|
5743
6024
|
readonly [x: string]: unknown;
|
|
5744
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;
|
|
5745
6035
|
}>;
|
|
5746
6036
|
|
|
5747
6037
|
/** Subscribe to server-primitive errors. Returns an unsubscribe fn. */
|
|
@@ -6666,6 +6956,29 @@ export declare interface VoltroPlugin {
|
|
|
6666
6956
|
* specific permission name in the error.
|
|
6667
6957
|
*/
|
|
6668
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;
|
|
6669
6982
|
/**
|
|
6670
6983
|
* Cross-cutting error schemas merged into every procedure's wire error
|
|
6671
6984
|
* union. Lets a plugin interceptor fail with a typed error (e.g.
|
|
@@ -6834,6 +7147,24 @@ export declare interface WebSocketGatewayRoute {
|
|
|
6834
7147
|
readonly onConnection: (connection: WebSocketGatewayConnection) => void | (() => void) | Promise<void | (() => void)>;
|
|
6835
7148
|
}
|
|
6836
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
|
+
|
|
6837
7168
|
/**
|
|
6838
7169
|
* The error union a procedure ACTUALLY puts on the wire — `descriptor.error`
|
|
6839
7170
|
* plus everything the framework can produce for it before or around the
|