@voltro/runtime 0.79.1 → 0.80.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 +79 -0
- package/dist/index.d.ts +73 -17
- package/dist/index.js +2031 -1982
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -39,6 +39,85 @@ _Changes staged for the next release accumulate here (rolled up from
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
+
## [0.80.0] — 2026-09-24
|
|
43
|
+
|
|
44
|
+
### ⚠ BREAKING
|
|
45
|
+
|
|
46
|
+
- **The inspect stream's replica selection is decided before the response** — `@voltro/runtime`, `@voltro/voltro`
|
|
47
|
+
|
|
48
|
+
`InspectStream.select` returns `Effect<InspectStreamSelection | undefined>` instead of a `Stream`, where `InspectStreamSelection` is either `{ kind: 'stream', stream }` or `{ kind: 'refused', status, body }`. A stream response is committed as `200` from its first byte, so a refusal can only be expressed before it starts. The option is wired by `voltro dev`, `voltro serve` and `voltro start`; code that passes its own `inspectStream` to `startRpcServer` wraps its stream in `Effect.succeed({ kind: 'stream', stream })`. `voltro update` lists every line that sets `inspectStream`.
|
|
49
|
+
- **`JiraService.listBoards` finds boards by project, name and type** — `@voltro/plugin-atlassian`
|
|
50
|
+
|
|
51
|
+
Every board method of `JiraService` took a board id, so a board picker had nothing to list and people looked ids up in Jira by hand. `listBoards({ projectKeyOrId?, name?, type? })` calls `GET /rest/agile/1.0/board`, follows the pages itself, and returns each board as `{ id, name, type, location? }`, where `location` names the project the board is located in. `name` is a contains match on Jira's side. It runs on the caller's own credentials like every other method, so a board the caller may not see is not listed. The break is for code that provides the service itself: a test stub written against the full `JiraServiceShape` needs the new method.
|
|
52
|
+
|
|
53
|
+
**`voltro update` carries you across this** — codemod `0.80.0/03_jira-service-lists-boards`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.80.0).
|
|
54
|
+
- **A loader has `ctx.query` in the browser, so a clicked-to page keeps its title** — `@voltro/web`, `@voltro/client`, `@voltro/protocol`, `@voltro/cli`
|
|
55
|
+
|
|
56
|
+
`ctx.query` was `undefined` when a loader ran in the browser. A page whose loader resolved an entity's name for `meta` therefore showed it in the title on a fresh load and dropped it after a click inside the app: the same URL, two titles. The browser loader now gets `ctx.query` too. It reads through the page's own api client, so it carries the visitor's session. A query resolves to its first snapshot, as on the server, and an in-band error rejects with the typed error on `voltroError`. A newer navigation that replaces the one in flight now aborts the loader's `signal`, as `LoaderContext.signal` always promised, and that closes the read. What changes for existing code is a check on `query`'s presence: `if (query)`, `query ? … : …` and `query?.(…)` now take the resolving branch in the browser too. Branch on `ctx.isServer` where the check meant "this is the server". `voltro update` lists every such line in your pages and layouts.
|
|
57
|
+
|
|
58
|
+
Bundle size: the browser read path adds 1.6 KB gz to a web app's first load (194.6 → 196.2 KB gz on the framework's zero-procedure fixture). Because the router now imports the api client, the shared chunk that carries the Effect runtime is emitted as `router-*.js` instead of `index-*.js`; the bytes moved between the two files, they did not grow.
|
|
59
|
+
- **No framework loading spinner; useNavigation() exposes navigation state** — `@voltro/web`
|
|
60
|
+
|
|
61
|
+
`FrameworkBoot` mounted a corner `NavigationIndicator` in every web app, in development and production, so a spinner appeared at the bottom right of pages whose design never included one — also in apps that had switched the devtools off. It is no longer mounted, and `NavigationIndicator` is removed from `@voltro/web`. In development the devtools button shows navigation progress as before. To build your own cue, read the new `useNavigation()` hook: `state` is `'loading'` while the router keeps the current page on screen for a target whose page module or loaders are not ready, and `pendingPathname` names that target; a server render is always `'idle'`. For a reconnecting api use `useConnectionStatus(apiName)` from `@voltro/client`. `voltro update` lists every file that still imports `NavigationIndicator`.
|
|
62
|
+
- **DB_MAX_CONNECTIONS is what the whole process holds, not the size of each pool** — `@voltro/cli`, `@voltro/workflow`, `@voltro/database`
|
|
63
|
+
|
|
64
|
+
Every framework user of the database opened its own pool: the store, the plugin bind context, the workflow runtime, aggregates, analytics, and the boot steps that migrate or check the schema. Each was sized by `DB_MAX_CONNECTIONS`, so one process could hold several times the number its boot line printed. Measured on postgres at `DB_MAX_CONNECTIONS=3`: the line promised 4 and the database saw 8 connections from `voltro dev` and from `voltro serve`. Two pods budgeted at 5 against a pooler of 15 both failed to acquire in the same second.
|
|
65
|
+
|
|
66
|
+
The pools were built by dialect-independent code, and the repair is measured the same way on every dialect: under boot and a burst of concurrent requests, postgres, mariadb, mysql and mssql each hold exactly what the line prints.
|
|
67
|
+
|
|
68
|
+
In `voltro dev` and `voltro serve` they now share one pool per database target, so the boot line's number is what the process holds. Short-lived commands such as `voltro data restore` keep their own pool, because they close it to get fresh sessions. A value tuned per pool buys fewer connections in total. Read replicas, a separate migration URL (`DB_DIRECT_URL` / `DB_MIGRATE_URL`), a provisioned tenant's database and pools your own code builds stay separate.
|
|
69
|
+
|
|
70
|
+
The workflow runtime needs 3 connections at once (2 with `VOLTRO_WORKFLOW_RUNNER_STORAGE=memory`). Below that the boot now refuses and names the number; it used to wait out the acquire timeout and exit on `Failed to acquire connection`. `sqlPoolCapacity` is exported from `@voltro/database/sql` beside `registerSqlPoolCapacity`, an addition.
|
|
71
|
+
|
|
72
|
+
**`voltro update` carries you across this** — codemod `0.80.0/01_db-max-connections-is-the-process-budget`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.80.0).
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- **security.clientAddressHeader reads a proxy's single-address header** — `@voltro/runtime`, `@voltro/cli`
|
|
77
|
+
|
|
78
|
+
`security.clientAddressHeader` (env `VOLTRO_CLIENT_ADDRESS_HEADER`) names a header a trusted proxy sets to the client's address, such as `cf-connecting-ip` or `x-real-ip`. It is believed only from a peer covered by `security.trustedProxies` and only when it holds one valid IP; otherwise `x-forwarded-for` applies as before. Naming a header without declaring a trusted proxy refuses the boot.
|
|
79
|
+
- **Rate-limit rules can key on the caller's address** — `@voltro/plugin-ratelimit`, `@voltro/protocol`, `@voltro/runtime`, `@voltro/cli`
|
|
80
|
+
|
|
81
|
+
A rule can now use `by: 'ip'`, one bucket per client address. Before sign-in every caller is the same anonymous subject, so `'subject'` put them all in one bucket and one client could use up a sign-in or invite endpoint for everyone. The address is the one the listener resolves through `security.trustedProxies`, and plugin interceptors receive it as `RpcInterceptorContext.remoteAddr` on WebSocket, `POST /rpc` and `publicApi` REST calls; REST handlers get it as `RestRouteContext.remoteAddr`. A client cannot set it. Calls that do not arrive over the network share one `unknown` bucket. `KeyBy` gains the `'ip'` member: every value that type-checked before still does; only code that exhaustively switches over `KeyBy` needs the new case.
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
|
|
85
|
+
- **Server-side rpc calls carry the visitor's address** — `@voltro/cli`
|
|
86
|
+
|
|
87
|
+
A loader's `ctx.query`, a page's `preload`, and the no-JavaScript form post now send `x-forwarded-for` to the api: the chain the request arrived with plus the address that delivered it to the web server. An api whose `security.trustedProxies` covers the web tier therefore rate-limits and audits these calls per visitor instead of counting every visitor against the web server. An api that trusts no proxy ignores the header, so its behaviour is unchanged. A visitor-written `x-forwarded-for` is never forwarded on its own.
|
|
88
|
+
|
|
89
|
+
### Fixed
|
|
90
|
+
|
|
91
|
+
- **The boot's database connection line names the dialect in use** — `@voltro/cli`
|
|
92
|
+
|
|
93
|
+
A database configured with `DB_HOST` and friends was logged as `db connection: postgres://…` whatever the dialect, so a SQL Server or MySQL connection read as a postgres one. The line now starts with the dialect's name.
|
|
94
|
+
- **The dashboard's live streams share one browser connection** — `@voltro/cli`
|
|
95
|
+
|
|
96
|
+
The DevTools dashboard opened one event-stream connection per app. Over HTTP/1.1 a browser holds at most six connections per origin across all tabs, so with six apps — or three in two tabs — the logs and overview pages held every connection the dashboard had, and each later request waited indefinitely: a click on another page changed the URL, kept the old page on screen and left the navigation indicator spinning. Every stream a page holds now travels on one connection: the page posts its targets, each with its own app bearer, to `/api/dashboard/proxy/streams`, and `voltro dev` and `voltro start` fan the apps in behind it, each reconnecting on its own backoff. `/api/dashboard/proxy/stream` is removed; `@voltro/dashboard` of the same release uses the new route.
|
|
97
|
+
- **`meta` keeps the rendered language after hydration without a locale cookie** — `@voltro/web`, `@voltro/cli`
|
|
98
|
+
|
|
99
|
+
In the browser, `meta` received the `voltro:locale` cookie's locale and otherwise a fixed `'en'`. A first visit carries no cookie, so the server rendered the title in the language it negotiated (`Accept-Language`, else `defaultLocale`), and after hydration the tab switched to English. `meta` now falls back to the locale the server rendered the document with, which it stamps on `<html lang>`, and the cookie still wins when a language switch has written one. A page `voltro serve` answers with the unrendered shell (a page the prerender did not produce) declared `lang="en"` in every app, and the client took that for the server's choice. The shell now declares the app's `defaultLocale`, or its first declared locale.
|
|
100
|
+
- **The first `voltro dev` on a fresh SQL Server database boots with CDC on** — `@voltro/sql-mssql`
|
|
101
|
+
|
|
102
|
+
With `changeStrategy: 'cdc'`, the default, the Change Tracking reader started before auto-migrate created the schema and enabled tracking on tables that did not exist yet. The boot died on `Failed to execute statement`, so every new project on SQL Server failed its first `voltro dev`. The same database booted once with `CDC=0` worked afterwards.
|
|
103
|
+
|
|
104
|
+
The reader now enables tracking on the tables that exist and picks up the rest on the poll that first finds them, reading each from its own tracking floor. A table the migration creates during the boot is tracked from then on. `ensureChangeTracking` resolves to the tables it could not enable yet.
|
|
105
|
+
- **Replica observations of ended processes are deleted after a day** — `@voltro/cli`, `@voltro/runtime`, `@voltro/database`
|
|
106
|
+
|
|
107
|
+
`_voltro_replica_observations` had no retention. A replica deletes its own rows on a graceful shutdown, but a process that crashed or was killed left them behind, and a replica ID that does not survive a restart — `dev-<pid>` under `voltro dev`, a pod replaced under a new name — never overwrites them. Every such row stayed listed as `departed` in each fleet read indefinitely. The retention sweep now deletes rows not rewritten for 24 hours on both boot paths and every dialect; live replicas rewrite theirs every 30 seconds. `VOLTRO_REPLICA_OBSERVATIONS_TTL_HOURS` changes the window, with a floor of one hour, and the boot line lists the policy with the others.
|
|
108
|
+
- **A live stream for an ended replica is refused instead of reconnecting forever** — `@voltro/cli`, `@voltro/devtools-ui`
|
|
109
|
+
|
|
110
|
+
`/_voltro/inspect/stream?replica=<id>` now resolves the named replica and verifies its handshake before it answers, with the same refusals as a read: `404` for an unknown ID or one without a reachable address (no request leaves the process), `504` for a peer that does not accept the connection, `502` for a peer that answers with another process's feed. Previously the API answered `200 text/event-stream` first and found the missing peer inside the body, so a dashboard still naming a process from before a restart received a stream that closed at once, reconnected on backoff indefinitely, and left a `Fiber terminated with an unhandled error` in the target's log on every attempt. The replica selector in the local and Cloud dashboards now also removes a selected replica once the fleet no longer lists it — the process ended and its ID never answers again — names the removed IDs, and returns an emptied selection to All replicas. A replica that is only unavailable (stale, without a reachable address, or expected by the fleet without an answer) stays selected.
|
|
111
|
+
- **A controlled workflow start without an acknowledgement returns its receipt** — `@voltro/runtime`, `@voltro/voltro`, `@voltro/workflow`, `@voltro/cli`, `@voltro/protocol`
|
|
112
|
+
|
|
113
|
+
When the engine dispatch of a start with flow controls failed without a definite answer, such as an exhausted database pool or an acknowledgement lost after the engine took the run, `start()` threw `Submission … remains unacknowledged`. The submission stayed durable and recovery retried it, so a caller that reported the failure could see the run happen anyway.
|
|
114
|
+
|
|
115
|
+
`start()` now returns `status: 'queued'` with `deferral.mode: 'unacknowledged'`, and `handle.id` is the receipt recovery binds to the resulting run. `start(…, { wait: true })` and `run(…)` follow it. A definite rejection still throws.
|
|
116
|
+
|
|
117
|
+
`WorkflowAdmissionOutcome`'s `accept` may now resolve to a `WorkflowQueuedAdmission` as well as an execution ID. Custom admission adapters only produce that outcome; the framework's facade is what calls `accept`, so adapter code keeps compiling. `isUnacknowledgedSubmission` is exported from `@voltro/workflow`.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
42
121
|
## [0.79.1] — 2026-09-24
|
|
43
122
|
|
|
44
123
|
### Changed
|
package/dist/index.d.ts
CHANGED
|
@@ -2245,6 +2245,8 @@ export declare interface ClientAddressInput {
|
|
|
2245
2245
|
readonly socketAddress: string | undefined;
|
|
2246
2246
|
/** Raw `x-forwarded-for` header, if present. */
|
|
2247
2247
|
readonly xForwardedFor: string | undefined;
|
|
2248
|
+
/** Raw value of `config.clientAddressHeader`, if the request carried it. */
|
|
2249
|
+
readonly clientAddressHeader?: string | undefined;
|
|
2248
2250
|
readonly config: TrustedProxyConfig;
|
|
2249
2251
|
}
|
|
2250
2252
|
|
|
@@ -5453,13 +5455,12 @@ export declare const forbiddenColumnsForSubject: (tableName: string, subject: Su
|
|
|
5453
5455
|
export declare const forgetCredential: (store: VaultStore, connectionId: string, subjectId: string) => Promise<boolean>;
|
|
5454
5456
|
|
|
5455
5457
|
/**
|
|
5456
|
-
* Drop rows
|
|
5458
|
+
* Drop this replica's rows on a graceful shutdown.
|
|
5457
5459
|
*
|
|
5458
|
-
*
|
|
5459
|
-
*
|
|
5460
|
-
*
|
|
5461
|
-
*
|
|
5462
|
-
* (a scale-down), whose row would otherwise be counted forever.
|
|
5460
|
+
* A restart under the same replica ID overwrites its row anyway; this handles
|
|
5461
|
+
* a replica that goes away for good (a scale-down). It cannot run for a process
|
|
5462
|
+
* that crashed or was killed — those rows are aged out by the retention sweep
|
|
5463
|
+
* on `writtenAt`, which every live replica refreshes each publish interval.
|
|
5463
5464
|
*/
|
|
5464
5465
|
export declare const forgetReplicaObservations: (store: DataStore, replicaId: string) => Promise<void>;
|
|
5465
5466
|
|
|
@@ -5862,7 +5863,10 @@ export { InspectStore }
|
|
|
5862
5863
|
*/
|
|
5863
5864
|
export declare interface InspectStream {
|
|
5864
5865
|
readonly replicaId?: string;
|
|
5865
|
-
|
|
5866
|
+
/** Undefined serves this process's own feed. Decided before the response
|
|
5867
|
+
* starts: an event stream is a 200 from its first byte, so a refusal found
|
|
5868
|
+
* inside the body could only reach the client as a feed that closes. */
|
|
5869
|
+
readonly select?: (url: string, headers: Readonly<Record<string, string>>) => Effect.Effect<InspectStreamSelection | undefined>;
|
|
5866
5870
|
/** Same shape as the inspect auth resolver — returns ok or reason. */
|
|
5867
5871
|
readonly authorize: (headers: Readonly<Record<string, string>>) => {
|
|
5868
5872
|
readonly ok: true;
|
|
@@ -5884,6 +5888,16 @@ export declare interface InspectStream {
|
|
|
5884
5888
|
readonly keepAliveMs?: number;
|
|
5885
5889
|
}
|
|
5886
5890
|
|
|
5891
|
+
/** A named process's feed, or the reasoned refusal to open it. */
|
|
5892
|
+
export declare type InspectStreamSelection = {
|
|
5893
|
+
readonly kind: 'stream';
|
|
5894
|
+
readonly stream: Stream.Stream<Uint8Array>;
|
|
5895
|
+
} | {
|
|
5896
|
+
readonly kind: 'refused';
|
|
5897
|
+
readonly status: number;
|
|
5898
|
+
readonly body: unknown;
|
|
5899
|
+
};
|
|
5900
|
+
|
|
5887
5901
|
export { inspectWorkflow }
|
|
5888
5902
|
|
|
5889
5903
|
export declare const installApprovalGate: (gate: ApprovalGateApi) => void;
|
|
@@ -9425,6 +9439,14 @@ export declare const resolveReactiveConfig: (config?: ReactiveConfigInput | unde
|
|
|
9425
9439
|
|
|
9426
9440
|
export declare const resolveRedirectUri: (definition: OAuth2ConnectionDefinition, publicUrl: string) => string;
|
|
9427
9441
|
|
|
9442
|
+
/** The client address of a request, read from its headers per `config`. The
|
|
9443
|
+
* one call every listener makes — which headers matter is decided here. */
|
|
9444
|
+
export declare const resolveRequestClientAddress: (input: {
|
|
9445
|
+
readonly socketAddress: string | undefined;
|
|
9446
|
+
readonly headers: Readonly<Record<string, string | undefined>>;
|
|
9447
|
+
readonly config: TrustedProxyConfig;
|
|
9448
|
+
}) => string | undefined;
|
|
9449
|
+
|
|
9428
9450
|
/**
|
|
9429
9451
|
* Whether the request reached the app over TLS.
|
|
9430
9452
|
*
|
|
@@ -9513,7 +9535,7 @@ export declare const resolveTransactionSubject: (subject: Subject, tenantId: str
|
|
|
9513
9535
|
* rebuild; the config field exists because it is a tunable and tunables belong
|
|
9514
9536
|
* in `app.config.ts`.
|
|
9515
9537
|
*/
|
|
9516
|
-
export declare const resolveTrustedProxies: (configured?: ReadonlyArray<string> | undefined, env?: Readonly<Record<string, string | undefined
|
|
9538
|
+
export declare const resolveTrustedProxies: (configured?: ReadonlyArray<string> | undefined, env?: Readonly<Record<string, string | undefined>>, configuredHeader?: string | undefined) => TrustedProxyConfig;
|
|
9517
9539
|
|
|
9518
9540
|
/** Per-resource-type policy. `actions` maps an action to the relations that
|
|
9519
9541
|
* grant it (ANY-of). `implies` is relation implication (owner ⇒ editor ⇒
|
|
@@ -9944,6 +9966,9 @@ export declare interface RowHistoryEntry {
|
|
|
9944
9966
|
readonly traceId?: string | null;
|
|
9945
9967
|
}
|
|
9946
9968
|
|
|
9969
|
+
/** Internal header name. Its value is meaningless without the process mark. */
|
|
9970
|
+
export declare const RPC_CLIENT_ADDRESS_HEADER = "x-voltro-client-address";
|
|
9971
|
+
|
|
9947
9972
|
/** Server-owned lifetime, not a user identity or a client-supplied claim.
|
|
9948
9973
|
* WebSockets retain it across calls; HTTP retains it only within one request.
|
|
9949
9974
|
* Reconnects and server restarts receive a new id. */
|
|
@@ -9951,6 +9976,11 @@ export declare interface RpcConnection {
|
|
|
9951
9976
|
readonly id: string;
|
|
9952
9977
|
readonly transport: 'websocket' | 'http';
|
|
9953
9978
|
readonly signal: AbortSignal;
|
|
9979
|
+
/** The caller's address, resolved by the rpc listener through
|
|
9980
|
+
* `security.trustedProxies` / `clientAddressHeader` — the same value its
|
|
9981
|
+
* per-IP shield counts. Absent when the call did not arrive through that
|
|
9982
|
+
* listener (an internal invocation, an embedder's own transport). */
|
|
9983
|
+
readonly remoteAddr?: string;
|
|
9954
9984
|
}
|
|
9955
9985
|
|
|
9956
9986
|
export declare interface RpcServerOptions<Rpcs extends Rpc.Any> {
|
|
@@ -10250,6 +10280,9 @@ export declare interface RuntimeContext {
|
|
|
10250
10280
|
/** Server-owned transport identity and close signal. Not authentication;
|
|
10251
10281
|
* session membership remains an application's explicit access decision. */
|
|
10252
10282
|
readonly connection?: RpcConnection;
|
|
10283
|
+
/** `connection.remoteAddr`, lifted so the serve pipeline — which also serves
|
|
10284
|
+
* REST calls that have no connection — reads one field for both. */
|
|
10285
|
+
readonly remoteAddr?: string;
|
|
10253
10286
|
}
|
|
10254
10287
|
|
|
10255
10288
|
/**
|
|
@@ -11099,6 +11132,11 @@ export declare interface ServeRequestContext {
|
|
|
11099
11132
|
readonly surface?: 'rest';
|
|
11100
11133
|
/** The descriptor's `publicApi` spec, on a projected call. */
|
|
11101
11134
|
readonly publicApi?: PublicApiSpec;
|
|
11135
|
+
/** The caller's address as the listener resolved it through
|
|
11136
|
+
* `security.trustedProxies` — carried to the plugin interceptor so a
|
|
11137
|
+
* per-endpoint limit can count per client. Absent for calls that did not
|
|
11138
|
+
* arrive over the network. */
|
|
11139
|
+
readonly remoteAddr?: string;
|
|
11102
11140
|
/**
|
|
11103
11141
|
* Caller-side cancellation (the gRPC deadline / client-cancel path).
|
|
11104
11142
|
* Honoured at the ONE place the executor effect runs to a promise, so an
|
|
@@ -11163,6 +11201,8 @@ export declare type ServeRpcInterceptor = (base: Effect.Effect<unknown, unknown,
|
|
|
11163
11201
|
/** See `ServeRequestContext.surface` — present on a REST-projected call. */
|
|
11164
11202
|
readonly surface?: 'rest';
|
|
11165
11203
|
readonly publicApi?: PublicApiSpec;
|
|
11204
|
+
/** See `ServeRequestContext.remoteAddr`. */
|
|
11205
|
+
readonly remoteAddr?: string;
|
|
11166
11206
|
}) => Effect.Effect<unknown, unknown, never>;
|
|
11167
11207
|
|
|
11168
11208
|
/** The published tracer, or `undefined` when tracing is off / no server booted. */
|
|
@@ -12182,6 +12222,12 @@ export declare interface TransportSecurityOptions {
|
|
|
12182
12222
|
* CIDRs, the presets `loopback` / `private`, a hop count (`['2']`), or
|
|
12183
12223
|
* `['*']` to trust any peer. Env override: `VOLTRO_TRUSTED_PROXIES`. */
|
|
12184
12224
|
readonly trustedProxies?: ReadonlyArray<string>;
|
|
12225
|
+
/** A header a trusted proxy SETS to the client's single address
|
|
12226
|
+
* (`'cf-connecting-ip'`, `'x-real-ip'`). Believed only from a peer
|
|
12227
|
+
* `trustedProxies` covers, and only when it holds one valid IP; otherwise
|
|
12228
|
+
* the `x-forwarded-for` chain applies. Env override:
|
|
12229
|
+
* `VOLTRO_CLIENT_ADDRESS_HEADER`. */
|
|
12230
|
+
readonly clientAddressHeader?: string;
|
|
12185
12231
|
/** Security response headers. Env override: `VOLTRO_SECURITY_HEADERS`
|
|
12186
12232
|
* (`off|default|strict`), plus `VOLTRO_CSP` / `VOLTRO_CSP_HTML` /
|
|
12187
12233
|
* `VOLTRO_HSTS`. */
|
|
@@ -12227,6 +12273,10 @@ export declare interface TrustedProxyConfig {
|
|
|
12227
12273
|
readonly trustAll: boolean;
|
|
12228
12274
|
/** Hop count (express's numeric `trust proxy`). `undefined` → not hop mode. */
|
|
12229
12275
|
readonly hops?: number;
|
|
12276
|
+
/** Lower-cased name of a single-address header a trusted proxy SETS
|
|
12277
|
+
* (`cf-connecting-ip`, `x-real-ip`). Preferred over the forwarded chain when
|
|
12278
|
+
* it holds one valid IP; `x-forwarded-for` remains the fallback. */
|
|
12279
|
+
readonly clientAddressHeader?: string;
|
|
12230
12280
|
}
|
|
12231
12281
|
|
|
12232
12282
|
/**
|
|
@@ -12806,15 +12856,12 @@ export declare type WorkflowAdmissionOutcome = {
|
|
|
12806
12856
|
* that filled up on this arrival. */
|
|
12807
12857
|
readonly payload: unknown;
|
|
12808
12858
|
/** Dispatch the resident submission and return its durably acknowledged
|
|
12809
|
-
* execution ID
|
|
12810
|
-
|
|
12811
|
-
|
|
12812
|
-
|
|
12813
|
-
readonly
|
|
12814
|
-
|
|
12815
|
-
readonly mode: string;
|
|
12816
|
-
readonly dueAt: number;
|
|
12817
|
-
} | {
|
|
12859
|
+
* execution ID — or, when the dispatch went unacknowledged, the receipt
|
|
12860
|
+
* recovery completes it under (`mode: 'unacknowledged'`). That start may
|
|
12861
|
+
* still run, so it is neither an execution ID nor a failure. Recovery owns
|
|
12862
|
+
* retries; callers must not execute separately. */
|
|
12863
|
+
readonly accept: () => Promise<string | WorkflowQueuedAdmission>;
|
|
12864
|
+
} | WorkflowQueuedAdmission | {
|
|
12818
12865
|
readonly kind: 'dropped';
|
|
12819
12866
|
readonly retryAfterMs: number;
|
|
12820
12867
|
} | {
|
|
@@ -13118,6 +13165,15 @@ export declare class WorkflowPayloadError extends Error {
|
|
|
13118
13165
|
missingFields: ReadonlyArray<string>, detail: string);
|
|
13119
13166
|
}
|
|
13120
13167
|
|
|
13168
|
+
/** A start the caller follows through its receipt instead of an execution ID. */
|
|
13169
|
+
export declare interface WorkflowQueuedAdmission {
|
|
13170
|
+
readonly kind: 'queued';
|
|
13171
|
+
readonly receiptId: string;
|
|
13172
|
+
readonly intentId: string;
|
|
13173
|
+
readonly mode: string;
|
|
13174
|
+
readonly dueAt: number;
|
|
13175
|
+
}
|
|
13176
|
+
|
|
13121
13177
|
export declare interface WorkflowQueuedControlOptions {
|
|
13122
13178
|
readonly callerContext?: WorkflowCallerContext;
|
|
13123
13179
|
readonly cause?: NonNullable<WorkflowControlRequest['cause']>;
|