@voltro/runtime 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 +247 -47
- package/dist/index.js +2578 -2334
- package/package.json +7 -7
package/dist/index.d.ts
CHANGED
|
@@ -33,10 +33,12 @@ import { EventResumePoint } from '@voltro/protocol';
|
|
|
33
33
|
import { EventStreamEvent } from '@voltro/protocol';
|
|
34
34
|
import { extractDbCause } from '@voltro/database';
|
|
35
35
|
import { Fiber } from 'effect';
|
|
36
|
+
import { FieldBinding } from '@voltro/database';
|
|
36
37
|
import { FieldCipher } from '@voltro/database';
|
|
37
38
|
import * as http from 'node:http';
|
|
38
39
|
import { HttpClient } from '@effect/platform';
|
|
39
40
|
import { HttpRequestInterceptor } from '@voltro/protocol';
|
|
41
|
+
import { HttpRouteObserver } from '@voltro/protocol';
|
|
40
42
|
import { IdempotencyStore } from '@voltro/protocol';
|
|
41
43
|
import { IdScheme } from '@voltro/database';
|
|
42
44
|
import { InMemoryMetricExporter } from '@opentelemetry/sdk-metrics';
|
|
@@ -55,6 +57,7 @@ import { listRetentions } from '@voltro/database';
|
|
|
55
57
|
import { Metric } from 'effect';
|
|
56
58
|
import { MetricBoundaries } from 'effect';
|
|
57
59
|
import { MetricReader } from '@opentelemetry/sdk-metrics';
|
|
60
|
+
import { NoRowFound } from '@voltro/protocol';
|
|
58
61
|
import { PendingApproval } from '@voltro/protocol';
|
|
59
62
|
import { PluginHttpRoute } from '@voltro/protocol';
|
|
60
63
|
import { PluginRefOrphanPolicy } from '@voltro/database';
|
|
@@ -102,6 +105,7 @@ import { Tracer } from 'effect';
|
|
|
102
105
|
import { Unauthenticated } from '@voltro/protocol';
|
|
103
106
|
import { ValidationError } from '@voltro/protocol';
|
|
104
107
|
import { ValidationErrors } from '@voltro/protocol';
|
|
108
|
+
import { VersionConflict } from '@voltro/database';
|
|
105
109
|
import { WebSocketGatewayRoute } from '@voltro/protocol';
|
|
106
110
|
import { WorkflowParentClosePolicy } from '@voltro/protocol';
|
|
107
111
|
import { WorkflowRunHandle } from '@voltro/protocol';
|
|
@@ -1568,6 +1572,15 @@ export declare interface BeginOAuthResult {
|
|
|
1568
1572
|
readonly state: string;
|
|
1569
1573
|
}
|
|
1570
1574
|
|
|
1575
|
+
/** Exported for the backpressure suite — the production caller is `bindSubscription`. */
|
|
1576
|
+
export declare const bindComputedUntyped: (computed: ComputedQuery, dispatcher: Dispatcher, context: RuntimeContext, label: string,
|
|
1577
|
+
/** Tunables override (tests); production reads the process-wide slot. */
|
|
1578
|
+
backpressure?: {
|
|
1579
|
+
readonly maxBufferedBytes: number;
|
|
1580
|
+
readonly overrunAfterMs: number;
|
|
1581
|
+
readonly oversizedEventBytes: number;
|
|
1582
|
+
}) => Stream.Stream<SubscriptionEvent<unknown>, never, never>;
|
|
1583
|
+
|
|
1571
1584
|
/**
|
|
1572
1585
|
* Present a new credential on an ALREADY-OPEN connection.
|
|
1573
1586
|
*
|
|
@@ -2070,6 +2083,27 @@ export declare const collectPluginRefRules: (tables: ReadonlyArray<{
|
|
|
2070
2083
|
}>;
|
|
2071
2084
|
}>, registered: ReadonlySet<string>) => ReadonlyArray<PluginRefRule>;
|
|
2072
2085
|
|
|
2086
|
+
/**
|
|
2087
|
+
* The soft-delete rules an app's OWN `reference(…, { onSoftDelete })` columns
|
|
2088
|
+
* declare — app→app, where the hard delete is a real foreign key and only the
|
|
2089
|
+
* soft delete needs the runtime.
|
|
2090
|
+
*
|
|
2091
|
+
* Refuses at boot, naming the column, when `'cascade'` points from a table
|
|
2092
|
+
* that cannot be soft-deleted: the engine would `store.delete` the child, the
|
|
2093
|
+
* middleware would HARD-delete it (no `softDelete()` to turn it into an
|
|
2094
|
+
* update), and a restore of the parent would bring back a parent whose
|
|
2095
|
+
* children are gone for good.
|
|
2096
|
+
*/
|
|
2097
|
+
export declare const collectReferenceSoftDeleteRules: (tables: ReadonlyArray<{
|
|
2098
|
+
readonly name: string;
|
|
2099
|
+
readonly softDeletable?: boolean;
|
|
2100
|
+
readonly fields?: Readonly<Record<string, {
|
|
2101
|
+
readonly type?: string;
|
|
2102
|
+
readonly references?: () => unknown;
|
|
2103
|
+
readonly refOnSoftDelete?: "cascade" | "setNull";
|
|
2104
|
+
}>>;
|
|
2105
|
+
}>) => ReadonlyArray<PluginRefRule>;
|
|
2106
|
+
|
|
2073
2107
|
/** Parse + return the compiled `Cron` for a definition. Throws the
|
|
2074
2108
|
* same way `defineSchedule` validated — callers that already hold a
|
|
2075
2109
|
* branded definition can trust this won't throw. */
|
|
@@ -2916,7 +2950,7 @@ export declare const decideApprovalInvocation: (input: {
|
|
|
2916
2950
|
* the old form is already on production disks, and a fix that needs the data
|
|
2917
2951
|
* rewritten before the app works is an outage with a migration attached.
|
|
2918
2952
|
*/
|
|
2919
|
-
export declare const decryptField: (value: string) => string;
|
|
2953
|
+
export declare const decryptField: (value: string, binding?: FieldBinding) => string;
|
|
2920
2954
|
|
|
2921
2955
|
export declare const DEFAULT_ANALYTICS_MIRROR_TUNABLES: AnalyticsMirrorTunables;
|
|
2922
2956
|
|
|
@@ -2977,6 +3011,9 @@ export declare const DEFAULT_MAX_RPC_BODY_BYTES: number;
|
|
|
2977
3011
|
* definition nothing keeps in step. */
|
|
2978
3012
|
export declare const DEFAULT_MAX_RUNTIME_MS: number;
|
|
2979
3013
|
|
|
3014
|
+
/** Default replay bound — `mutations.replayAttempts` in `app.config.ts` moves it. */
|
|
3015
|
+
export declare const DEFAULT_MUTATION_REPLAY_ATTEMPTS = 3;
|
|
3016
|
+
|
|
2980
3017
|
/** See {@link DrainDeps.claimLeaseMs}. */
|
|
2981
3018
|
export declare const DEFAULT_OUTBOX_CLAIM_LEASE_MS: number;
|
|
2982
3019
|
|
|
@@ -3035,6 +3072,8 @@ export declare const DEFAULT_SOCKET_OVERRUN_AFTER_MS = 10000;
|
|
|
3035
3072
|
|
|
3036
3073
|
export declare const DEFAULT_SOCKET_OVERSIZED_EVENT_BYTES = 262144;
|
|
3037
3074
|
|
|
3075
|
+
export declare const DEFAULT_SOCKET_STALL_WARN_AFTER_MS = 5000;
|
|
3076
|
+
|
|
3038
3077
|
/**
|
|
3039
3078
|
* How old a row may be before a reader should stop trusting it as current.
|
|
3040
3079
|
*
|
|
@@ -3622,6 +3661,27 @@ export declare interface EffectStoreOps {
|
|
|
3622
3661
|
readonly insertIgnore: (table: string, row: Row, options: {
|
|
3623
3662
|
readonly conflictColumns: ReadonlyArray<string>;
|
|
3624
3663
|
}) => Effect.Effect<Row, StoreError>;
|
|
3664
|
+
/**
|
|
3665
|
+
* The single-row reads, on the typed channel — the same three `ctx.store`
|
|
3666
|
+
* offers, taking a builder or a descriptor.
|
|
3667
|
+
*
|
|
3668
|
+
* Their absence made two doctor rules mutually unsatisfiable: `row-not-found`
|
|
3669
|
+
* says reach for `.maybeOne(builder)`, `store-effect-lift` says reach for
|
|
3670
|
+
* `yield* EffectStore`, and `EffectStoreOps` had no `.maybeOne`. An app
|
|
3671
|
+
* following both got `Property 'maybeOne' does not exist on type
|
|
3672
|
+
* 'EffectStoreOps'`. Nothing was hard about it: the object this service
|
|
3673
|
+
* closes over has always been the full `FluentStore` — only the declared type
|
|
3674
|
+
* was narrower, and every call site was already casting to say so.
|
|
3675
|
+
*
|
|
3676
|
+
* `one` fails with `NoRowFound` on its own channel rather than collapsing
|
|
3677
|
+
* into `StoreOperationFailed`, for the reason the catch boundary gives about
|
|
3678
|
+
* `TenantRowNotFound`: stringifying a deliberate, catchable refusal is how it
|
|
3679
|
+
* stops being one. There is no `onMissing` here — `Effect.catchTag` says the
|
|
3680
|
+
* same thing and keeps the failure channel exact.
|
|
3681
|
+
*/
|
|
3682
|
+
readonly one: <R = Row>(query: QueryLike<R>) => Effect.Effect<R, StoreError | NoRowFound>;
|
|
3683
|
+
readonly maybeOne: <R = Row>(query: QueryLike<R>) => Effect.Effect<R | null, StoreError>;
|
|
3684
|
+
readonly first: <R = Row>(query: QueryLike<R>) => Effect.Effect<R | null, StoreError>;
|
|
3625
3685
|
/** Bypass soft-delete. See `MutationStore.hardDelete`. */
|
|
3626
3686
|
readonly hardDelete: (table: string, primaryKey: string) => Effect.Effect<boolean, StoreError>;
|
|
3627
3687
|
/** Run one Effect program atomically against the transaction-scoped store.
|
|
@@ -3650,7 +3710,7 @@ export declare const enableGraphObservation: () => void;
|
|
|
3650
3710
|
* not: their session rows written here were unreadable through the store, and
|
|
3651
3711
|
* the error blamed the key.
|
|
3652
3712
|
*/
|
|
3653
|
-
export declare const encryptField: (plaintext: string) => string;
|
|
3713
|
+
export declare const encryptField: (plaintext: string, binding?: FieldBinding) => string;
|
|
3654
3714
|
|
|
3655
3715
|
/**
|
|
3656
3716
|
* The dispatch-spine entry point. A no-op for a descriptor with no policy; a
|
|
@@ -3721,7 +3781,11 @@ export declare const evaluateTouchedRules: (params: {
|
|
|
3721
3781
|
readonly subject: unknown;
|
|
3722
3782
|
}) => Promise<void>;
|
|
3723
3783
|
|
|
3724
|
-
|
|
3784
|
+
/** The outbox is generic in the payload: a descriptor subscription carries an
|
|
3785
|
+
* id-keyed row set (and deltas against it), a computed one carries whatever
|
|
3786
|
+
* its handler returned (snapshots only). Coalescing differs only in whether a
|
|
3787
|
+
* patch can be diffed — everything else is the same policy. */
|
|
3788
|
+
declare type Event_2<T> = SubscriptionEvent<T>;
|
|
3725
3789
|
|
|
3726
3790
|
/**
|
|
3727
3791
|
* How many deliveries one client may fall behind before the oldest are dropped.
|
|
@@ -4744,8 +4808,14 @@ export declare interface FluentStore extends Omit<MutationStore, 'update' | 'del
|
|
|
4744
4808
|
* Scoping is identical to `query()` (tenant + soft-delete apply), because it
|
|
4745
4809
|
* IS `query()` underneath — the probe just asks for one row more than it
|
|
4746
4810
|
* needs so "the first of several" cannot masquerade as "the one you meant".
|
|
4811
|
+
*
|
|
4812
|
+
* `onMissing` turns the refusal into YOUR typed error: it receives the same
|
|
4813
|
+
* `{ table, found }` `NoRowFound` carries and whatever it returns is thrown
|
|
4814
|
+
* instead — `ctx.store.one(q, () => new NotFoundError({ resource: 'user', id }))`.
|
|
4815
|
+
* A contract that already declares its not-found error keeps it, without a
|
|
4816
|
+
* `maybeOne` + `if (row === null)` hop at every call site.
|
|
4747
4817
|
*/
|
|
4748
|
-
one<R = Row_7>(query: QueryLike<R
|
|
4818
|
+
one<R = Row_7>(query: QueryLike<R>, onMissing?: OnMissingRow): Promise<R>;
|
|
4749
4819
|
/** Terminal: the first matching row or `null`, TYPED. Use when "any match" is
|
|
4750
4820
|
* genuinely what you mean. */
|
|
4751
4821
|
first<R = Row_7>(query: QueryLike<R>): Promise<R | null>;
|
|
@@ -5375,6 +5445,8 @@ export declare interface IssueInput {
|
|
|
5375
5445
|
readonly metadata?: Readonly<Record<string, unknown>> | null;
|
|
5376
5446
|
}
|
|
5377
5447
|
|
|
5448
|
+
export declare const isTransactionReadConflict: (err: unknown) => err is TransactionReadConflict;
|
|
5449
|
+
|
|
5378
5450
|
/** Is `address` one of the peers we are willing to take a forwarded chain from? */
|
|
5379
5451
|
export declare const isTrustedProxy: (address: string | undefined, config: TrustedProxyConfig) => boolean;
|
|
5380
5452
|
|
|
@@ -5629,12 +5701,17 @@ export declare const makeAdvisoryLockCoordinator: (store: DataStore, replicaId:
|
|
|
5629
5701
|
log?: SchedulerLogger) => Coordinator;
|
|
5630
5702
|
|
|
5631
5703
|
/**
|
|
5632
|
-
* Build an AES-256-GCM cipher.
|
|
5633
|
-
*
|
|
5634
|
-
*
|
|
5635
|
-
*
|
|
5636
|
-
*
|
|
5637
|
-
*
|
|
5704
|
+
* Build an AES-256-GCM cipher. Two envelopes, one key:
|
|
5705
|
+
*
|
|
5706
|
+
* `enc:v1:<ivB64>:<tagB64>:<ctB64>` unbound — no binding given
|
|
5707
|
+
* `enc:v2:<r|c>:<ivB64>:<tagB64>:<ctB64>` bound — the GCM tag also covers
|
|
5708
|
+
* `binding.row` (`r`) or
|
|
5709
|
+
* `binding.column` (`c`)
|
|
5710
|
+
*
|
|
5711
|
+
* A random 12-byte IV per call (the same plaintext encrypts differently each
|
|
5712
|
+
* time), plus the GCM auth tag for tamper detection. `decrypt` throws on a
|
|
5713
|
+
* tampered / wrong-key value AND on a bound value read at another address —
|
|
5714
|
+
* both surface as a read error, never silent corruption.
|
|
5638
5715
|
*/
|
|
5639
5716
|
export declare const makeAesCipher: (key: Buffer) => FieldCipher;
|
|
5640
5717
|
|
|
@@ -5786,7 +5863,7 @@ export declare const makeDisconnectConnectionExecutor: (deps: ConnectionBuiltinD
|
|
|
5786
5863
|
* dev.ts (CLI) and any standalone test setup uses this to wire up the
|
|
5787
5864
|
* tag before running the user's Effect.
|
|
5788
5865
|
*/
|
|
5789
|
-
export declare const makeEffectStoreLayer: (store:
|
|
5866
|
+
export declare const makeEffectStoreLayer: (store: FluentStore) => Layer.Layer<EffectStore>;
|
|
5790
5867
|
|
|
5791
5868
|
export declare const makeEventPublisher: (ctx: EventPublishContext) => EventPublisher;
|
|
5792
5869
|
|
|
@@ -5983,7 +6060,7 @@ export declare const makeSubmitTokenExecutor: (deps: ConnectionBuiltinDeps) => (
|
|
|
5983
6060
|
readonly ok: boolean;
|
|
5984
6061
|
}>;
|
|
5985
6062
|
|
|
5986
|
-
export declare const makeSubscriptionOutbox: (options: SubscriptionBackpressureOptions) => SubscriptionOutbox
|
|
6063
|
+
export declare const makeSubscriptionOutbox: <T = ReadonlyArray<Row>>(options: SubscriptionBackpressureOptions) => SubscriptionOutbox<T>;
|
|
5987
6064
|
|
|
5988
6065
|
/**
|
|
5989
6066
|
* The factory the serve entrypoints inject into `makeMutationRunner`'s
|
|
@@ -6024,6 +6101,34 @@ export declare const makeWorkflowLayers: <Context>(workflows: ReadonlyArray<Disc
|
|
|
6024
6101
|
|
|
6025
6102
|
export declare const makeWorkflowRunHandle: (workflowName: string, executionId: string) => WorkflowRunHandle;
|
|
6026
6103
|
|
|
6104
|
+
/**
|
|
6105
|
+
* Build the shared WORKFLOW START runner — the third member of the family,
|
|
6106
|
+
* beside `makeMutationRunner` and `makeActionRunner`, for the unary rpc a
|
|
6107
|
+
* `*.workflow.tsx` becomes on the wire.
|
|
6108
|
+
*
|
|
6109
|
+
* It existed on neither boot path. Both bound the start handler straight to
|
|
6110
|
+
* the workflow facade, outside every plugin interceptor and with no guard
|
|
6111
|
+
* check — measured with an anonymous socket: a query declaring
|
|
6112
|
+
* `guards: [{ scope }]` answered `ScopeError`, the workflow beside it answered
|
|
6113
|
+
* a run handle. So the three things this runner does are the three things a
|
|
6114
|
+
* start never had:
|
|
6115
|
+
*
|
|
6116
|
+
* 1. `enforceGuards` — the workflow's `guards:` / `openAccess:` decision,
|
|
6117
|
+
* BEFORE the run row is written or the engine is asked for anything.
|
|
6118
|
+
* Under `defaultDeny` an undecided workflow is refused per request, the
|
|
6119
|
+
* defense-in-depth behind the boot gate exactly as for a mutation.
|
|
6120
|
+
* 2. the plugin `interceptWorkflow` chain — audit records the start, rate
|
|
6121
|
+
* limiting counts it, rbac publishes the caller's role scopes so (1) can
|
|
6122
|
+
* see them. Without the chain, a role-derived scope guard could never pass.
|
|
6123
|
+
* 3. a metric sample under `workflow.<name>`, so the rpc panel shows starts
|
|
6124
|
+
* beside mutations instead of a surface that does not exist there.
|
|
6125
|
+
*
|
|
6126
|
+
* `start` is injected rather than the facade: the two boot paths build their
|
|
6127
|
+
* caller-context facade differently, and this runner owns the composition,
|
|
6128
|
+
* not the engine.
|
|
6129
|
+
*/
|
|
6130
|
+
export declare const makeWorkflowStartRunner: (deps: WorkflowStartRunnerDeps) => (workflow: WorkflowStartLike, input: unknown, requestContext: ServeRequestContext, start: () => Promise<unknown>) => Promise<unknown>;
|
|
6131
|
+
|
|
6027
6132
|
/**
|
|
6028
6133
|
* Mark a wakeup as handled. The row is kept (not deleted) so a crash
|
|
6029
6134
|
* between firing and the producer re-registering the next occurrence is
|
|
@@ -6286,6 +6391,9 @@ export declare type MetricsMode = 'off' | 'console' | 'otlp' | 'memory';
|
|
|
6286
6391
|
*/
|
|
6287
6392
|
export declare type MirrorVersion = number;
|
|
6288
6393
|
|
|
6394
|
+
/** The error `.one()` throws: the factory's, or the framework's `NoRowFound`. */
|
|
6395
|
+
export declare const missingRowError: (info: NoRowFoundInfo, onMissing: OnMissingRow | undefined) => unknown;
|
|
6396
|
+
|
|
6289
6397
|
export declare interface MutationLifecycle {
|
|
6290
6398
|
readonly afterCommit: (work: () => Promise<unknown>) => void;
|
|
6291
6399
|
}
|
|
@@ -6325,6 +6433,13 @@ export declare interface MutationOutputCodec<Output> {
|
|
|
6325
6433
|
|
|
6326
6434
|
export declare interface MutationRunnerDeps {
|
|
6327
6435
|
readonly store: TransactionalStore;
|
|
6436
|
+
/**
|
|
6437
|
+
* How many times a mutation's transaction runs before a replayable failure
|
|
6438
|
+
* (contention, a transaction-read conflict) surfaces. Resolved by the CLI
|
|
6439
|
+
* from `app.config.ts` `mutations.replayAttempts` (env
|
|
6440
|
+
* `VOLTRO_MUTATION_REPLAY_ATTEMPTS` wins); absent → the default.
|
|
6441
|
+
*/
|
|
6442
|
+
readonly replayAttempts?: number;
|
|
6328
6443
|
/**
|
|
6329
6444
|
* `security.defaultDeny`, threaded to the dispatch spine: `true` refuses a
|
|
6330
6445
|
* descriptor with NO access decision per-request (typed `ScopeError`, the
|
|
@@ -6490,24 +6605,14 @@ export declare const normalizeIp: (raw: string) => string | undefined;
|
|
|
6490
6605
|
* never accidentally compare equal to a real one. */
|
|
6491
6606
|
export declare const normalizeOrigin: (raw: string) => string;
|
|
6492
6607
|
|
|
6493
|
-
|
|
6494
|
-
* `.one()` matched a number of rows other than exactly one.
|
|
6495
|
-
*
|
|
6496
|
-
* `found` distinguishes the two failure modes without a second error type:
|
|
6497
|
-
* `0` is "the row you required is missing", `2` is "your filter is not as
|
|
6498
|
-
* unique as you assumed" (the terminal probes with LIMIT 2, so `2` means
|
|
6499
|
-
* "at least two" — it never counts the whole set just to report a number).
|
|
6500
|
-
*/
|
|
6501
|
-
export declare class NoRowFound extends NoRowFound_base {
|
|
6502
|
-
get message(): string;
|
|
6503
|
-
}
|
|
6608
|
+
export { NoRowFound }
|
|
6504
6609
|
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6508
|
-
|
|
6509
|
-
found:
|
|
6510
|
-
}
|
|
6610
|
+
/** What `.one()` hands an `onMissing` factory — the two facts `NoRowFound` carries. */
|
|
6611
|
+
export declare interface NoRowFoundInfo {
|
|
6612
|
+
readonly table: string;
|
|
6613
|
+
/** `0` — no row; `2` — at least two, the filter is not unique. */
|
|
6614
|
+
readonly found: number;
|
|
6615
|
+
}
|
|
6511
6616
|
|
|
6512
6617
|
/** Note one read against the in-flight procedure. No scope → nothing happens. */
|
|
6513
6618
|
export declare const noteRead: (descriptor: QueryDescriptor) => void;
|
|
@@ -6621,6 +6726,14 @@ export declare interface OneShotQueryRunnerDeps<D> extends QueryProducerDeps<D>
|
|
|
6621
6726
|
readonly queryRows: (descriptor: D, context: ServeRequestContext) => Promise<ReadonlyArray<unknown>>;
|
|
6622
6727
|
}
|
|
6623
6728
|
|
|
6729
|
+
/**
|
|
6730
|
+
* Turn `.one()`'s refusal into the caller's own typed error. Whatever it
|
|
6731
|
+
* returns is THROWN — so a contract that declares `NotFoundError({ resource,
|
|
6732
|
+
* id })` keeps declaring it, and the call site is `one(q, () => new
|
|
6733
|
+
* NotFoundError(...))` instead of `maybeOne` + a null branch at every read.
|
|
6734
|
+
*/
|
|
6735
|
+
export declare type OnMissingRow = (info: NoRowFoundInfo) => unknown;
|
|
6736
|
+
|
|
6624
6737
|
/**
|
|
6625
6738
|
* Register teardown to run on SIGINT/SIGTERM, before the launches are drained
|
|
6626
6739
|
* and the process exits. Returns a de-register function.
|
|
@@ -6814,6 +6927,12 @@ export declare interface OutboxHandlerDefinition {
|
|
|
6814
6927
|
readonly maxAttempts?: number;
|
|
6815
6928
|
}
|
|
6816
6929
|
|
|
6930
|
+
/** What the pump hands events to — the `emit` of a `Stream.asyncScoped`. */
|
|
6931
|
+
export declare interface OutboxSink<T> {
|
|
6932
|
+
readonly single: (event: Event_2<T>) => Promise<unknown>;
|
|
6933
|
+
readonly end: () => unknown;
|
|
6934
|
+
}
|
|
6935
|
+
|
|
6817
6936
|
export declare type OutboxStatus = 'pending' | 'delivering' | 'delivered' | 'dead';
|
|
6818
6937
|
|
|
6819
6938
|
export declare const outboxTable: TableLike;
|
|
@@ -6924,6 +7043,13 @@ export declare interface PluginRefRule {
|
|
|
6924
7043
|
readonly targetTable: string;
|
|
6925
7044
|
readonly policy: PluginRefOrphanPolicy;
|
|
6926
7045
|
readonly onSoftDelete: boolean;
|
|
7046
|
+
/**
|
|
7047
|
+
* Fire on a HARD delete of the target. Default `true` — a `pluginRef` has no
|
|
7048
|
+
* foreign key, so the runtime is the only thing that can clean up. `false`
|
|
7049
|
+
* for a `reference(…, { onSoftDelete })` rule: its hard delete IS a foreign
|
|
7050
|
+
* key, and the database already did whatever `onDelete` says.
|
|
7051
|
+
*/
|
|
7052
|
+
readonly onHardDelete?: boolean;
|
|
6927
7053
|
}
|
|
6928
7054
|
|
|
6929
7055
|
/** The store surface the enforcement needs. */
|
|
@@ -7074,6 +7200,36 @@ export declare const publishEvent: <Name extends string, Key extends Schema.Sche
|
|
|
7074
7200
|
*/
|
|
7075
7201
|
export declare const publishServerTracerLayer: Layer.Layer<never, never, never>;
|
|
7076
7202
|
|
|
7203
|
+
export declare interface PumpOptions {
|
|
7204
|
+
/** Override the process-wide tunable (tests). */
|
|
7205
|
+
readonly stallWarnAfterMs?: number;
|
|
7206
|
+
readonly warn?: (message: string, fields: Record<string, unknown>) => void;
|
|
7207
|
+
}
|
|
7208
|
+
|
|
7209
|
+
/**
|
|
7210
|
+
* The pump between an outbox and its stream: hand events over ONE at a time,
|
|
7211
|
+
* awaiting each acceptance — that await is the backpressure signal the outbox
|
|
7212
|
+
* coalesces behind. `take()` resolves null once the outbox closes (scope
|
|
7213
|
+
* teardown, or the terminal overrun error has drained).
|
|
7214
|
+
*
|
|
7215
|
+
* ONE definition for every subscription binder (descriptor, adopted-on-resume,
|
|
7216
|
+
* computed). The three used to carry the same loop inline; a diagnostic added
|
|
7217
|
+
* to one of them would have been absent from the other two.
|
|
7218
|
+
*
|
|
7219
|
+
* **The stall diagnostic.** `@effect/rpc` delivers a stream as `Chunk`
|
|
7220
|
+
* messages and waits for the client's `Ack` of each before writing the next.
|
|
7221
|
+
* The typed client acknowledges by itself; a hand-rolled socket client that
|
|
7222
|
+
* only listens receives exactly one Chunk per subscription — the snapshot — and
|
|
7223
|
+
* every later delivery parks behind the missing Ack. From the outside that is
|
|
7224
|
+
* indistinguishable from "the server never produced an update", and it was
|
|
7225
|
+
* reported as exactly that: the producer, the matcher and the registry all
|
|
7226
|
+
* looked healthy because they were. The stream's buffer is ONE event deep, so
|
|
7227
|
+
* the second delivery a stalled consumer does not accept blocks HERE — and a
|
|
7228
|
+
* delivery the transport has not accepted within `stallWarnAfterMs` is said out
|
|
7229
|
+
* loud, with the cause named, and counted in `voltro_subscription_stalled_total`.
|
|
7230
|
+
*/
|
|
7231
|
+
export declare const pumpOutbox: <T>(outbox: SubscriptionOutbox<T>, sink: OutboxSink<T>, label: string, options?: PumpOptions) => Promise<void>;
|
|
7232
|
+
|
|
7077
7233
|
/**
|
|
7078
7234
|
* The two scopes, composed once, for every path that finalises a descriptor.
|
|
7079
7235
|
*
|
|
@@ -7346,7 +7502,8 @@ export declare interface ReactiveConfigInput {
|
|
|
7346
7502
|
* threshold is closed with a typed `SubscriptionOverrun` (it re-subscribes
|
|
7347
7503
|
* for a fresh snapshot). Resolved by the CLI's `reactiveTunables.ts`; env
|
|
7348
7504
|
* overrides win: `VOLTRO_REACTIVE_MAX_BUFFERED_BYTES` /
|
|
7349
|
-
* `VOLTRO_REACTIVE_OVERRUN_AFTER_MS` / `VOLTRO_REACTIVE_OVERSIZED_EVENT_BYTES
|
|
7505
|
+
* `VOLTRO_REACTIVE_OVERRUN_AFTER_MS` / `VOLTRO_REACTIVE_OVERSIZED_EVENT_BYTES` /
|
|
7506
|
+
* `VOLTRO_REACTIVE_STALL_WARN_AFTER_MS`.
|
|
7350
7507
|
*/
|
|
7351
7508
|
readonly socket?: {
|
|
7352
7509
|
/** Estimated pending bytes per subscription above which a persistently
|
|
@@ -7357,6 +7514,11 @@ export declare interface ReactiveConfigInput {
|
|
|
7357
7514
|
readonly overrunAfterMs?: number;
|
|
7358
7515
|
/** Per-event WARN threshold (telemetry, NOT a cap). Default 256 KiB. */
|
|
7359
7516
|
readonly oversizedEventBytes?: number;
|
|
7517
|
+
/** How long a delivery may sit unaccepted by the transport before the
|
|
7518
|
+
* server says so — a WARN naming the subscription and the missing
|
|
7519
|
+
* `@effect/rpc` `Ack`, plus `voltro_subscription_stalled_total`.
|
|
7520
|
+
* Default 5_000 ms. */
|
|
7521
|
+
readonly stallWarnAfterMs?: number;
|
|
7360
7522
|
};
|
|
7361
7523
|
}
|
|
7362
7524
|
|
|
@@ -7377,6 +7539,9 @@ export declare interface ReactiveSocketTunables {
|
|
|
7377
7539
|
readonly maxBufferedBytes: number;
|
|
7378
7540
|
readonly overrunAfterMs: number;
|
|
7379
7541
|
readonly oversizedEventBytes: number;
|
|
7542
|
+
/** How long a delivery may sit unaccepted by the transport before the pump
|
|
7543
|
+
* says so (WARN + `voltro_subscription_stalled_total`). See `pumpOutbox`. */
|
|
7544
|
+
readonly stallWarnAfterMs: number;
|
|
7380
7545
|
}
|
|
7381
7546
|
|
|
7382
7547
|
/** The resolved tunables, or the defaults when no boot path has set any
|
|
@@ -7574,6 +7739,10 @@ export declare const recordSample: (s: RecordedSample) => void;
|
|
|
7574
7739
|
*/
|
|
7575
7740
|
export declare const recordScheduleRun: (r: ScheduleRunRecord) => void;
|
|
7576
7741
|
|
|
7742
|
+
/** A delivery the pump handed to the stream that the transport did not accept
|
|
7743
|
+
* within the stall threshold — the consumer is not acknowledging chunks. */
|
|
7744
|
+
export declare const recordStalled: (label: string) => void;
|
|
7745
|
+
|
|
7577
7746
|
/** Move the live-subscriptions gauge for `label`'s tag by `delta` (+1 open, -1 close). */
|
|
7578
7747
|
export declare const recordSubscriptionActive: (label: string, delta: number) => void;
|
|
7579
7748
|
|
|
@@ -8833,6 +9002,9 @@ export declare interface RpcServerOptions<Rpcs extends Rpc.Any> {
|
|
|
8833
9002
|
/** Public raw-HTTP routes contributed by plugins (e.g. storage
|
|
8834
9003
|
* byte-serving). Mounted on the same listener as rpc + inspect. */
|
|
8835
9004
|
readonly httpRoutes?: ReadonlyArray< PluginHttpRoute>;
|
|
9005
|
+
/** Called after every plugin HTTP route answers — the composed plugins'
|
|
9006
|
+
* `onHttpRoute` hooks. Fire-and-forget; a throw never reaches the client. */
|
|
9007
|
+
readonly onHttpRoute?: HttpRouteObserver;
|
|
8836
9008
|
/** The app's DataStore, for `PluginHttpRouteRequest.store`. A getter rather
|
|
8837
9009
|
* than a value: routes mount before the store is built, and the two boot
|
|
8838
9010
|
* paths build it on opposite sides of that point. */
|
|
@@ -9070,6 +9242,10 @@ export declare const runWakeLoop: (deps: WakeOrchestratorDeps, intervalMs?: numb
|
|
|
9070
9242
|
*/
|
|
9071
9243
|
export declare const runWithDeadlockRetry: <T>(work: () => Promise<T>, options?: {
|
|
9072
9244
|
readonly delay?: (attempt: number) => Promise<void>;
|
|
9245
|
+
/** How many times the transaction runs in total before the failure
|
|
9246
|
+
* surfaces. Default {@link DEFAULT_MUTATION_REPLAY_ATTEMPTS}; a hot row
|
|
9247
|
+
* with many simultaneous writers may need more — or a row lock. */
|
|
9248
|
+
readonly attempts?: number;
|
|
9073
9249
|
}) => Promise<T>;
|
|
9074
9250
|
|
|
9075
9251
|
/**
|
|
@@ -9671,6 +9847,8 @@ export declare class SelectBuilder {
|
|
|
9671
9847
|
where(...args: ReadonlyArray<unknown>): SelectBuilder;
|
|
9672
9848
|
orderBy(column: string, direction?: 'asc' | 'desc'): SelectBuilder;
|
|
9673
9849
|
limit(n: number): SelectBuilder;
|
|
9850
|
+
/** Lock the matched rows for the rest of the transaction — see `Query.forUpdate`. */
|
|
9851
|
+
forUpdate(): SelectBuilder;
|
|
9674
9852
|
offset(n: number): SelectBuilder;
|
|
9675
9853
|
select(...columns: ReadonlyArray<string>): SelectBuilder;
|
|
9676
9854
|
using(indexName: string): SelectBuilder;
|
|
@@ -9698,8 +9876,10 @@ export declare class SelectBuilder {
|
|
|
9698
9876
|
* the bug would surface far away from its cause. One extra row on the
|
|
9699
9877
|
* wire buys a loud failure at the point the assumption breaks. Use
|
|
9700
9878
|
* `.first()` / `.maybeOne()` when you genuinely want "any match".
|
|
9879
|
+
*
|
|
9880
|
+
* `onMissing` swaps the thrown error for yours — see `OnMissingRow`.
|
|
9701
9881
|
*/
|
|
9702
|
-
one(): Promise<Row>;
|
|
9882
|
+
one(onMissing?: OnMissingRow): Promise<Row>;
|
|
9703
9883
|
/** Terminal: COUNT(*) of matching rows (real aggregate, not a fetch). */
|
|
9704
9884
|
count(): Promise<number>;
|
|
9705
9885
|
/** Terminal: does any row match? Uses a `LIMIT 1` probe. */
|
|
@@ -9719,6 +9899,8 @@ export declare type SerializedGuard = {
|
|
|
9719
9899
|
readonly action: string;
|
|
9720
9900
|
readonly resourceType: string;
|
|
9721
9901
|
readonly resource: string | null;
|
|
9902
|
+
} | {
|
|
9903
|
+
readonly kind: 'authenticated';
|
|
9722
9904
|
};
|
|
9723
9905
|
|
|
9724
9906
|
/**
|
|
@@ -9775,7 +9957,7 @@ export declare const serverOnlyLeaks: (query: AuditableQuery, serverOnlyByTable:
|
|
|
9775
9957
|
/** A plugin interceptor — wraps the base run Effect (Effect-native chain). */
|
|
9776
9958
|
export declare type ServeRpcInterceptor = (base: Effect.Effect<unknown, unknown, never>, meta: {
|
|
9777
9959
|
readonly tag: string;
|
|
9778
|
-
readonly kind: 'mutation' | 'action' | 'query';
|
|
9960
|
+
readonly kind: 'mutation' | 'action' | 'query' | 'workflow';
|
|
9779
9961
|
readonly input: unknown;
|
|
9780
9962
|
readonly subject: unknown;
|
|
9781
9963
|
readonly traceId: string;
|
|
@@ -10328,13 +10510,13 @@ export declare interface SubscriptionDeliveryRecord {
|
|
|
10328
10510
|
readonly rowCount: number;
|
|
10329
10511
|
}
|
|
10330
10512
|
|
|
10331
|
-
export declare interface SubscriptionOutbox {
|
|
10513
|
+
export declare interface SubscriptionOutbox<T = ReadonlyArray<Row>> {
|
|
10332
10514
|
/** SYNC push from the dispatcher's emit callback. */
|
|
10333
|
-
readonly push: (event: Event_2) => void;
|
|
10515
|
+
readonly push: (event: Event_2<T>) => void;
|
|
10334
10516
|
/** Pump side: resolves with the next event to hand into the stream —
|
|
10335
10517
|
* coalesced when more than one push landed since the last take — or null
|
|
10336
10518
|
* once the outbox is closed and drained. */
|
|
10337
|
-
readonly take: () => Promise<Event_2 | null>;
|
|
10519
|
+
readonly take: () => Promise<Event_2<T> | null>;
|
|
10338
10520
|
/** True once the overrun policy fired (the error event has been queued;
|
|
10339
10521
|
* the stream ends after it drains). */
|
|
10340
10522
|
readonly overrun: () => boolean;
|
|
@@ -10436,6 +10618,10 @@ export declare interface SubscriptionSocketMetricsSnapshot {
|
|
|
10436
10618
|
readonly total: number;
|
|
10437
10619
|
readonly byLabel: Readonly<Record<string, number>>;
|
|
10438
10620
|
};
|
|
10621
|
+
readonly stalledTotal: {
|
|
10622
|
+
readonly total: number;
|
|
10623
|
+
readonly byLabel: Readonly<Record<string, number>>;
|
|
10624
|
+
};
|
|
10439
10625
|
}
|
|
10440
10626
|
|
|
10441
10627
|
export declare const subscriptionSocketMetricsSnapshot: () => SubscriptionSocketMetricsSnapshot;
|
|
@@ -10722,11 +10908,19 @@ export declare interface TracingOptions {
|
|
|
10722
10908
|
readonly sampler?: Sampler;
|
|
10723
10909
|
}
|
|
10724
10910
|
|
|
10911
|
+
export declare const TRANSACTION_READ_CONFLICT_NAME = "VoltroTransactionReadConflict";
|
|
10912
|
+
|
|
10725
10913
|
/** The store surface the runner needs — just the transactional wrap. */
|
|
10726
10914
|
export declare interface TransactionalStore {
|
|
10727
10915
|
transactional<T>(work: (tx: unknown) => Promise<T>): Promise<T>;
|
|
10728
10916
|
}
|
|
10729
10917
|
|
|
10918
|
+
export declare class TransactionReadConflict extends Error {
|
|
10919
|
+
readonly conflict: VersionConflict;
|
|
10920
|
+
readonly name = "VoltroTransactionReadConflict";
|
|
10921
|
+
constructor(conflict: VersionConflict);
|
|
10922
|
+
}
|
|
10923
|
+
|
|
10730
10924
|
/**
|
|
10731
10925
|
* Transport-level security policy for the api listener.
|
|
10732
10926
|
*
|
|
@@ -10872,6 +11066,10 @@ export declare type TupleSource = (req: {
|
|
|
10872
11066
|
readonly load: DataLoader | undefined;
|
|
10873
11067
|
}) => Promise<ReadonlyArray<RelationTuple>> | ReadonlyArray<RelationTuple>;
|
|
10874
11068
|
|
|
11069
|
+
/** The mutation runner's key for opening the read-version map on a transaction
|
|
11070
|
+
* view it obtained from the dialect store directly. */
|
|
11071
|
+
export declare const TX_READ_VERSIONS_KEY: unique symbol;
|
|
11072
|
+
|
|
10875
11073
|
/**
|
|
10876
11074
|
* Drop everything this connection accumulated. Called by the WS-close finalizer
|
|
10877
11075
|
* and by explicit sign-out flows. Idempotent.
|
|
@@ -11786,6 +11984,13 @@ export declare class WorkflowSingletonHeldError extends Error {
|
|
|
11786
11984
|
constructor(workflowName: string, executionId: string);
|
|
11787
11985
|
}
|
|
11788
11986
|
|
|
11987
|
+
/** The slice of a `workflow()` definition the start runner reads — its name
|
|
11988
|
+
* and its resolved access decision (`workflowAccessDescriptor` produces it). */
|
|
11989
|
+
export declare interface WorkflowStartLike {
|
|
11990
|
+
readonly name: string;
|
|
11991
|
+
readonly guards?: ReadonlyArray<AnyCheckSpec> | undefined;
|
|
11992
|
+
}
|
|
11993
|
+
|
|
11789
11994
|
export declare interface WorkflowStartOptions {
|
|
11790
11995
|
readonly wait?: boolean;
|
|
11791
11996
|
/**
|
|
@@ -11816,18 +12021,13 @@ export declare interface WorkflowStartOptions {
|
|
|
11816
12021
|
/* Excluded from this release type: admitted */
|
|
11817
12022
|
}
|
|
11818
12023
|
|
|
11819
|
-
export declare
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
dueAt: Schema.NullOr<typeof Schema.Number>;
|
|
11827
|
-
retryAfterMs: Schema.NullOr<typeof Schema.Number>;
|
|
11828
|
-
intentId: Schema.NullOr<typeof Schema.String>;
|
|
11829
|
-
}>>;
|
|
11830
|
-
}>, Schema.Schema.All, never>;
|
|
12024
|
+
export declare interface WorkflowStartRunnerDeps {
|
|
12025
|
+
/** Composed plugin workflow-interceptor (`interceptWorkflow`). */
|
|
12026
|
+
readonly interceptor?: ServeRpcInterceptor;
|
|
12027
|
+
readonly recordMetric?: MutationRunnerDeps['recordMetric'];
|
|
12028
|
+
/** See {@link MutationRunnerDeps.defaultDeny} — same flag, same refusal. */
|
|
12029
|
+
readonly defaultDeny?: boolean;
|
|
12030
|
+
}
|
|
11831
12031
|
|
|
11832
12032
|
export declare interface WorkflowUpdateOptions {
|
|
11833
12033
|
readonly timeoutMs?: number;
|