@voltro/protocol 0.79.0 → 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 +134 -0
- package/dist/index.d.ts +52 -3
- package/dist/index.js +182 -170
- package/dist/rest.d.ts +3 -0
- package/dist/rest.js +2 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -39,6 +39,140 @@ _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
|
+
|
|
121
|
+
## [0.79.1] — 2026-09-24
|
|
122
|
+
|
|
123
|
+
### Changed
|
|
124
|
+
|
|
125
|
+
- **A charset conversion blocked by a soft-drop snapshot names the reclaim** — `@voltro/database`
|
|
126
|
+
|
|
127
|
+
A declared `store.charset` refuses to convert a table that a foreign key ties to a table the plan does not convert. When that other table is a soft-drop snapshot (`<name>__dropped_<stamp>`), the fix line offered three remedies that do not fit one: declaring it, converting it by hand, or leaving the charset undeclared. It now names `voltro db gc-snapshots --before <date>` with the date that covers the newest blocking snapshot, says that the date also removes every older snapshot, and names `voltro db restore-snapshot` for data that is still wanted.
|
|
128
|
+
- **The devtools capsule morphs into a status and back** — `@voltro/devtools`
|
|
129
|
+
|
|
130
|
+
When `voltro dev` reported a status such as "Compiling…", the closed capsule swapped its chips for the status in one frame and jumped to the new width. The swap is now a morph: the incoming content fades up while the capsule glides from its old width to the new one, and a status that changes mid-glide continues from the width reached. Under `prefers-reduced-motion` the swap stays immediate.
|
|
131
|
+
- **The devtools console animates out the way it came in** — `@voltro/devtools`
|
|
132
|
+
|
|
133
|
+
Opening the console slid it in from its dock edge (or scaled a floating window up), but closing removed it in one frame. Closing now plays the entrance backwards: the console slides out towards its edge, or the window scales down and fades, and the capsule returns once it has gone. The console ignores input while it leaves, a page pushed aside by it reflows only after it is gone, and under `prefers-reduced-motion` it still closes at once.
|
|
134
|
+
|
|
135
|
+
### Fixed
|
|
136
|
+
|
|
137
|
+
- **Probes are answered while a large app's modules load at boot** — `@voltro/cli`
|
|
138
|
+
|
|
139
|
+
The app port answers the health probes from the start of the boot, but the process that answers them is the one booting. Loading the app's modules runs on Node's synchronous loader, so the whole run was one turn of the event loop and a probe waited for all of it: over 800 ms on a generated 1798-module app booting `voltro serve` from the production bundle, and over a second on a loaded machine. Loading now hands the thread back every 25 ms; on the same app, at load 14 on 12 cores, the slowest liveness answer during boot was 89 ms. `voltro dev` loads its modules through the same path.
|
|
140
|
+
- **`voltro update` notes are not silenced by a comment naming the option** — `@voltro/cli`
|
|
141
|
+
|
|
142
|
+
A `manual` codemod decides whether your project is affected by searching its files. In code that search counted comments: an api config that set neither anonymous-tenant option and explained why in a comment read as one that had chosen, and the note meant for it was skipped as "none matched your project". Commented-out code did the same. In `.ts`, `.js` and `.json` files (and their variants) comments no longer count; prose files — runbooks, CI definitions, shell scripts — are still read whole. This applies to every codemod a later `voltro update` runs, including the ones of versions you have not crossed yet.
|
|
143
|
+
- **`voltro update` notes search the whole workspace, not only the app** — `@voltro/cli`
|
|
144
|
+
|
|
145
|
+
`voltro update` runs from an app directory, and the search behind every manual note read only that app. A compose file at the workspace root still setting `VOLTRO_DEV_HEALTH_PORT`, or a tsconfig in another workspace member with a removed TypeScript 7 option, got "nothing in this repository matched" — though the TypeScript 7 note says it checks every tsconfig in the workspace. The search now covers the workspace the app sits in. The TypeScript 7 note also names each tsconfig line it found, and its hand-check `git grep` line matches `"moduleResolution": "node"` and the lower-case spellings (`amd`, `classic`, `es5`); it required `node10` before.
|
|
146
|
+
- **Correction: a compose baseline from before 0.79.0 needs `voltro baseline sync`** — `@voltro/cli`
|
|
147
|
+
|
|
148
|
+
The 0.79.0 note on probes said a compose baseline needs nothing because it regenerates on the next `voltro dev`. It does not: only `voltro baseline sync` rewrites those files, and a baseline synced before 0.79.0 sets `VOLTRO_DEV_HEALTH_PORT` and points its healthchecks at a port nothing listens on any more. `voltro update` now prints a correcting note for every project whose workspace still sets that variable, naming the lines.
|
|
149
|
+
- **`db apply` undo converts tables back and says exactly what still differs** — `@voltro/cli`, `@voltro/database`
|
|
150
|
+
|
|
151
|
+
On MySQL, MariaDB, SQLite and Turso a failed `voltro db apply` is undone rather than rolled back. When the run had converted tables to a declared `store.charset`, the undo planned the way back with that same charset, so no conversion could be planned in reverse — and the check behind "the database is as it was" asked the same charset-blind question, so the line printed over tables that stayed converted. The undo now converts each table back to the character set its string columns had before the run, and verifies it by reading every column back. A column the undo redefines also keeps its default: a string default read from MariaDB was quoted a second time. When an undo does not read as it was, the report now lists each remaining difference instead of asking you to compare by hand.
|
|
152
|
+
- **The compose dev image starts pnpm 11 and later as the non-root user** — `@voltro/cli`
|
|
153
|
+
|
|
154
|
+
The `compose` and `compose-mariadb` baselines' `docker/dev.Dockerfile` activated pnpm as root but never ran it. pnpm 11 and later download a native binary on their first run, into a directory the non-root user compose starts every service as cannot write, so `docker compose … up` stopped at the first service with `EACCES … pnpm-native….tgz`. The image now runs pnpm once while it is built. Run `voltro baseline sync` and rebuild the image to take it.
|
|
155
|
+
- **The closed devtools capsule glows again** — `@voltro/devtools`
|
|
156
|
+
|
|
157
|
+
When the overlay became a docking console, the closed state turned into a status capsule and lost the launcher's animated halo: a bright slice of brand violet circling the rim while a soft glow breathes around it. The capsule carries it again, traced along its pill outline. It steps aside while `voltro dev` reports a status, where the capsule's own pulse in the status colour takes over, and it stands still under `prefers-reduced-motion`.
|
|
158
|
+
- **`db gc-snapshots` drops snapshots that reference each other, and works on MySQL** — `@voltro/cli`
|
|
159
|
+
|
|
160
|
+
`voltro db gc-snapshots` dropped snapshots in catalog order. Two tables one plan soft-dropped keep the foreign key between them, so dropping the parent first failed with `1451` on MySQL and MariaDB (and on Postgres whenever the parent came first), every run, after the snapshots before it were already gone; a soft-dropped reference column failed with `1553`. The keys the snapshots hold are now dropped first, then the columns, then the tables, a child before its parent. A snapshot that a table outside the set still references is refused before anything is dropped, with each constraint named. On MySQL 8 the command, and `voltro db restore-snapshot`, found no snapshot at all, because the catalog answers in upper case there; both read it now.
|
|
161
|
+
- **`voltro update` lists the lines a manual note is about** — `@voltro/cli`
|
|
162
|
+
|
|
163
|
+
A `manual` codemod decides between "found" and "nothing in this repository matched" with its own check, and that check could be narrower than the search its note tells you to run. The note for probes on the app port told readers to grep for `internal/(liveness|readiness|startup)` but looked only for `VOLTRO_DEV_HEALTH_PORT`, so a project whose Helm values, compose healthcheck and smoke script probed app port + 1 directly got "nothing in this repository matched" while every one of them needed moving. A manual note can now list the lines it is about, and `voltro update` prints them under it (`── found in this repository (N lines) — check each:`, `file:line text`). The probe note lists every probe path and variable it finds, with the port beside each probe; the anonymous-tenant note names each api config that sets neither option. A note that lists a line is reported as found.
|
|
164
|
+
- **MariaDB: a charset, type or nullability change keeps the column's CHECK** — `@voltro/database`
|
|
165
|
+
|
|
166
|
+
On MariaDB a CHECK written on the column itself — how a table with `text().oneOf([...])` is created — is part of the column definition, and `MODIFY COLUMN` without it removes it. Every operation that redefines a column did exactly that: a `convert-charset` rewrite, a nullability, type or default change. The re-plan then proposed `add-check` for each, and the apply refused with "the migration did not converge", after every statement had run. Declaring `store.charset` over an existing schema hit it once per converted enum column. The rewrite now restates each column's CHECK, so it stays where it was; an explicit `drop-check` still removes one the declaration no longer has. MySQL keeps such a CHECK on its own and is unchanged. `emitConvertCharsetMysql` takes the CHECKs to keep as an optional last argument, so every existing call compiles as before.
|
|
167
|
+
- **`voltro serve` checks pending file migrations from its bundle, not from source** — `@voltro/cli`, `@voltro/database`
|
|
168
|
+
|
|
169
|
+
Before it boots, `voltro serve` refuses when a `migrations/<timestamp>_<slug>.ts` file has never run. `voltro build` did not bundle those files, so serve imported each from source — and, through it, a second copy of the framework's migration code. Measured on an app with two file migrations: about 290 ms during which the process answered nothing, after the port was already taking health probes. The build now bundles every file migration, and serve reads them from the bundle. `pendingFileMigrationIds` takes an optional module importer as its last argument, and `walkMigrationFiles` is exported; every existing call compiles as before.
|
|
170
|
+
- **`voltro update --wait` says what the registry answered, per package** — `@voltro/cli`
|
|
171
|
+
|
|
172
|
+
`--wait` reported every package it could not confirm as "not on the registry", whether the registry had answered 404 or the request had failed. It now names the answer with each package — `404: not published yet`, or `no answer: <status or error>` — while it waits, at the ceiling, and after a failed install, so a network error is never reported as a missing release. In `pnpm-workspace.yaml`, a `minimumReleaseAgeExclude` entry that pnpm 12.5 and later widened in place to a version union (`'@voltro/ai@0.78.0 || 0.79.0'`) was skipped without a word; a union holding the version being left now moves to the target like any other entry.
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
42
176
|
## [0.79.0] — 2026-09-23
|
|
43
177
|
|
|
44
178
|
### ⚠ BREAKING
|
package/dist/index.d.ts
CHANGED
|
@@ -2302,6 +2302,27 @@ export declare const findAdvisoryResourceGuards: (procedures: ReadonlyArray<{
|
|
|
2302
2302
|
/** Record a completed response for replay. */
|
|
2303
2303
|
export declare const finishIdempotent: (store: IdempotencyStore, scope: string, key: string, response: IdempotencyResponse, now: number) => Promise<void>;
|
|
2304
2304
|
|
|
2305
|
+
/**
|
|
2306
|
+
* The value a ONE-SHOT read of an rpc resolves to, given the first value it
|
|
2307
|
+
* delivered.
|
|
2308
|
+
*
|
|
2309
|
+
* A query's success type is the three-member union above, and a one-shot read
|
|
2310
|
+
* takes only its first event: a `snapshot` resolves to its `data`; an `error`
|
|
2311
|
+
* REJECTS — with the typed error on `cause` and `voltroError`, so a caller can
|
|
2312
|
+
* still branch on its `_tag` — exactly as a failed exit would; a `delta` cannot
|
|
2313
|
+
* be first and says so. Anything else is a mutation or action's own result and
|
|
2314
|
+
* passes through, which may legitimately be a tagged object of the app's.
|
|
2315
|
+
*
|
|
2316
|
+
* Shared because there are two such reads — the server loader's `ctx.query`
|
|
2317
|
+
* over `POST /rpc` and the browser loader's over the page's socket — and an
|
|
2318
|
+
* unwrap written twice is how an authorization answer once reached a loader as
|
|
2319
|
+
* its return value on one transport and as a rejection on the other.
|
|
2320
|
+
*
|
|
2321
|
+
* `label` prefixes every message (`[loader.query]`), naming the surface that
|
|
2322
|
+
* made the read.
|
|
2323
|
+
*/
|
|
2324
|
+
export declare const firstEventValue: (tag: string, value: unknown, label: string) => unknown;
|
|
2325
|
+
|
|
2305
2326
|
/** A route rendered for humans — logs, the inspect surface, the dashboard. */
|
|
2306
2327
|
export declare const formatEventRoute: (route: string) => string;
|
|
2307
2328
|
|
|
@@ -4847,6 +4868,9 @@ declare interface RestRouteContext {
|
|
|
4847
4868
|
* REST handlers on the same capability surface as query/mutation/action
|
|
4848
4869
|
* handlers without creating a protocol-to-runtime package dependency. */
|
|
4849
4870
|
readonly app?: unknown;
|
|
4871
|
+
/** The caller's address, resolved through `security.trustedProxies` — the
|
|
4872
|
+
* value the listener's per-IP shield counts. Absent when unknown. */
|
|
4873
|
+
readonly remoteAddr?: string;
|
|
4850
4874
|
}
|
|
4851
4875
|
|
|
4852
4876
|
declare interface RestRouteDescriptor<I, O> {
|
|
@@ -5149,6 +5173,16 @@ export declare interface RpcInterceptorContext {
|
|
|
5149
5173
|
readonly surface?: 'rest';
|
|
5150
5174
|
/** The descriptor's `publicApi` spec, on a projected call. */
|
|
5151
5175
|
readonly publicApi?: PublicApiSpec;
|
|
5176
|
+
/**
|
|
5177
|
+
* The caller's address — the same value the pre-auth IP shield counts, resolved
|
|
5178
|
+
* by the listener through `security.trustedProxies` (and
|
|
5179
|
+
* `security.clientAddressHeader`). A client cannot set it: the listener
|
|
5180
|
+
* writes it, and any copy the client sends is discarded.
|
|
5181
|
+
*
|
|
5182
|
+
* Absent when the call did not arrive over the network (a workflow step, a
|
|
5183
|
+
* schedule, an in-process invocation) or the socket address is unknown.
|
|
5184
|
+
*/
|
|
5185
|
+
readonly remoteAddr?: string;
|
|
5152
5186
|
}
|
|
5153
5187
|
|
|
5154
5188
|
/**
|
|
@@ -7506,7 +7540,12 @@ export declare const WorkflowRunHandleSchema: Schema.Struct<{
|
|
|
7506
7540
|
status: Schema.Literal<["running", "succeeded", "queued", "dropped", "skipped", "replayed"]>;
|
|
7507
7541
|
deferral: Schema.optional<Schema.Struct<{
|
|
7508
7542
|
/** `debounce` | `batch` | `throttle` | `concurrency` | `paused` |
|
|
7509
|
-
* `rateLimit` | `singleton`.
|
|
7543
|
+
* `rateLimit` | `singleton` | `delayed` | `transaction` | `unacknowledged`.
|
|
7544
|
+
*
|
|
7545
|
+
* `unacknowledged` — the start was dispatched and no acknowledgement came
|
|
7546
|
+
* back. The engine may have it; recovery retries the dispatch and binds the
|
|
7547
|
+
* receipt (`id`) to whatever run results. It is not a failure: do not
|
|
7548
|
+
* report one to a user, follow the receipt. */
|
|
7510
7549
|
mode: typeof Schema.String;
|
|
7511
7550
|
/** Epoch ms this may first be admitted. Null for a drop or a skip. */
|
|
7512
7551
|
dueAt: Schema.NullOr<typeof Schema.Number>;
|
|
@@ -7694,7 +7733,12 @@ export declare type WorkflowStartDeferral = Schema.Schema.Type<typeof WorkflowSt
|
|
|
7694
7733
|
*/
|
|
7695
7734
|
export declare const WorkflowStartDeferralSchema: Schema.Struct<{
|
|
7696
7735
|
/** `debounce` | `batch` | `throttle` | `concurrency` | `paused` |
|
|
7697
|
-
* `rateLimit` | `singleton`.
|
|
7736
|
+
* `rateLimit` | `singleton` | `delayed` | `transaction` | `unacknowledged`.
|
|
7737
|
+
*
|
|
7738
|
+
* `unacknowledged` — the start was dispatched and no acknowledgement came
|
|
7739
|
+
* back. The engine may have it; recovery retries the dispatch and binds the
|
|
7740
|
+
* receipt (`id`) to whatever run results. It is not a failure: do not
|
|
7741
|
+
* report one to a user, follow the receipt. */
|
|
7698
7742
|
mode: typeof Schema.String;
|
|
7699
7743
|
/** Epoch ms this may first be admitted. Null for a drop or a skip. */
|
|
7700
7744
|
dueAt: Schema.NullOr<typeof Schema.Number>;
|
|
@@ -7812,7 +7856,12 @@ export declare const workflowToRpc: <const Name extends string, Payload extends
|
|
|
7812
7856
|
status: Schema.Literal<["running", "succeeded", "queued", "dropped", "skipped", "replayed"]>;
|
|
7813
7857
|
deferral: Schema.optional<Schema.Struct<{
|
|
7814
7858
|
/** `debounce` | `batch` | `throttle` | `concurrency` | `paused` |
|
|
7815
|
-
* `rateLimit` | `singleton`.
|
|
7859
|
+
* `rateLimit` | `singleton` | `delayed` | `transaction` | `unacknowledged`.
|
|
7860
|
+
*
|
|
7861
|
+
* `unacknowledged` — the start was dispatched and no acknowledgement came
|
|
7862
|
+
* back. The engine may have it; recovery retries the dispatch and binds the
|
|
7863
|
+
* receipt (`id`) to whatever run results. It is not a failure: do not
|
|
7864
|
+
* report one to a user, follow the receipt. */
|
|
7816
7865
|
mode: typeof Schema.String;
|
|
7817
7866
|
/** Epoch ms this may first be admitted. Null for a drop or a skip. */
|
|
7818
7867
|
dueAt: Schema.NullOr<typeof Schema.Number>;
|
package/dist/index.js
CHANGED
|
@@ -136,10 +136,22 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
136
136
|
_tag: p.Literal("error"),
|
|
137
137
|
error: p.Unknown,
|
|
138
138
|
revision: p.optional(p.Number)
|
|
139
|
-
})),
|
|
139
|
+
})), wt = (e, t, n) => {
|
|
140
|
+
if (!t || typeof t != "object" || !("_tag" in t)) return t;
|
|
141
|
+
let r = t;
|
|
142
|
+
switch (r._tag) {
|
|
143
|
+
case "snapshot": return r.data;
|
|
144
|
+
case "error": {
|
|
145
|
+
let t = r.error;
|
|
146
|
+
throw Object.assign(Error(`${n} ${e}: ${t?._tag ?? "error"}${t?.message ? ` — ${t.message}` : ""}`, { cause: r.error }), { voltroError: r.error });
|
|
147
|
+
}
|
|
148
|
+
case "delta": throw Error(`${n} ${e}: received a 'delta' as the first event, which should be impossible — a subscription always opens with a snapshot. This is a framework bug.`);
|
|
149
|
+
default: return t;
|
|
150
|
+
}
|
|
151
|
+
}, b = (e, t) => {
|
|
140
152
|
let n = `${e ?? "this procedure"} input`;
|
|
141
153
|
return t === !0 ? `${n} (guarded — the guard did NOT run: the payload failed to decode first, so this says nothing about access)` : n;
|
|
142
|
-
},
|
|
154
|
+
}, Tt = (e) => {
|
|
143
155
|
let t = e.ast;
|
|
144
156
|
return t._tag === "TypeLiteral" && (t.propertySignatures?.length ?? 0) === 0 && (t.indexSignatures?.length ?? 0) === 0;
|
|
145
157
|
}, x = (e, t, n) => {
|
|
@@ -147,8 +159,8 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
147
159
|
parseOptions: { onExcessProperty: "error" },
|
|
148
160
|
parseIssueTitle: () => r
|
|
149
161
|
});
|
|
150
|
-
return
|
|
151
|
-
},
|
|
162
|
+
return Tt(e) ? p.filter((e) => typeof e == "object" && e && Object.keys(e).length > 0 ? `this procedure declares no input; received: ${Object.keys(e).join(", ")}` : void 0)(i).annotations({ identifier: r }) : i;
|
|
163
|
+
}, Et = class extends p.TaggedError()("BusinessRuleViolation", {
|
|
152
164
|
rule: p.String,
|
|
153
165
|
params: p.optional(p.Record({
|
|
154
166
|
key: p.String,
|
|
@@ -157,21 +169,21 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
157
169
|
field: p.optional(p.String),
|
|
158
170
|
message: p.optional(p.String),
|
|
159
171
|
severity: p.Literal("error", "warning")
|
|
160
|
-
}) {},
|
|
172
|
+
}) {}, Dt = p.Struct({
|
|
161
173
|
field: p.String,
|
|
162
174
|
message: p.String,
|
|
163
175
|
params: p.optional(p.Record({
|
|
164
176
|
key: p.String,
|
|
165
177
|
value: p.Unknown
|
|
166
178
|
}))
|
|
167
|
-
}),
|
|
179
|
+
}), Ot = class extends p.TaggedError()("ValidationError", {
|
|
168
180
|
field: p.String,
|
|
169
181
|
message: p.String,
|
|
170
182
|
params: p.optional(p.Record({
|
|
171
183
|
key: p.String,
|
|
172
184
|
value: p.Unknown
|
|
173
185
|
}))
|
|
174
|
-
}) {},
|
|
186
|
+
}) {}, kt = class extends p.TaggedError()("ValidationErrors", { issues: p.Array(Dt).pipe(p.minItems(1)) }) {}, At = (e) => {
|
|
175
187
|
if (typeof e != "object" || !e) return [];
|
|
176
188
|
let t = e._tag;
|
|
177
189
|
if (t === "ValidationError") {
|
|
@@ -197,34 +209,34 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
197
209
|
}];
|
|
198
210
|
}
|
|
199
211
|
return [];
|
|
200
|
-
},
|
|
212
|
+
}, jt = class extends p.TaggedError()("ApprovalRequired", {
|
|
201
213
|
approvalId: p.String,
|
|
202
214
|
procedure: p.String,
|
|
203
215
|
expiresAt: p.String,
|
|
204
216
|
requiredScopes: p.Array(p.String),
|
|
205
217
|
reason: p.NullOr(p.String),
|
|
206
218
|
created: p.Boolean
|
|
207
|
-
}) {},
|
|
219
|
+
}) {}, Mt = class extends p.TaggedError()("ApprovalNotFound", { approvalId: p.String }) {}, Nt = class extends p.TaggedError()("ApprovalSelfApproval", {
|
|
208
220
|
approvalId: p.String,
|
|
209
221
|
subjectId: p.NullOr(p.String)
|
|
210
|
-
}) {},
|
|
222
|
+
}) {}, Pt = class extends p.TaggedError()("ApprovalRejected", {
|
|
211
223
|
approvalId: p.String,
|
|
212
224
|
decidedBy: p.NullOr(p.String),
|
|
213
225
|
note: p.NullOr(p.String)
|
|
214
226
|
}) {}, S = class extends p.TaggedError()("ApprovalExpired", {
|
|
215
227
|
approvalId: p.String,
|
|
216
228
|
expiredAt: p.String
|
|
217
|
-
}) {},
|
|
229
|
+
}) {}, Ft = class extends p.TaggedError()("ApprovalNotPending", {
|
|
218
230
|
approvalId: p.String,
|
|
219
231
|
status: p.String
|
|
220
|
-
}) {},
|
|
232
|
+
}) {}, It = class extends p.TaggedError()("ApprovalForbidden", {
|
|
221
233
|
approvalId: p.String,
|
|
222
234
|
required: p.NullOr(p.String),
|
|
223
235
|
message: p.String
|
|
224
236
|
}) {}, C = class extends p.TaggedError()("ApprovalUnavailable", {
|
|
225
237
|
procedure: p.String,
|
|
226
238
|
message: p.String
|
|
227
|
-
}) {},
|
|
239
|
+
}) {}, Lt = p.Union(jt, Pt, S, C), Rt = p.Union(Mt, Nt, S, Ft, It, C), zt = p.Struct({
|
|
228
240
|
id: p.String,
|
|
229
241
|
procedure: p.String,
|
|
230
242
|
kind: p.Literal("mutation", "action"),
|
|
@@ -238,8 +250,8 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
238
250
|
decidedAt: p.NullOr(p.String),
|
|
239
251
|
note: p.NullOr(p.String),
|
|
240
252
|
relation: p.Literal("to-decide", "requested")
|
|
241
|
-
}), w = "channel:",
|
|
242
|
-
if (!
|
|
253
|
+
}), w = "channel:", Bt = Symbol.for("@voltro/protocol/reactivityChannels"), Vt = globalThis, T = Vt[Bt] ?? (Vt[Bt] = /* @__PURE__ */ new Map()), Ht = /^[a-z][a-z0-9-]*(\.[a-z0-9-]+)*$/, Ut = (e) => {
|
|
254
|
+
if (!Ht.test(e)) throw Error(`reactivityChannel('${e}'): invalid channel name.\n Use lowercase kebab segments separated by dots — \`presence\`, \`job-queue\`,
|
|
243
255
|
\`billing.usage\`. A \`:\` is refused because it is the namespace separator in
|
|
244
256
|
the routing key (\`${w}<name>\`), and an uppercase letter is\n refused because a key that differs only by case reads as one channel and
|
|
245
257
|
routes as two.`);
|
|
@@ -252,14 +264,14 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
252
264
|
toString: () => n
|
|
253
265
|
});
|
|
254
266
|
return T.set(e, r), r;
|
|
255
|
-
},
|
|
256
|
-
let t =
|
|
257
|
-
for (let r of e) for (let e of
|
|
267
|
+
}, Wt = (e) => typeof e == "object" && !!e && e.kind === "reactivity-channel", Gt = (e) => e.startsWith(w), Kt = () => new Set([...T.values()].map((e) => e.key)), E = (e) => Wt(e) ? e.key : e, qt = (e) => e === void 0 ? [] : Array.isArray(e) ? e.map(E) : [E(e)], Jt = (e) => e === void 0 ? void 0 : Array.isArray(e) ? e.map(E) : E(e), Yt = (e) => {
|
|
268
|
+
let t = Kt(), n = [];
|
|
269
|
+
for (let r of e) for (let e of qt(r.source)) Gt(e) && !t.has(e) && n.push({
|
|
258
270
|
procedure: r.name,
|
|
259
271
|
key: e
|
|
260
272
|
});
|
|
261
273
|
return n;
|
|
262
|
-
},
|
|
274
|
+
}, Xt = (e, t) => {
|
|
263
275
|
let n = e?.injectExternalChange;
|
|
264
276
|
return n !== void 0 && (n.call(e, {
|
|
265
277
|
table: t.key,
|
|
@@ -268,8 +280,8 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
268
280
|
old: {},
|
|
269
281
|
origin: "inline"
|
|
270
282
|
}), !0);
|
|
271
|
-
},
|
|
272
|
-
if (
|
|
283
|
+
}, Zt = (e) => e.internal !== !0, D = (e) => {
|
|
284
|
+
if (Qt(e), e.idempotencyScope !== void 0 && e.idempotencyScope !== "subject" && e.idempotencyScope !== "connection") throw Error(`${e.name}: idempotencyScope must be 'subject' or 'connection'`);
|
|
273
285
|
let t = e.serialize;
|
|
274
286
|
if (t !== void 0) {
|
|
275
287
|
if (typeof t.key != "function") throw Error(`${e.name}: serialize.key must be a function of the input`);
|
|
@@ -293,7 +305,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
293
305
|
let r = [e.publicApi === void 0 ? void 0 : "publicApi", e.exposeAsTool === void 0 ? void 0 : "exposeAsTool"].filter((e) => e !== void 0);
|
|
294
306
|
if (r.length === 0) return e;
|
|
295
307
|
throw Error(`${e.name}: \`internal: true\` cannot be combined with ${r.map((e) => `\`${e}\``).join(" or ")}. \`internal\` takes the procedure OFF the wire; those put it back ON a different one (${r.includes("publicApi") ? "a REST route" : "an agent tool"}), and that surface is projected without consulting the flag — so the procedure would be unreachable from your client and reachable from the internet. Drop \`internal: true\` if the wider surface is intended, or remove the ${r.join(" / ")} annotation if it is not.`);
|
|
296
|
-
},
|
|
308
|
+
}, Qt = (e) => {
|
|
297
309
|
let t = e.requiresApproval;
|
|
298
310
|
if (t !== void 0) {
|
|
299
311
|
if (!Array.isArray(t.approvers) || t.approvers.length === 0) throw Error(`${e.name}: \`requiresApproval.approvers\` is empty, so NOBODY can approve this\n procedure and every call would park forever. Name the scope(s) an approver must hold
|
|
@@ -303,7 +315,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
303
315
|
human from every other, and the second-pair-of-eyes control would enforce nothing.
|
|
304
316
|
Give the procedure a \`guards:\` decision, or drop \`requiresApproval\`.`);
|
|
305
317
|
}
|
|
306
|
-
},
|
|
318
|
+
}, $t = (e) => e.action !== void 0 && e.resourceType !== void 0, en = (e) => e.authenticated === !0, tn = (e) => e.openAccess !== void 0 || e.guards !== void 0 && e.guards.length > 0, O = (e, t, n) => {
|
|
307
319
|
if (n === void 0) return t;
|
|
308
320
|
if (n.trim() === "") throw Error(`${e}: \`openAccess\` needs a REASON, not an empty string. It is the sentence a\n reviewer reads to decide whether this procedure should really be callable
|
|
309
321
|
without an authorization check — e.g. \`openAccess: 'public pricing, no caller data'\`.`);
|
|
@@ -316,7 +328,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
316
328
|
input: e.input,
|
|
317
329
|
output: e.output,
|
|
318
330
|
error: e.error ?? p.Never,
|
|
319
|
-
source:
|
|
331
|
+
source: Jt(e.source),
|
|
320
332
|
cache: e.cache,
|
|
321
333
|
guards: O(e.name, e.guards, e.openAccess),
|
|
322
334
|
openAccess: e.openAccess,
|
|
@@ -355,7 +367,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
355
367
|
requiresApproval: e.requiresApproval,
|
|
356
368
|
internal: e.internal,
|
|
357
369
|
overridesPlugin: e.overridesPlugin
|
|
358
|
-
}),
|
|
370
|
+
}), nn = (e) => D({
|
|
359
371
|
kind: "stream",
|
|
360
372
|
name: e.name,
|
|
361
373
|
input: e.input,
|
|
@@ -365,44 +377,44 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
365
377
|
overridesPlugin: e.overridesPlugin,
|
|
366
378
|
guards: O(e.name, e.guards, e.openAccess),
|
|
367
379
|
openAccess: e.openAccess
|
|
368
|
-
}), M = (e, t) => t && t.length > 0 ? p.Union(e, ...t) : e, N = (e) => e !== void 0 && e.some((e) => !u(e)),
|
|
369
|
-
let n =
|
|
380
|
+
}), M = (e, t) => t && t.length > 0 ? p.Union(e, ...t) : e, N = (e) => e !== void 0 && e.some((e) => !u(e)), rn = (e) => p.Union(e, f, l), an = (e) => p.Union(e, Et), on = (e) => p.Union(e, Ot, kt), sn = (e, t) => t === void 0 ? e : p.Union(e, Lt), P = (e, t) => {
|
|
381
|
+
let n = rn(e.error);
|
|
370
382
|
if (t === "stream" || t === "query") return n;
|
|
371
|
-
let r = t === "mutation" ? p.Union(
|
|
372
|
-
return on(
|
|
373
|
-
},
|
|
383
|
+
let r = t === "mutation" ? p.Union(an(n), dt, ht) : n;
|
|
384
|
+
return sn(on(t === "mutation" && e.idempotencyScope === "connection" ? p.Union(r, qe) : r), e.requiresApproval);
|
|
385
|
+
}, cn = (e, t) => M(rn(e.errorSchema), t), ln = (e, t) => ({
|
|
374
386
|
...e,
|
|
375
387
|
name: t
|
|
376
|
-
}),
|
|
388
|
+
}), un = (e, t) => m.make(e.name, {
|
|
377
389
|
payload: x(e.input, e.name, N(e.guards)),
|
|
378
390
|
success: y(e.output),
|
|
379
391
|
error: M(P(e, "query"), t),
|
|
380
392
|
stream: !0
|
|
381
|
-
}),
|
|
393
|
+
}), dn = (e, t) => m.make(e.name, {
|
|
382
394
|
payload: x(e.input, e.name, N(e.guards)),
|
|
383
395
|
success: e.output,
|
|
384
396
|
error: M(P(e, "mutation"), t)
|
|
385
|
-
}),
|
|
397
|
+
}), fn = (e, t) => m.make(e.name, {
|
|
386
398
|
payload: x(e.input, e.name, N(e.guards)),
|
|
387
399
|
success: e.output,
|
|
388
400
|
error: M(P(e, "action"), t)
|
|
389
|
-
}),
|
|
401
|
+
}), pn = (e, t) => m.make(e.name, {
|
|
390
402
|
payload: x(e.input, e.name, N(e.guards)),
|
|
391
403
|
success: e.element,
|
|
392
404
|
error: M(e.error, t),
|
|
393
405
|
stream: !0
|
|
394
|
-
}),
|
|
406
|
+
}), mn = (e) => {
|
|
395
407
|
if (typeof e != "object" || !e) return;
|
|
396
408
|
let t = e._tag;
|
|
397
409
|
return typeof t == "string" ? t : void 0;
|
|
398
|
-
},
|
|
410
|
+
}, hn = (e) => {
|
|
399
411
|
switch (e.kind) {
|
|
400
|
-
case "query": return
|
|
401
|
-
case "mutation": return
|
|
402
|
-
case "action": return
|
|
403
|
-
case "stream": return
|
|
412
|
+
case "query": return un(e);
|
|
413
|
+
case "mutation": return dn(e);
|
|
414
|
+
case "action": return fn(e);
|
|
415
|
+
case "stream": return pn(e);
|
|
404
416
|
}
|
|
405
|
-
},
|
|
417
|
+
}, gn = (e) => {
|
|
406
418
|
let t = e.input, n = t === void 0 ? {} : { input: t }, r = e.output, i = r === void 0 ? {} : { output: r };
|
|
407
419
|
if (e.kind === "query") return {
|
|
408
420
|
kind: "query",
|
|
@@ -448,7 +460,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
448
460
|
..."relations" in e && e.relations !== void 0 ? { relations: e.relations } : {}
|
|
449
461
|
}))
|
|
450
462
|
};
|
|
451
|
-
},
|
|
463
|
+
}, _n = 7500, vn = (e) => typeof e == "object" && !!e && e.kind === "event", yn = (e) => {
|
|
452
464
|
if (e.name.length === 0) throw Error("defineEvent: `name` must not be empty");
|
|
453
465
|
if (/\s/.test(e.name)) throw Error(`defineEvent("${e.name}"): \`name\` must not contain whitespace.\n The name is used as a broker subject segment. NATS refuses a subject with
|
|
454
466
|
whitespace and delivers nothing — silently, and only on that broker, so an
|
|
@@ -483,62 +495,62 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
483
495
|
webhook: e.webhook,
|
|
484
496
|
delivery: e.delivery
|
|
485
497
|
};
|
|
486
|
-
},
|
|
498
|
+
}, bn = (e) => p.Struct({
|
|
487
499
|
_tag: p.Literal("event"),
|
|
488
500
|
origin: p.String,
|
|
489
501
|
n: p.Number,
|
|
490
502
|
emittedAt: p.Number,
|
|
491
503
|
payload: e
|
|
492
|
-
}),
|
|
504
|
+
}), xn = p.Struct({
|
|
493
505
|
_tag: p.Literal("gap"),
|
|
494
506
|
missed: p.Number,
|
|
495
507
|
reason: p.Literal("buffer", "resume")
|
|
496
|
-
}),
|
|
508
|
+
}), Sn = p.Struct({ _tag: p.Literal("attached") }), Cn = (e) => p.Union(Sn, bn(e), xn), wn = p.Struct({
|
|
497
509
|
origin: p.String,
|
|
498
510
|
n: p.Number
|
|
499
|
-
}),
|
|
511
|
+
}), Tn = (e) => p.Struct({
|
|
500
512
|
key: e,
|
|
501
|
-
resume: p.optional(p.Array(
|
|
502
|
-
}),
|
|
513
|
+
resume: p.optional(p.Array(wn))
|
|
514
|
+
}), En = (e, t) => {
|
|
503
515
|
let n = e.guards !== void 0 && e.guards.some((e) => !u(e)) ? p.Union(f, l) : l, r = t && t.length > 0 ? p.Union(n, ...t) : n;
|
|
504
516
|
return m.make(e.name, {
|
|
505
|
-
payload: x(
|
|
506
|
-
success:
|
|
517
|
+
payload: x(Tn(e.key), e.name, N(e.guards)),
|
|
518
|
+
success: Cn(e.payload),
|
|
507
519
|
error: r,
|
|
508
520
|
stream: !0
|
|
509
521
|
});
|
|
510
|
-
},
|
|
522
|
+
}, Dn = class extends p.TaggedError()("EventPayloadInvalid", {
|
|
511
523
|
event: p.String,
|
|
512
524
|
message: p.String
|
|
513
|
-
}) {},
|
|
525
|
+
}) {}, On = class extends p.TaggedError()("EventKeyInvalid", {
|
|
514
526
|
event: p.String,
|
|
515
527
|
message: p.String
|
|
516
|
-
}) {},
|
|
528
|
+
}) {}, kn = class extends p.TaggedError()("EventPayloadTooLarge", {
|
|
517
529
|
event: p.String,
|
|
518
530
|
bytes: p.Number,
|
|
519
531
|
limit: p.Number
|
|
520
|
-
}) {},
|
|
532
|
+
}) {}, An = (e) => {
|
|
521
533
|
if (typeof e != "object" || !e) return JSON.stringify(e) ?? "null";
|
|
522
534
|
let t = Object.entries(e).filter(([, e]) => e !== void 0).sort(([e], [t]) => e < t ? -1 : +(e > t));
|
|
523
535
|
return JSON.stringify(t);
|
|
524
|
-
},
|
|
536
|
+
}, jn = "\0", Mn = (e, t, n) => [
|
|
525
537
|
e ?? "~",
|
|
526
538
|
t,
|
|
527
|
-
|
|
528
|
-
].join("\0"),
|
|
539
|
+
An(n)
|
|
540
|
+
].join("\0"), Nn = (e) => {
|
|
529
541
|
let [t = "~", n = "", r = ""] = e.split("\0");
|
|
530
542
|
return {
|
|
531
543
|
tenantId: t === "~" ? null : t,
|
|
532
544
|
event: n,
|
|
533
545
|
key: r
|
|
534
546
|
};
|
|
535
|
-
},
|
|
547
|
+
}, Pn = (e) => e.split("\0").join(" · "), F = Symbol.for("@voltro/protocol/workflow-access"), Fn = {
|
|
536
548
|
guards: void 0,
|
|
537
549
|
openAccess: void 0,
|
|
538
550
|
internal: !1,
|
|
539
551
|
controls: void 0,
|
|
540
552
|
controlsOpenAccess: void 0
|
|
541
|
-
},
|
|
553
|
+
}, In = (e, t) => {
|
|
542
554
|
let n = t.guards;
|
|
543
555
|
if (n !== void 0 && n.length === 0) throw Error(`workflow("${e}"): \`guards: []\` is empty, so it enforces nothing —\n but it reads at the call site as if the start were protected. Omit the field
|
|
544
556
|
and decide with \`openAccess\` or \`internal\`, or list the scopes that may start it.`);
|
|
@@ -570,16 +582,16 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
570
582
|
controls: i === void 0 ? r : [d(i.trim())],
|
|
571
583
|
controlsOpenAccess: i?.trim()
|
|
572
584
|
};
|
|
573
|
-
},
|
|
585
|
+
}, Ln = (e, t) => {
|
|
574
586
|
Object.defineProperty(e, F, {
|
|
575
587
|
value: t,
|
|
576
588
|
enumerable: !1
|
|
577
589
|
});
|
|
578
590
|
}, I = (e) => {
|
|
579
|
-
if (typeof e != "object" || !e) return
|
|
591
|
+
if (typeof e != "object" || !e) return Fn;
|
|
580
592
|
let t = e[F];
|
|
581
|
-
return t === void 0 ?
|
|
582
|
-
},
|
|
593
|
+
return t === void 0 ? Fn : t;
|
|
594
|
+
}, Rn = (e) => {
|
|
583
595
|
let t = I(e);
|
|
584
596
|
return {
|
|
585
597
|
name: e.name,
|
|
@@ -588,18 +600,18 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
588
600
|
openAccess: t.openAccess,
|
|
589
601
|
controls: t.controls
|
|
590
602
|
};
|
|
591
|
-
},
|
|
603
|
+
}, zn = (e) => e, Bn = (e) => e, Vn = (e) => {
|
|
592
604
|
let t = e.alias?.trim(), n = e.instance?.trim(), r = t !== void 0 && t !== "" ? t : e.base;
|
|
593
605
|
return n !== void 0 && n !== "" ? `${r}#${n}` : r;
|
|
594
|
-
},
|
|
606
|
+
}, Hn = (e) => {
|
|
595
607
|
if (e.length !== 0) return (t, n) => e.reduceRight((e, t) => t(e, n), t);
|
|
596
|
-
},
|
|
608
|
+
}, Un = (e, t) => {
|
|
597
609
|
let n = pt.GenericTag(e);
|
|
598
610
|
return {
|
|
599
611
|
Tag: n,
|
|
600
612
|
Live: mt.succeed(n, t)
|
|
601
613
|
};
|
|
602
|
-
},
|
|
614
|
+
}, Wn = (e, t, n) => {
|
|
603
615
|
if (!t) return { ok: !0 };
|
|
604
616
|
let r = n === void 0 ? null : L(n);
|
|
605
617
|
if (!r) return {
|
|
@@ -609,7 +621,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
609
621
|
let i = t.trim();
|
|
610
622
|
if (i === "*" || i === "") return { ok: !0 };
|
|
611
623
|
let a = i.split(/\s+/).filter((e) => e.length > 0);
|
|
612
|
-
for (let i of a) if (!
|
|
624
|
+
for (let i of a) if (!Gn(i, r)) return {
|
|
613
625
|
ok: !1,
|
|
614
626
|
reason: `plugin "${e}" requires framework ${t}, running ${n}`
|
|
615
627
|
};
|
|
@@ -622,7 +634,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
622
634
|
patch: Number(t[3]),
|
|
623
635
|
pre: t[4] ?? ""
|
|
624
636
|
} : null;
|
|
625
|
-
}, R = (e, t) => e.major === t.major ? e.minor === t.minor ? e.patch === t.patch ? e.pre && !t.pre ? -1 : !e.pre && t.pre ? 1 : e.pre && t.pre ? e.pre < t.pre ? -1 : +(e.pre > t.pre) : 0 : e.patch - t.patch : e.minor - t.minor : e.major - t.major,
|
|
637
|
+
}, R = (e, t) => e.major === t.major ? e.minor === t.minor ? e.patch === t.patch ? e.pre && !t.pre ? -1 : !e.pre && t.pre ? 1 : e.pre && t.pre ? e.pre < t.pre ? -1 : +(e.pre > t.pre) : 0 : e.patch - t.patch : e.minor - t.minor : e.major - t.major, Gn = (e, t) => {
|
|
626
638
|
if (e === "*") return !0;
|
|
627
639
|
if (e.startsWith("^")) {
|
|
628
640
|
let n = L(e.slice(1));
|
|
@@ -644,29 +656,29 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
644
656
|
}
|
|
645
657
|
let r = L(e);
|
|
646
658
|
return r ? R(t, r) === 0 : !1;
|
|
647
|
-
},
|
|
659
|
+
}, Kn = (e) => {
|
|
648
660
|
let t = e.startsWith("@") ? e.replace(/^@[^/]+\//, "") : e;
|
|
649
661
|
return (t.startsWith("plugin-") ? t.slice(7) : t).replace(/-([a-zA-Z0-9])/g, (e, t) => t.toUpperCase());
|
|
650
|
-
},
|
|
662
|
+
}, qn = (e) => {
|
|
651
663
|
let t = e.startsWith("@") ? e.replace(/^@[^/]+\//, "") : e;
|
|
652
664
|
return (t.startsWith("plugin-") ? t.slice(7) : t).replace(/#/g, "--").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
653
|
-
},
|
|
654
|
-
let e = globalThis, t = e[
|
|
665
|
+
}, Jn = Symbol.for("@voltro/protocol/pluginAliases"), z = () => {
|
|
666
|
+
let e = globalThis, t = e[Jn];
|
|
655
667
|
if (t instanceof Map) return t;
|
|
656
668
|
let n = /* @__PURE__ */ new Map();
|
|
657
|
-
return e[
|
|
658
|
-
},
|
|
669
|
+
return e[Jn] = n, n;
|
|
670
|
+
}, Yn = (e) => {
|
|
659
671
|
let t = z();
|
|
660
672
|
for (let [n, r] of Object.entries(e)) {
|
|
661
673
|
if (r === "") continue;
|
|
662
674
|
let e = t.get(n);
|
|
663
675
|
e === void 0 ? t.set(n, /* @__PURE__ */ new Set([r])) : e.add(r);
|
|
664
676
|
}
|
|
665
|
-
},
|
|
677
|
+
}, Xn = () => {
|
|
666
678
|
z().clear();
|
|
667
|
-
},
|
|
679
|
+
}, Zn = (e, t) => {
|
|
668
680
|
let n = z().get(e);
|
|
669
|
-
if (n === void 0 || n.size === 0) return `${
|
|
681
|
+
if (n === void 0 || n.size === 0) return `${Kn(e)}.${t}`;
|
|
670
682
|
if (n.size === 1) return `${[...n][0]}.${t}`;
|
|
671
683
|
let r = [...n].filter((e) => !e.includes("#"));
|
|
672
684
|
if (r.length === 1) return `${r[0]}.${t}`;
|
|
@@ -732,23 +744,23 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
732
744
|
if (t.kind === "admission") return e.kind === (t.outcome.kind === "cancelled" ? "confirmed" : "already-terminal");
|
|
733
745
|
let n = t.snapshot;
|
|
734
746
|
return n.workflowName !== e.workflowName || n.id.length === 0 || n.executionId.length === 0 ? !1 : e.kind === "accepted" ? n.status === "running" || n.status === "suspended" : e.kind === "confirmed" ? n.status === "cancelled" : e.kind === "already-terminal" && (n.status === "succeeded" || n.status === "failed" || n.status === "cancelled");
|
|
735
|
-
}, { message: () => "Queued cancellation acknowledgement contradicts its receipt or observation" })),
|
|
747
|
+
}, { message: () => "Queued cancellation acknowledgement contradicts its receipt or observation" })), Qn = p.Literal("cancel", "terminate", "abandon"), $n = p.Struct({
|
|
736
748
|
mode: p.String,
|
|
737
749
|
dueAt: p.NullOr(p.Number),
|
|
738
750
|
retryAfterMs: p.NullOr(p.Number),
|
|
739
751
|
intentId: p.NullOr(p.String)
|
|
740
|
-
}),
|
|
752
|
+
}), er = p.Struct({
|
|
741
753
|
id: p.String,
|
|
742
754
|
workflowName: p.String,
|
|
743
755
|
executionId: p.NullOr(p.String),
|
|
744
756
|
status: p.Literal("running", "succeeded", "queued", "dropped", "skipped", "replayed"),
|
|
745
|
-
deferral: p.optional(
|
|
746
|
-
}),
|
|
757
|
+
deferral: p.optional($n)
|
|
758
|
+
}), tr = (e, t) => {
|
|
747
759
|
let n = I(e);
|
|
748
760
|
return m.make(e.name, {
|
|
749
761
|
payload: x(e.payloadSchema, e.name, N(n.guards)),
|
|
750
|
-
success:
|
|
751
|
-
error:
|
|
762
|
+
success: er,
|
|
763
|
+
error: cn({
|
|
752
764
|
errorSchema: e.errorSchema,
|
|
753
765
|
guards: n.guards
|
|
754
766
|
}, t)
|
|
@@ -772,12 +784,12 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
772
784
|
durationMs: p.NullOr(p.Number),
|
|
773
785
|
traceId: p.NullOr(p.String),
|
|
774
786
|
parentExecutionId: p.NullOr(p.String),
|
|
775
|
-
parentClosePolicy: p.NullOr(
|
|
776
|
-
}),
|
|
787
|
+
parentClosePolicy: p.NullOr(Qn)
|
|
788
|
+
}), nr = p.Struct({
|
|
777
789
|
tag: p.optional(p.String),
|
|
778
790
|
status: p.optional(V),
|
|
779
791
|
limit: p.optional(p.Number)
|
|
780
|
-
}),
|
|
792
|
+
}), rr = p.Struct({
|
|
781
793
|
id: p.String,
|
|
782
794
|
runId: p.String,
|
|
783
795
|
stepName: p.String,
|
|
@@ -792,7 +804,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
792
804
|
startedAt: p.Date,
|
|
793
805
|
completedAt: p.NullOr(p.Date),
|
|
794
806
|
durationMs: p.NullOr(p.Number)
|
|
795
|
-
}),
|
|
807
|
+
}), ir = p.Struct({
|
|
796
808
|
id: p.String,
|
|
797
809
|
runId: p.String,
|
|
798
810
|
eventType: p.String,
|
|
@@ -800,7 +812,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
800
812
|
occurredAt: p.Date,
|
|
801
813
|
stepName: p.NullOr(p.String),
|
|
802
814
|
attempt: p.NullOr(p.Number)
|
|
803
|
-
}),
|
|
815
|
+
}), ar = p.Struct({
|
|
804
816
|
id: p.String,
|
|
805
817
|
name: p.String,
|
|
806
818
|
payload: p.Unknown,
|
|
@@ -808,7 +820,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
808
820
|
subject: p.NullOr(p.Unknown),
|
|
809
821
|
traceId: p.NullOr(p.String),
|
|
810
822
|
occurredAt: p.Date
|
|
811
|
-
}),
|
|
823
|
+
}), or = p.Struct({
|
|
812
824
|
id: p.String,
|
|
813
825
|
eventId: p.String,
|
|
814
826
|
eventName: p.String,
|
|
@@ -821,10 +833,10 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
821
833
|
errorMessage: p.NullOr(p.String),
|
|
822
834
|
createdAt: p.Date,
|
|
823
835
|
completedAt: p.NullOr(p.Date)
|
|
824
|
-
}),
|
|
836
|
+
}), sr = p.Struct({ id: p.String }), K = p.Struct({ runId: p.String }), cr = p.Struct({
|
|
825
837
|
name: p.optional(p.String),
|
|
826
838
|
limit: p.optional(p.Number)
|
|
827
|
-
}),
|
|
839
|
+
}), lr = p.Struct({ eventId: p.String }), q = p.Struct({
|
|
828
840
|
requestId: p.NonEmptyString,
|
|
829
841
|
cause: p.optionalWith(p.Struct({
|
|
830
842
|
reason: p.NonEmptyString,
|
|
@@ -837,7 +849,7 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
837
849
|
workflowName: p.String,
|
|
838
850
|
executionId: p.String,
|
|
839
851
|
control: p.optional(q)
|
|
840
|
-
}),
|
|
852
|
+
}), ur = p.Struct({
|
|
841
853
|
workflowName: p.NonEmptyString,
|
|
842
854
|
executionId: p.NonEmptyString,
|
|
843
855
|
operation: p.Literal("cancel", "resume"),
|
|
@@ -846,106 +858,106 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
846
858
|
workflowName: p.NonEmptyString,
|
|
847
859
|
receiptId: p.NonEmptyString,
|
|
848
860
|
requestId: p.NonEmptyString
|
|
849
|
-
}),
|
|
861
|
+
}), dr = p.Struct({
|
|
850
862
|
...Y.fields,
|
|
851
863
|
cause: q.fields.cause
|
|
852
|
-
}),
|
|
864
|
+
}), fr = j({
|
|
853
865
|
name: "__voltro.workflow.queued.cancel",
|
|
854
|
-
input:
|
|
866
|
+
input: dr,
|
|
855
867
|
output: W,
|
|
856
868
|
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
857
|
-
}),
|
|
869
|
+
}), pr = k({
|
|
858
870
|
name: "__voltro.workflow.queued.control",
|
|
859
871
|
input: Y,
|
|
860
872
|
output: W,
|
|
861
873
|
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
862
|
-
}),
|
|
874
|
+
}), mr = k({
|
|
863
875
|
name: "__voltro.workflow.control",
|
|
864
|
-
input:
|
|
876
|
+
input: ur,
|
|
865
877
|
output: U,
|
|
866
878
|
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
867
|
-
}),
|
|
879
|
+
}), hr = p.Struct({
|
|
868
880
|
id: p.String,
|
|
869
881
|
signalName: p.String,
|
|
870
882
|
payload: p.optional(p.Unknown)
|
|
871
|
-
}),
|
|
883
|
+
}), gr = p.Struct({
|
|
872
884
|
id: p.String,
|
|
873
885
|
updateName: p.String,
|
|
874
886
|
payload: p.optional(p.Unknown),
|
|
875
887
|
timeoutMs: p.optional(p.Number)
|
|
876
|
-
}),
|
|
888
|
+
}), _r = p.Struct({
|
|
877
889
|
eventId: p.String,
|
|
878
890
|
updateId: p.String,
|
|
879
891
|
completedEventId: p.String,
|
|
880
892
|
result: p.Unknown
|
|
881
|
-
}),
|
|
893
|
+
}), vr = k({
|
|
882
894
|
name: "__voltro.workflow.run",
|
|
883
895
|
source: "_voltro_workflow_runs",
|
|
884
|
-
input:
|
|
896
|
+
input: sr,
|
|
885
897
|
output: p.Array(G),
|
|
886
898
|
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
887
|
-
}),
|
|
899
|
+
}), yr = k({
|
|
888
900
|
name: "__voltro.workflow.runs",
|
|
889
901
|
source: "_voltro_workflow_runs",
|
|
890
|
-
input:
|
|
902
|
+
input: nr,
|
|
891
903
|
output: p.Array(G),
|
|
892
904
|
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
893
|
-
}),
|
|
905
|
+
}), br = k({
|
|
894
906
|
name: "__voltro.workflow.run.steps",
|
|
895
907
|
source: "_voltro_workflow_run_steps",
|
|
896
908
|
input: K,
|
|
897
|
-
output: p.Array(
|
|
909
|
+
output: p.Array(rr),
|
|
898
910
|
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
899
|
-
}),
|
|
911
|
+
}), xr = k({
|
|
900
912
|
name: "__voltro.workflow.run.events",
|
|
901
913
|
source: "_voltro_workflow_run_events",
|
|
902
914
|
input: K,
|
|
903
|
-
output: p.Array(
|
|
915
|
+
output: p.Array(ir),
|
|
904
916
|
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
905
|
-
}),
|
|
917
|
+
}), Sr = k({
|
|
906
918
|
name: "__voltro.workflow.domainEvents",
|
|
907
919
|
source: "_voltro_workflow_events",
|
|
908
|
-
input:
|
|
909
|
-
output: p.Array(
|
|
920
|
+
input: cr,
|
|
921
|
+
output: p.Array(ar),
|
|
910
922
|
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
911
|
-
}),
|
|
923
|
+
}), Cr = k({
|
|
912
924
|
name: "__voltro.workflow.event.deliveries",
|
|
913
925
|
source: "_voltro_workflow_event_deliveries",
|
|
914
|
-
input:
|
|
915
|
-
output: p.Array(
|
|
926
|
+
input: lr,
|
|
927
|
+
output: p.Array(or),
|
|
916
928
|
openAccess: "the answer is scoped to the caller in the executor — a system or admin:full subject sees every row, an authenticated subject the rows of its own tenant, an anonymous caller none"
|
|
917
|
-
}),
|
|
929
|
+
}), wr = j({
|
|
918
930
|
name: "__voltro.workflow.cancel",
|
|
919
931
|
input: J,
|
|
920
932
|
output: U,
|
|
921
933
|
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
922
|
-
}),
|
|
934
|
+
}), Tr = j({
|
|
923
935
|
name: "__voltro.workflow.resume",
|
|
924
936
|
input: J,
|
|
925
937
|
output: U,
|
|
926
938
|
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
927
|
-
}),
|
|
939
|
+
}), Er = j({
|
|
928
940
|
name: "__voltro.workflow.signal",
|
|
929
|
-
input:
|
|
941
|
+
input: hr,
|
|
930
942
|
output: p.Struct({ eventId: p.String }),
|
|
931
943
|
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
932
|
-
}),
|
|
944
|
+
}), Dr = j({
|
|
933
945
|
name: "__voltro.workflow.update",
|
|
934
|
-
input:
|
|
935
|
-
output:
|
|
946
|
+
input: gr,
|
|
947
|
+
output: _r,
|
|
936
948
|
openAccess: "authorization happens in the executor against the target run — the caller must be authenticated and belong to the run's tenant, and a missing run answers exactly like a foreign one"
|
|
937
|
-
}),
|
|
938
|
-
runId:
|
|
939
|
-
requestId:
|
|
940
|
-
}),
|
|
941
|
-
...
|
|
949
|
+
}), Or = p.NonEmptyString.pipe(p.filter((e) => e.trim().length > 0, { message: () => "A workflow retry identity must not be blank" })), kr = p.Struct({
|
|
950
|
+
runId: Or,
|
|
951
|
+
requestId: Or
|
|
952
|
+
}), Ar = p.Struct({
|
|
953
|
+
...kr.fields,
|
|
942
954
|
payloadOverride: p.optional(p.Unknown)
|
|
943
955
|
}), X = {
|
|
944
956
|
requestId: p.NonEmptyString,
|
|
945
957
|
originalRunId: p.NonEmptyString,
|
|
946
958
|
originalExecutionId: p.NonEmptyString,
|
|
947
959
|
workflowName: p.NonEmptyString
|
|
948
|
-
},
|
|
960
|
+
}, jr = p.Union(p.Struct({
|
|
949
961
|
...X,
|
|
950
962
|
kind: p.Literal("queued"),
|
|
951
963
|
nextAttemptAt: p.NullOr(p.Number)
|
|
@@ -966,38 +978,38 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
966
978
|
...X,
|
|
967
979
|
kind: p.Literal("rejected"),
|
|
968
980
|
failureId: p.NonEmptyString
|
|
969
|
-
})),
|
|
981
|
+
})), Mr = "__voltro.undo.log", Nr = "__voltro.undo.apply", Pr = "__voltro.undo.redo", Fr = p.Struct({
|
|
970
982
|
id: p.String,
|
|
971
983
|
tag: p.String,
|
|
972
984
|
label: p.NullOr(p.String),
|
|
973
985
|
undone: p.Boolean,
|
|
974
986
|
crossesAction: p.Boolean,
|
|
975
987
|
createdAt: p.String
|
|
976
|
-
}),
|
|
988
|
+
}), Ir = class extends p.TaggedError()("UndoNotFound", { invocationId: p.String }) {}, Lr = class extends p.TaggedError()("UndoForbidden", { invocationId: p.String }) {}, Rr = class extends p.TaggedError()("UndoConflict", {
|
|
977
989
|
invocationId: p.String,
|
|
978
990
|
reason: p.Literal("conflict", "action")
|
|
979
|
-
}) {},
|
|
980
|
-
name:
|
|
991
|
+
}) {}, zr = p.Union(Ir, Lr, Rr), Br = k({
|
|
992
|
+
name: Mr,
|
|
981
993
|
source: "_voltro_undo_log",
|
|
982
994
|
input: p.Struct({ limit: p.optional(p.Number) }),
|
|
983
|
-
output: p.Array(
|
|
995
|
+
output: p.Array(Fr),
|
|
984
996
|
openAccess: "subject-scoped by construction: lists only the calling subject's own undoable actions"
|
|
985
|
-
}),
|
|
986
|
-
name:
|
|
997
|
+
}), Vr = A({
|
|
998
|
+
name: Nr,
|
|
987
999
|
input: p.Struct({ invocationId: p.String }),
|
|
988
1000
|
output: p.Struct({ ok: p.Boolean }),
|
|
989
|
-
error:
|
|
1001
|
+
error: zr,
|
|
990
1002
|
openAccess: "subject-scoped by construction: undo is per-actor — another subject's invocation fails typed with UndoForbidden"
|
|
991
|
-
}),
|
|
992
|
-
name:
|
|
1003
|
+
}), Hr = A({
|
|
1004
|
+
name: Pr,
|
|
993
1005
|
input: p.Struct({ invocationId: p.String }),
|
|
994
1006
|
output: p.Struct({ ok: p.Boolean }),
|
|
995
|
-
error:
|
|
1007
|
+
error: zr,
|
|
996
1008
|
openAccess: "subject-scoped by construction: redo is per-actor — another subject's invocation fails typed with UndoForbidden"
|
|
997
|
-
}),
|
|
1009
|
+
}), Ur = class extends p.TaggedError()("TenantScopeViolation", {
|
|
998
1010
|
table: p.String,
|
|
999
1011
|
reason: p.String
|
|
1000
|
-
}) {},
|
|
1012
|
+
}) {}, Wr = class extends p.TaggedError()("StoreOperationFailed", {
|
|
1001
1013
|
operation: p.String,
|
|
1002
1014
|
table: p.String,
|
|
1003
1015
|
cause: p.String
|
|
@@ -1005,28 +1017,28 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
1005
1017
|
get message() {
|
|
1006
1018
|
return `${this.operation} on '${this.table}' failed: ${this.cause}`;
|
|
1007
1019
|
}
|
|
1008
|
-
},
|
|
1020
|
+
}, Gr = class extends p.TaggedError()("NoRowFound", {
|
|
1009
1021
|
table: p.String,
|
|
1010
1022
|
found: p.Number
|
|
1011
1023
|
}) {
|
|
1012
1024
|
get message() {
|
|
1013
1025
|
return this.found === 0 ? `expected exactly one row in '${this.table}', found none` : `expected exactly one row in '${this.table}', found more than one`;
|
|
1014
1026
|
}
|
|
1015
|
-
},
|
|
1027
|
+
}, Kr = class extends p.TaggedError()("TableValidationFailed", {
|
|
1016
1028
|
table: p.String,
|
|
1017
1029
|
summary: p.String,
|
|
1018
1030
|
issues: p.Array(p.Struct({
|
|
1019
1031
|
path: p.String,
|
|
1020
1032
|
message: p.String
|
|
1021
1033
|
}))
|
|
1022
|
-
}) {},
|
|
1034
|
+
}) {}, qr = class extends p.TaggedError()("TenantRowNotFound", {
|
|
1023
1035
|
table: p.String,
|
|
1024
1036
|
id: p.String,
|
|
1025
1037
|
reason: p.String
|
|
1026
|
-
}) {},
|
|
1038
|
+
}) {}, Jr = class extends p.TaggedError()("ServerOnlyColumnWrite", {
|
|
1027
1039
|
table: p.String,
|
|
1028
1040
|
columns: p.Array(p.String)
|
|
1029
|
-
}) {},
|
|
1041
|
+
}) {}, Yr = class extends p.TaggedError()("ConstraintViolation", {
|
|
1030
1042
|
kind: p.Literal("foreignKey", "foreignKeyInUse", "unique", "notNull", "check"),
|
|
1031
1043
|
table: p.String,
|
|
1032
1044
|
operation: p.String,
|
|
@@ -1045,14 +1057,14 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
1045
1057
|
case "check": return `check constraint ${t}: the row does not satisfy it`;
|
|
1046
1058
|
}
|
|
1047
1059
|
}
|
|
1048
|
-
},
|
|
1049
|
-
name:
|
|
1060
|
+
}, Xr = "__voltro.approvals.pending", Zr = "__voltro.approvals.decide", Qr = k({
|
|
1061
|
+
name: Xr,
|
|
1050
1062
|
source: "_voltro_approvals",
|
|
1051
1063
|
input: p.Struct({ limit: p.optional(p.Number) }),
|
|
1052
|
-
output: p.Array(
|
|
1064
|
+
output: p.Array(zt),
|
|
1053
1065
|
openAccess: "the answer is scoped to the caller — a row appears only if they requested it or hold its recorded approver scopes, so an anonymous caller sees nothing"
|
|
1054
|
-
}),
|
|
1055
|
-
name:
|
|
1066
|
+
}), $r = A({
|
|
1067
|
+
name: Zr,
|
|
1056
1068
|
input: p.Struct({
|
|
1057
1069
|
approvalId: p.String,
|
|
1058
1070
|
decision: p.Literal("approve", "reject"),
|
|
@@ -1062,35 +1074,35 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
1062
1074
|
approvalId: p.String,
|
|
1063
1075
|
status: p.Literal("approved", "rejected")
|
|
1064
1076
|
}),
|
|
1065
|
-
error:
|
|
1077
|
+
error: Rt,
|
|
1066
1078
|
openAccess: "the approver authority is the PENDING ROW's own recorded scopes (plus an unconditional self-approval refusal), checked in-handler — a descriptor-level scope would have to be the union of every approval-requiring procedure in the app"
|
|
1067
|
-
}), Z = "__voltro.connections.list",
|
|
1079
|
+
}), Z = "__voltro.connections.list", ei = "__voltro.connections.start", ti = "__voltro.connections.submitToken", ni = "__voltro.connections.disconnect", Q = p.Literal("oauth2", "pat"), ri = p.Literal("disconnected", "connected", "expired", "revoked", "error"), ii = p.Struct({
|
|
1068
1080
|
connectionId: p.String,
|
|
1069
1081
|
kind: Q,
|
|
1070
1082
|
label: p.String,
|
|
1071
|
-
status:
|
|
1083
|
+
status: ri,
|
|
1072
1084
|
accountId: p.NullOr(p.String),
|
|
1073
1085
|
accountLabel: p.NullOr(p.String),
|
|
1074
1086
|
scopes: p.Array(p.String),
|
|
1075
1087
|
expiresAt: p.NullOr(p.String),
|
|
1076
1088
|
lastError: p.NullOr(p.String),
|
|
1077
1089
|
connectedAt: p.NullOr(p.String)
|
|
1078
|
-
}),
|
|
1090
|
+
}), ai = class extends p.TaggedError()("ConnectionNotDeclared", { connectionId: p.String }) {}, oi = class extends p.TaggedError()("ConnectionSubjectRequired", { connectionId: p.String }) {}, si = class extends p.TaggedError()("ConnectionKindMismatch", {
|
|
1079
1091
|
connectionId: p.String,
|
|
1080
1092
|
expected: Q,
|
|
1081
1093
|
actual: Q
|
|
1082
|
-
}) {},
|
|
1094
|
+
}) {}, ci = class extends p.TaggedError()("ConnectionHandshakeFailed", {
|
|
1083
1095
|
connectionId: p.String,
|
|
1084
1096
|
reason: p.String,
|
|
1085
1097
|
transient: p.Boolean
|
|
1086
|
-
}) {}, $ = p.Union(
|
|
1098
|
+
}) {}, $ = p.Union(ai, oi, si, ci), li = k({
|
|
1087
1099
|
name: Z,
|
|
1088
1100
|
source: "_voltro_connections",
|
|
1089
1101
|
input: p.Struct({}),
|
|
1090
|
-
output: p.Array(
|
|
1102
|
+
output: p.Array(ii),
|
|
1091
1103
|
openAccess: "self-scoped read: projects the declared connections for the calling subject only (its own connect/disconnect state)"
|
|
1092
|
-
}),
|
|
1093
|
-
name:
|
|
1104
|
+
}), ui = j({
|
|
1105
|
+
name: ei,
|
|
1094
1106
|
input: p.Struct({
|
|
1095
1107
|
connectionId: p.String,
|
|
1096
1108
|
redirectTo: p.optional(p.String)
|
|
@@ -1101,8 +1113,8 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
1101
1113
|
}),
|
|
1102
1114
|
error: $,
|
|
1103
1115
|
openAccess: "self-service: begins an oauth handshake that stores a credential for the calling subject only; anonymous callers fail typed with ConnectionSubjectRequired"
|
|
1104
|
-
}),
|
|
1105
|
-
name:
|
|
1116
|
+
}), di = A({
|
|
1117
|
+
name: ti,
|
|
1106
1118
|
input: p.Struct({
|
|
1107
1119
|
connectionId: p.String,
|
|
1108
1120
|
token: p.String
|
|
@@ -1110,21 +1122,21 @@ var gt = p.Union(p.String, p.Number), h = p.Record({
|
|
|
1110
1122
|
output: p.Struct({ ok: p.Boolean }),
|
|
1111
1123
|
error: $,
|
|
1112
1124
|
openAccess: "self-service: stores a pasted token as the calling subject's own credential; anonymous callers fail typed with ConnectionSubjectRequired"
|
|
1113
|
-
}),
|
|
1114
|
-
name:
|
|
1125
|
+
}), fi = A({
|
|
1126
|
+
name: ni,
|
|
1115
1127
|
input: p.Struct({ connectionId: p.String }),
|
|
1116
1128
|
output: p.Struct({ ok: p.Boolean }),
|
|
1117
1129
|
error: $,
|
|
1118
1130
|
openAccess: "self-service: deletes only the calling subject's own credential row for this connection"
|
|
1119
|
-
}),
|
|
1131
|
+
}), pi = (e) => {
|
|
1120
1132
|
let t = e instanceof Date ? e.getTime() : typeof e == "number" ? e : typeof e == "string" ? new Date(e).getTime() : 0;
|
|
1121
1133
|
return Number.isNaN(t) ? 0 : t;
|
|
1122
|
-
},
|
|
1134
|
+
}, mi = 4001, hi = /* @__PURE__ */ new Set(["/rpc", "/ws"]), gi = (e) => {
|
|
1123
1135
|
for (let [t, n] of Object.entries(e.limits ?? {})) if (n !== void 0 && (!Number.isSafeInteger(n) || n <= 0)) throw Error(`defineWebSocket: limits.${t} must be a positive safe integer`);
|
|
1124
1136
|
if (!e.path.startsWith("/")) throw Error(`defineWebSocket: path must be absolute, got '${e.path}'`);
|
|
1125
|
-
if (
|
|
1137
|
+
if (hi.has(e.path)) throw Error(`defineWebSocket: '${e.path}' is the framework's own socket surface — mount the gateway elsewhere`);
|
|
1126
1138
|
if (e.auth !== "subject" && e.auth !== "public") throw Error(`defineWebSocket: auth must be 'subject' or 'public' (explicitly — an unauthenticated socket has to be a decision somebody wrote down), got '${String(e.auth)}'`);
|
|
1127
1139
|
return e;
|
|
1128
|
-
},
|
|
1140
|
+
}, _i = 1;
|
|
1129
1141
|
//#endregion
|
|
1130
|
-
export { Ne as ADMIN_SCOPE, Oe as APIKEY_ISSUE_ORG_SCOPE, je as APIKEY_ISSUE_OTHER_SCOPE, Ce as APIKEY_ISSUE_SELF_SCOPE,
|
|
1142
|
+
export { Ne as ADMIN_SCOPE, Oe as APIKEY_ISSUE_ORG_SCOPE, je as APIKEY_ISSUE_OTHER_SCOPE, Ce as APIKEY_ISSUE_SELF_SCOPE, Zr as APPROVALS_DECIDE_TAG, Xr as APPROVALS_PENDING_TAG, Rt as ApprovalDecisionErrors, Lt as ApprovalErrors, S as ApprovalExpired, It as ApprovalForbidden, Mt as ApprovalNotFound, Ft as ApprovalNotPending, Pt as ApprovalRejected, jt as ApprovalRequired, Nt as ApprovalSelfApproval, C as ApprovalUnavailable, i as AuthMiddleware, Et as BusinessRuleViolation, Z as CONNECTIONS_LIST_TAG, ni as CONNECTION_DISCONNECT_TAG, ei as CONNECTION_START_TAG, ti as CONNECTION_SUBMIT_TOKEN_TAG, ci as ConnectionHandshakeFailed, ae as ConnectionInfo, Te as ConnectionInfoMiddleware, Q as ConnectionKind, si as ConnectionKindMismatch, ai as ConnectionNotDeclared, ii as ConnectionState, ri as ConnectionStatus, oi as ConnectionSubjectRequired, Yr as ConstraintViolation, t as DEFAULT_SCOPE_CACHE_MAX_ENTRIES, we as DEFAULT_SCOPE_CACHE_TTL_MS, _e as DEVICES_MANAGE_SCOPE, jn as EVENT_ROUTE_SEP, On as EventKeyInvalid, Dn as EventPayloadInvalid, kn as EventPayloadTooLarge, mi as GATEWAY_CREDENTIAL_EXPIRED_CLOSE_CODE, re as IMPERSONATION_METADATA_KEY, st as JSONAPI_MEDIA_TYPE, _n as MAX_EVENT_ENVELOPE_BYTES, ft as MUTATION_IN_FLIGHT_RETRY_AFTER_MS, qe as MutationConnectionRequired, dt as MutationInFlight, Gr as NoRowFound, nt as PROBLEM_JSON, _i as PROTOCOL_VERSION, zt as PendingApproval, w as REACTIVITY_CHANNEL_PREFIX, at as REST_PROFILE_NAMES, f as ScopeError, Jr as ServerOnlyColumnWrite, Wr as StoreOperationFailed, ie as Subject, oe as SubjectIdentity, o as SubjectService, Kr as TableValidationFailed, qr as TenantRowNotFound, Ur as TenantScopeViolation, ge as TenantSelectionRefused, Nr as UNDO_APPLY_TAG, Mr as UNDO_LOG_TAG, Pr as UNDO_REDO_TAG, l as Unauthenticated, Rr as UndoConflict, Lr as UndoForbidden, Fr as UndoLogEntry, Ir as UndoNotFound, Ot as ValidationError, kt as ValidationErrors, Dt as ValidationIssue, F as WorkflowAccessProperty, J as WorkflowControlInputSchema, q as WorkflowControlIntentSchema, ur as WorkflowControlReadInputSchema, U as WorkflowControlResultSchema, ar as WorkflowDomainEventRowSchema, cr as WorkflowDomainEventsInputSchema, lr as WorkflowEventDeliveriesInputSchema, or as WorkflowEventDeliveryRowSchema, Qn as WorkflowParentClosePolicySchema, dr as WorkflowQueuedCancelInputSchema, W as WorkflowQueuedCancellationResultSchema, Y as WorkflowQueuedControlTargetSchema, Ar as WorkflowRetryCommandSchema, jr as WorkflowRetryObservationSchema, kr as WorkflowRetryTargetSchema, ir as WorkflowRunEventRowSchema, er as WorkflowRunHandleSchema, sr as WorkflowRunRefSchema, G as WorkflowRunRowSchema, H as WorkflowRunSnapshotSchema, V as WorkflowRunStatusSchema, rr as WorkflowRunStepRowSchema, K as WorkflowRunTableRefSchema, nr as WorkflowRunsInputSchema, hr as WorkflowSignalInputSchema, $n as WorkflowStartDeferralSchema, B as WorkflowStartTerminalOutcomeSchema, gr as WorkflowUpdateInputSchema, _r as WorkflowUpdateResultSchema, fn as actionToRpc, Me as advisoryResourceGuardWarning, ne as anonymousSubject, Ct as applyRowPatch, ce as applyScopeDecision, $r as approvalsDecideDescriptor, Qr as approvalsPendingQueryDescriptor, ze as assertAuthenticated, Ln as attachWorkflowAccess, Ue as beginIdempotent, Wn as checkFrameworkCompat, ve as checkGuards, Ee as checkGuardsEffect, tt as compareSurfaces, n as composeAuthStrategies, Hn as composeRpcInterceptors, fi as connectionDisconnectDescriptor, ui as connectionStartDescriptor, di as connectionSubmitTokenDescriptor, li as connectionsListQueryDescriptor, Kt as declaredReactivityChannelKeys, j as defineAction, yn as defineEvent, A as defineMutation, zn as definePlugin, Bn as definePluginRoute, Un as definePluginService, k as defineQuery, nn as defineStream, gi as defineWebSocket, xt as diffRows, Pe as effectiveScopes, An as encodeEventKey, $e as encodeRestError, Ke as encodeRestSuccess, mn as errorTag, Sn as eventAttached, bn as eventEnvelope, xn as eventGap, wn as eventResumePoint, Mn as eventRoute, Cn as eventStreamEvent, Tn as eventSubscribeInput, En as eventToRpc, Be as failIdempotent, At as fieldIssuesOf, ye as findAdvisoryResourceGuards, ut as finishIdempotent, wt as firstEventValue, Pn as formatEventRoute, be as getPolicyGuardResolver, ke as getResourceScopeResolver, I as getWorkflowAccess, Qe as groupShapeDigests, tn as hasAccessDecision, fe as hasCallbackRoutes, De as hasEffectiveScope, N as hasEnforcedGuard, Se as hasScope, g as idToPath, We as idempotencyScope, b as inputLabel, xe as isAuthenticatedCheck, en as isAuthenticatedGuard, vn as isEventDescriptor, St as isIdKeyed, u as isOpenAccess, Fe as isPolicyCheck, $t as isPolicyGuard, Wt as isReactivityChannel, Gt as isReactivityChannelKey, c as isSystemSubject, se as isTenantSelectable, Zt as isWireReachable, de as makeScopeCache, He as memoryIdempotencyStore, Re as missingAccessDecision, dn as mutationToRpc, rt as nextLink, gn as normalizeDescriptor, Jt as normalizeSource, d as openAccessSpec, pe as orderByBearerPrefix, Nn as parseEventRoute, yt as pathToId, Kn as pluginAlias, Vn as pluginInstanceName, qn as pluginSlug, Zn as pluginTag, ot as problemDetails, Je as problemTypeOf, Ze as procedureShapeDigest, Xt as publishReactivity, Xe as publishServerError, un as queryToRpc, s as rawImpersonationMark, Ut as reactivityChannel, Yn as registerPluginAliases, Le as requireScope, Xn as resetPluginAliases, le as resolvePolicyAction, et as resolveRestFormat, In as resolveWorkflowAccess, ct as restViewSchema, _t as rowPatchOpSchema, vt as rowPatchSchema, Ge as schemaShape, te as scopeCacheKey, e as selectSubjectTenant, r as setEffectiveScopes, Ie as setPolicyGuardResolver, ue as setResourceScopeResolver, lt as shapeDigest, qt as sourceKeys, pn as streamToRpc, x as strictInput, Ae as subjectAuditActorId, ee as subjectIdentity, a as subjectScopes, it as subscribeServerErrors, y as subscriptionEvent, he as systemSubject, me as tenantScopedSubject, hn as toRpc, Ye as toSnakeCase, pi as tsMs, Yt as undeclaredChannelKeys, Vr as undoApplyDescriptor, Br as undoLogQueryDescriptor, Hr as undoRedoDescriptor, P as wireErrorUnion, ln as withRpcTag, Rn as workflowAccessDescriptor, wr as workflowCancelDescriptor, mr as workflowControlQueryDescriptor, Sr as workflowDomainEventsQueryDescriptor, Cr as workflowEventDeliveriesQueryDescriptor, fr as workflowQueuedCancelDescriptor, pr as workflowQueuedControlQueryDescriptor, Tr as workflowResumeDescriptor, xr as workflowRunEventsQueryDescriptor, vr as workflowRunQueryDescriptor, br as workflowRunStepsQueryDescriptor, yr as workflowRunsQueryDescriptor, Er as workflowSignalDescriptor, tr as workflowToRpc, Dr as workflowUpdateDescriptor, cn as workflowWireErrorUnion, Ve as wsMutationIdempotencyScope };
|
package/dist/rest.d.ts
CHANGED
|
@@ -1289,6 +1289,9 @@ export declare interface RestRouteContext {
|
|
|
1289
1289
|
* REST handlers on the same capability surface as query/mutation/action
|
|
1290
1290
|
* handlers without creating a protocol-to-runtime package dependency. */
|
|
1291
1291
|
readonly app?: unknown;
|
|
1292
|
+
/** The caller's address, resolved through `security.trustedProxies` — the
|
|
1293
|
+
* value the listener's per-IP shield counts. Absent when unknown. */
|
|
1294
|
+
readonly remoteAddr?: string;
|
|
1292
1295
|
}
|
|
1293
1296
|
|
|
1294
1297
|
export declare interface RestRouteDescriptor<I, O> {
|
package/dist/rest.js
CHANGED
|
@@ -519,7 +519,8 @@ var w = (e, t, n) => t.method ?? (t.resource !== void 0 && n !== void 0 ? D("",
|
|
|
519
519
|
path: l.path,
|
|
520
520
|
query: l.query,
|
|
521
521
|
rawBody: l.rawBody,
|
|
522
|
-
..._ === void 0 ? {} : { app: _ }
|
|
522
|
+
..._ === void 0 ? {} : { app: _ },
|
|
523
|
+
...l.remoteAddr === void 0 ? {} : { remoteAddr: l.remoteAddr }
|
|
523
524
|
};
|
|
524
525
|
if (a.guards) for (let e of a.guards) {
|
|
525
526
|
let t = await e(x);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.80.0",
|
|
4
4
|
"description": "The Voltro wire + plugin contract — defineQuery/Mutation/Action/Stream, definePlugin, sessions / JWT / API-keys, and the RPC protocol.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@effect/experimental": "^0.61.1",
|
|
67
67
|
"@effect/sql": "^0.52.1",
|
|
68
|
-
"@voltro/database": "0.
|
|
69
|
-
"@voltro/logger": "0.
|
|
68
|
+
"@voltro/database": "0.80.0",
|
|
69
|
+
"@voltro/logger": "0.80.0",
|
|
70
70
|
"jose": "^6.2.12"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|