@voltro/protocol 0.14.0 → 0.15.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 +127 -0
- package/dist/apikey.d.ts +24 -0
- package/dist/{auth-DVrHg739.js → auth-BPdyOBsd.js} +12 -8
- package/dist/index.d.ts +56 -5
- package/dist/index.js +64 -60
- package/dist/jwt.d.ts +24 -0
- package/dist/rest.js +1 -1
- package/dist/session.js +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -39,6 +39,133 @@ _Changes staged for the next release accumulate here (rolled up from
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
+
## [0.15.0] — 2026-07-27
|
|
43
|
+
|
|
44
|
+
### ⚠ BREAKING
|
|
45
|
+
|
|
46
|
+
- **@voltro/cli** — **A rename now carries the ALIASED importers, not only the relative ones.**
|
|
47
|
+
|
|
48
|
+
An app's web build stopped compiling after `voltro update`: 84 relative imports were rewritten correctly, 163 aliased ones across 88 files were not, and `tsc` reported 249 errors on names that no longer existed. Nothing in the codemod's output hinted that a whole class of import had been skipped.
|
|
49
|
+
|
|
50
|
+
Two independent halves, and each alone leaves the imports stale:
|
|
51
|
+
|
|
52
|
+
- The codemod's ts-morph project was built with **no `baseUrl` and no `paths`**, so `@/components/link` resolved to nothing. It now gets the compiler's own shape — the raw `paths`, not the pre-resolved Vite table, because a `paths` target is relative to `baseUrl` by definition and an absolute one does not resolve. - `SourceFile.move()` rewrites relative specifiers and **nothing else**, which is correct on its own terms: ts-morph cannot know whether the alias mapping or the file is meant to change. So the codemods now rewrite the aliased ones themselves, narrowly — only specifiers that RESOLVED to the moved file, and only the trailing stem, which needs no alias table and so cannot disagree with one. The run reports how many it rewrote.
|
|
53
|
+
|
|
54
|
+
**A file an exact `paths` entry names is left alone and reported.** Renaming `link.tsx` while `"@/link": ["src/components/link.tsx"]` points at it leaves the mapping resolving to nothing — and the same path is usually repeated in a vite/vitest alias table no codemod owns. One app hit this and then saw a taxonomy violation reported on a file the codemod had itself created.
|
|
55
|
+
|
|
56
|
+
**`page/unsuffixed-in-pages` is now an error, not a warning.** The comment justifying the warning contradicted the scanner it described: that bucket is already narrowed to a default export nothing imports, which is what an unmigrated page looks like and what a co-located component never does. The failure it names is invisible everywhere else — an unmigrated route simply 404s, with a clean `tsc` and a green suite. One app finished a migration with 51 of them. `codemod: none`: the rename it asks for already ships as `0.14.0/03_pages-suffix`, and nothing about a user's SOURCE changes here — what changes is that `voltro check` now fails on a route that does not route.
|
|
57
|
+
- **@voltro/protocol, @voltro/client, @voltro/web** — **`errorTag` moves from `@voltro/client` to `@voltro/protocol`.**
|
|
58
|
+
|
|
59
|
+
It reads the `_tag` that `toRpc` writes, so it now lives beside `toRpc` — one file owning both ends of that contract. Where it used to live had a cost invisible from inside the framework: an app's shared error handler sat in a package that pulled only `@voltro/i18n`, and reading a tag would have meant depending on the entire client package for seven lines. They declined, and kept parsing message strings with a regex — the exact outcome the helper exists to prevent.
|
|
60
|
+
|
|
61
|
+
`@voltro/web` re-exports the client surface, so it loses the symbol too — the same codemod covers an app that imported it from there.
|
|
62
|
+
|
|
63
|
+
Not re-exported from `@voltro/client`: two import paths for one helper is how the next reader learns the wrong one. The transform codemod repoints the import, preserving an alias (`errorTag as tagOf`) and the type-only form, and merges into an existing `@voltro/protocol` import rather than adding a second.
|
|
64
|
+
|
|
65
|
+
While moving it, its doc comment gained the thing that matters at the call site and was only implied before: **`instanceof` does not hold on the client.** What arrives there was decoded from JSON and never constructed, so match on the tag, not on the class. One team read the old wording as a promise that `instanceof` works and was right to say so.
|
|
66
|
+
|
|
67
|
+
### Added
|
|
68
|
+
|
|
69
|
+
- **@voltro/protocol, @voltro/cli** — **An auth strategy reaches the app's DataStore, on `input.store`.**
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
const sessionStrategy: AuthStrategy = {
|
|
73
|
+
id: 'db-session',
|
|
74
|
+
resolve: async ({ headers, store }) => {
|
|
75
|
+
if (store === undefined) return { kind: 'skip' } // still booting
|
|
76
|
+
const [row] = await store.query(sessions.byToken(headers.authorization))
|
|
77
|
+
return row ? { kind: 'matched', subject: toSubject(row) } : { kind: 'skip' }
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Without it, a DB-backed strategy — a session row, an API-key record, a PAT table — had to open a SECOND connection path beside the framework's, to the same database the request store opens a moment later. One adopter's `auth/db.ts` is 105 lines of exactly that: a second `ManagedRuntime` plus a `MysqlClient`, load-bearing for their session lookup and their ApiKeyStore. Every DB-backed OIDC / SAML / PAT integration rebuilds it, which is what made this a framework gap rather than an app's problem.
|
|
83
|
+
|
|
84
|
+
It is the **same value** `auth.resolveScopes` already receives, through the **same lazy getter** — one ref, two consumers, rather than each caller reaching for the store its own way. That is deliberate: `voltro dev` builds the store AFTER the auth chain and `voltro serve` builds it BEFORE, so a value captured at config time would be `undefined` forever in dev and correct in production. The getter is read ONCE per request, not once per strategy.
|
|
85
|
+
|
|
86
|
+
`store` is `undefined` only while the store is still being built, and on an app with no store — a strategy should `skip` rather than throw. It is the BOOT store, not a request-scoped one: strategies resolve before a request store exists.
|
|
87
|
+
|
|
88
|
+
**Not narrowed to a read-only surface**, and the reason is worth stating: the narrower type would be the better guarantee, `DataStore` is the driver SPI, and giving strategies a different type from the one `resolveScopes` gets would put two views of one object in the same file. A strategy that writes during subject resolution is a design mistake; the type system is not going to catch it for you. Read users / sessions / keys, do not run domain writes.
|
|
89
|
+
|
|
90
|
+
### Fixed
|
|
91
|
+
|
|
92
|
+
- **@voltro/cli** — `voltro dev` terminates when its dev server does, instead of living forever.
|
|
93
|
+
|
|
94
|
+
The supervisor never watched its child die. `runChild` was an `Effect.acquireUseRelease` whose `use` was `Effect.never`, so `proc.on('exit')` existed ONLY in the release path — and that path runs when the fiber is interrupted (a restart, a signal), never when the child exits by itself. Two more places assumed the same thing: `runSupervisor` was typed `Effect<never>`, and `dev.ts` returned `new Promise(() => {})` after starting it.
|
|
95
|
+
|
|
96
|
+
So a boot that aborted — an unreachable database, a refused migration, a failed env gate — left the child dead and the supervisor waiting for a file change that nobody was there to make. Measured, not inferred: one developer machine carried 15 such `voltro dev` process pairs, `ppid=1`, the oldest 7 days old, every one a boot that had failed against a remote database. They hold a watcher and a terminal-less process each; in CI the same shape keeps a runner busy after the job "finished".
|
|
97
|
+
|
|
98
|
+
`use` now awaits the child (`awaitChildExit`) and the supervisor races that against the watch loop, so whichever happens first decides. A restart still does NOT end it — `stopChild` interrupts the fiber, so the deferred is never completed on that path. A child killed by a signal reports `code: null`, which is reported as a failure rather than a clean 0.
|
|
99
|
+
|
|
100
|
+
What a self-exit MEANS then depends on whether anyone is watching, because the two failure modes pull in opposite directions:
|
|
101
|
+
|
|
102
|
+
- **Interactive** (stdout is a TTY) — a crashed boot is something you are about to fix, so the supervisor says so and keeps watching. The next save restarts it, which is what every other dev server does; stopping would throw away the watcher mid-edit and make you retype the command. - **Non-interactive** — nobody is going to fix anything. `voltro dev` exits with the child's code, so a failed boot is a failed command. This is the case that produced the invisible processes, and the one CI actually waits on.
|
|
103
|
+
|
|
104
|
+
A CLEAN exit always stops, watched or not. `VOLTRO_DEV_KEEP_ALIVE=1|0` forces the answer for what the TTY check cannot see — a CI runner with a TTY allocated, or a wrapper that pipes output while a human still watches it — and cannot keep a clean exit alive, which would turn a deliberate shutdown into a hang.
|
|
105
|
+
|
|
106
|
+
Pinned against REAL child processes, because the defect was an Effect that never settled — a stubbed `once('exit')` that resolves is exactly what would have passed while the bug shipped.
|
|
107
|
+
- **@voltro/database** — **A user-facing message that names an API must have one — now checked in CI.**
|
|
108
|
+
|
|
109
|
+
The sibling of the claimed-wiring check. That one asserts a doc comment's claimed caller exists; this one asserts a message's claimed API exists. Same failure shape, worse audience: a doc comment is read by somebody browsing, a refusal by somebody already blocked and looking for the sanctioned way out.
|
|
110
|
+
|
|
111
|
+
It exists for a reported bug that nothing could have caught. The drop-table refusal offered, as its FIRST option, *"chain `.dropped()` on it"* — tables have no such marker, only columns do. Doc SAMPLES are typechecked; message strings are not, and cannot be. Three of one release's reported defects lived in that blind spot.
|
|
112
|
+
|
|
113
|
+
Three rules, each with an unambiguous answer, because a noisy gate is skipped and then costs more than it saves:
|
|
114
|
+
|
|
115
|
+
- a `VOLTRO_*` variable a message tells you to **set** must be read somewhere, - a `` `.method()` `` a message tells you to **chain** must be a callable MEMBER of a published type, - a `--flag` in a `voltro …` instruction must be parsed.
|
|
116
|
+
|
|
117
|
+
The member rule is the one that took two attempts. The first version asked "does this name exist in the public surface" and the motivating bug **passed it**: `dropped` is exported, as a free `dropped()` you write as a column's value. A dotted claim is a claim about something chainable, so a free function and a `readonly dropped?: boolean` data property are both correctly rejected now.
|
|
118
|
+
|
|
119
|
+
**It immediately found a second instance nobody had reported** — the drop-COLUMN refusal also said "chain `.dropped()`", one level down from the reported one, and the real spelling is `<column>: dropped()` as the field's value. Close enough to guess from, which is why it survived.
|
|
120
|
+
|
|
121
|
+
Ships with a `--selftest` that runs first in CI, for the reason the changelog gate has one: a check that has quietly stopped detecting anything still prints green, and green is read as evidence.
|
|
122
|
+
- **@voltro/database** — **Two migration refusals sent people the wrong way** — the worst place for a bad hint, because whoever reads one is already blocked and looking for the sanctioned way out.
|
|
123
|
+
|
|
124
|
+
**The drop-table refusal recommended an API that does not exist.** Its first option was *"add it to your declared set + chain `.dropped()` on it"*. There is no table-level `dropped()` — only the column marker. The recommendation was also the conceptually RIGHT one, which is what made it expensive: the two options that do work are both worse, so a reader picks the one they cannot follow.
|
|
125
|
+
|
|
126
|
+
There is now a real per-table answer: **`VOLTRO_DESTRUCTIVE_OK` accepts a table list**, not just `1`. `VOLTRO_DESTRUCTIVE_OK=old_things` acknowledges the data loss for that table and leaves every other lossy op in the plan blocked. `1` still means all of them — which is rarely what somebody means, and was previously the only way to say anything. A user with one intended drop and three other lossy ops had to acknowledge all four or hand-write a `DROP TABLE` migration, the path 0.14.0's own upgrade note warns against.
|
|
127
|
+
|
|
128
|
+
The message also states why there is deliberately no table marker: a dropped COLUMN leaves a slot worth documenting in the declaration; a dropped TABLE leaves nothing, so the marker would be a dead entry you must remember to delete.
|
|
129
|
+
|
|
130
|
+
**The drop-column refusal never mentioned `renamedFrom`.** It offered "chain `.dropped()`" or "restore the field" — and followed literally on a rename, the first costs exactly the data the user was trying to keep. When the plan drops AND adds columns on the same table, the message now leads with *"did you rename one?"* and names both sides. The evidence was in the plan the whole time.
|
|
131
|
+
|
|
132
|
+
Finding that required fixing a second thing: the footer read only the BLOCKED operations, and an `add-column` is `safe`. The counterpart of a rename was never in the list it was looking at.
|
|
133
|
+
- **@voltro/cli** — **0.14.0's taxonomy codemod renamed two kinds of file it should not have, and a repo that already upgraded carries the damage with a green build.** Both were found by adopters running it on real projects; both are silent — the rename succeeds, the imports are rewritten, nothing throws.
|
|
134
|
+
|
|
135
|
+
**A framework primitive was treated as an undeclared file.** `health.route.tsx` → `health.route.component.tsx`, five times in one app. The codemod kept its OWN list of "suffixes that already carry a contract" instead of reading `fileConventions.ts`, and `.route.` was not on it — the exact drift that module exists to prevent, reproduced inside a file that imports from it. The list is gone; the registry answers now, and it gained `ROUTE_PATTERN` plus a `carriesFrameworkConvention()` every consumer shares.
|
|
136
|
+
|
|
137
|
+
The root cause underneath was worse than a missing entry: `export default defineRestRoute({...})` resolved to the placeholder name `Default`, which starts with a capital, and was counted as a COMPONENT on that basis. A default export is now only evidence of a component when the exported thing is callable — so a convention nobody has registered yet is safe too.
|
|
138
|
+
|
|
139
|
+
**A file with no exports was called a type file.** `test-setup.ts` → `test-setup.types.ts`, while `vitest.config.ts` still named `./test-setup.ts` as a **string**. Not an import, so nothing rewrote it and nothing failed: that suite would have run without its setup and stayed green. Five more went the same way — a registry module, two migration runners, a `.register.ts`, and a code generator whose `export` tokens live inside template strings.
|
|
140
|
+
|
|
141
|
+
`*.types.ts` promises "zero runtime exports", and that promise only means something for a file that exports TYPES. Zero of everything promises nothing, and renames a module whose whole purpose is being imported for effect — where the filename is often the only reference there is. It now requires at least one exported type.
|
|
142
|
+
|
|
143
|
+
**The shipped codemod undoes both**, and can only reach files whose content proves the suffix was wrong: a `.component.` on a name that already carries a framework convention, and a `.types.` on a file that exports nothing at all. It also prints the one thing it cannot fix — references by PATH rather than by import (a vitest `setupFiles`, a tsconfig `include`, a Docker `COPY`) were strings on the way out and are strings on the way back.
|
|
144
|
+
- **@voltro/cli** — Three ways `voltro update` failed on a real adopter's host, none of which we could have found ourselves — each needs a machine we do not have.
|
|
145
|
+
|
|
146
|
+
**The install could not run here, and the refusal left the tree half-upgraded.** Their install runs in a container against its own store. `voltro update` ran the package manager on the host anyway; pnpm refused (it wanted to remove `node_modules` and had no TTY to ask) and exited — after the version bumps were already written and before any codemod ran. That is the state this command's own documentation calls the worst one to be in, and it was reachable by design.
|
|
147
|
+
|
|
148
|
+
`--no-install` now writes the bump and stops, saying plainly that the tree is half-upgraded and naming both remaining steps. The install-failed message points at it too. Note what this is not: a compatibility flag. It is a mode for a host where the install is somebody else's job, and it ends by telling you the job is not done.
|
|
149
|
+
|
|
150
|
+
**The codemod scan exhausted a 12 GB heap, and said nothing about why.** The crash was a bare V8 out-of-memory stack. The scan pruned the directories WE know about — `node_modules`, `dist`, `.turbo` — which cannot cover a project's own heavy ignored trees (a build cache, a data dump, a virtualenv).
|
|
151
|
+
|
|
152
|
+
Inside a git repository the scan now asks git: `git ls-files --cached --others --exclude-standard` is exactly "files this project considers its own", and a codemod rewrites source — source that git ignores is not source we may rewrite. It also removes the traversal, so there is nothing left to exhaust memory on. Outside a repo the walk remains, now with a ceiling that REPORTS which directory to exclude instead of dying namelessly.
|
|
153
|
+
|
|
154
|
+
**"not a Voltro app" was the wrong conclusion.** Said of a directory containing an `app.config.ts`, it sends the reader looking for the wrong problem. Three web apps in a workspace inherited from another tool had their `@voltro/*` dependencies in an ancestor `package.json` — the apps ARE Voltro apps; only the declaration lives elsewhere. With an `app.config.ts` present the message now says that, and names the two ways forward.
|
|
155
|
+
|
|
156
|
+
**A monorepo may keep ONE root `package.json`** with its apps carrying only an `app.config.ts`. Running `voltro update` inside such an app used to say "no package.json at <dir>" — true, and useless. It now recognises the layout, says it is supported, and prints the command with the root already filled in.
|
|
157
|
+
- **@voltro/cli** — **`voltro update --only <id>`**, and a summary that stops contradicting itself.
|
|
158
|
+
|
|
159
|
+
`--only` runs just the named codemods (repeatable or comma-separated; ids are what `--dry-run` prints). The ask behind it: two of three codemods were load-bearing for one app — without them, 52 type errors and 51 routes that 404 — while the third was elective, and the repairing tool refuses on a dirty tree, so there was no way to take the necessary half first.
|
|
160
|
+
|
|
161
|
+
It is deliberately NOT a `--required` flag over a REQUIRED/OPTIONAL axis on each codemod. "Required" would have to mean *this app does not run without it*, and that is a property of the app: `03_pages-suffix` is unavoidable for a project with pages and irrelevant to an api-only one. Marking it on the codemod would encode a guess as a contract. An unknown id is an error listing the ids that ARE available — "it did nothing" and "you typed it wrong" otherwise look identical.
|
|
162
|
+
|
|
163
|
+
**The summary counted only edited files, and renames vanished from it.** The before-snapshot is keyed by PATH, so a moved file has no entry under its new one: a pass that renamed 238 files and edited 9 importers reported `(9 files)`, understating the change by a factor of 26 in the line a user plans around. Moves are now paired by content and reported separately — `(238 renamed, 9 edited)`.
|
|
164
|
+
|
|
165
|
+
**A dry run no longer speaks in the past tense.** It printed `✓ <id> — <title>`, the same line a real run prints. Now `·` and `[would apply]`.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
42
169
|
## [0.14.0] — 2026-07-26
|
|
43
170
|
|
|
44
171
|
### ⚠ BREAKING
|
package/dist/apikey.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DataStore } from '@voltro/database';
|
|
1
2
|
import { Schema } from 'effect';
|
|
2
3
|
|
|
3
4
|
export declare interface ApiKeyRecord {
|
|
@@ -94,6 +95,29 @@ declare interface AuthStrategy {
|
|
|
94
95
|
declare interface AuthStrategyInput {
|
|
95
96
|
readonly headers: Readonly<Record<string, string | undefined>>;
|
|
96
97
|
readonly clientId: number;
|
|
98
|
+
/**
|
|
99
|
+
* The app's DataStore, for a strategy that must READ to identify the caller.
|
|
100
|
+
*
|
|
101
|
+
* Without it, a DB-backed strategy — a session row, an API-key record, a PAT
|
|
102
|
+
* table — had to open a SECOND connection path beside the framework's, to the
|
|
103
|
+
* same database the request store opens a moment later. One adopter's
|
|
104
|
+
* `auth/db.ts` is 105 lines of exactly that: a second `ManagedRuntime` plus a
|
|
105
|
+
* `MysqlClient`, load-bearing for session lookup and their ApiKeyStore. Every
|
|
106
|
+
* DB-backed OIDC / SAML / PAT integration rebuilds it.
|
|
107
|
+
*
|
|
108
|
+
* It is the SAME value `auth.resolveScopes` receives — one store, handed to
|
|
109
|
+
* both, rather than a second narrower type for the same object. A read-only
|
|
110
|
+
* surface would be the better guarantee and it is not available cheaply here:
|
|
111
|
+
* `DataStore` is the driver SPI, and a strategy that writes during subject
|
|
112
|
+
* resolution is a design mistake the type system is not going to catch for
|
|
113
|
+
* you. Read users / sessions / keys; do not run domain writes.
|
|
114
|
+
*
|
|
115
|
+
* It is the BOOT store, not a request-scoped one — strategies resolve before
|
|
116
|
+
* a request store exists. `undefined` only while the store is still being
|
|
117
|
+
* built (`voltro dev` builds it after the auth chain; `voltro serve` before),
|
|
118
|
+
* and on an app with no store at all.
|
|
119
|
+
*/
|
|
120
|
+
readonly store?: DataStore;
|
|
97
121
|
}
|
|
98
122
|
|
|
99
123
|
/** A strategy's verdict on a request.
|
|
@@ -58,21 +58,25 @@ var r = t.Record({
|
|
|
58
58
|
scopes: n
|
|
59
59
|
};
|
|
60
60
|
}, x = (e, t) => async (n) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
let r = t?.getStore?.(), i = r === void 0 ? n : {
|
|
62
|
+
...n,
|
|
63
|
+
store: r
|
|
64
|
+
};
|
|
65
|
+
for (let n of e) {
|
|
66
|
+
let e = await n.resolve(i);
|
|
67
|
+
if (e.kind === "matched") return t?.resolveScopes === void 0 ? e.subject : b(e.subject, await t.resolveScopes(e.subject, i));
|
|
64
68
|
if (e.kind === "failed") {
|
|
65
69
|
t?.onStrategyFailed?.({
|
|
66
|
-
strategyId:
|
|
70
|
+
strategyId: n.id,
|
|
67
71
|
reason: e.reason
|
|
68
72
|
});
|
|
69
73
|
break;
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
|
-
if (t?.fallback) return t.fallback(
|
|
73
|
-
let
|
|
74
|
-
if (
|
|
75
|
-
return d(
|
|
76
|
+
if (t?.fallback) return t.fallback(i);
|
|
77
|
+
let a = i.headers["x-tenant"] ?? null;
|
|
78
|
+
if (a === null && t?.anonymousTenantRequired === !0) throw new S({ reason: "tenant required (x-tenant header missing)" });
|
|
79
|
+
return d(a);
|
|
76
80
|
}, S = class extends t.TaggedError()("Unauthenticated", { reason: t.optional(t.String) }) {}, C = (e, t) => {
|
|
77
81
|
if (e.type === "anonymous") throw new S(t === void 0 ? {} : { reason: t });
|
|
78
82
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -194,6 +194,29 @@ export declare interface AuthStrategy {
|
|
|
194
194
|
export declare interface AuthStrategyInput {
|
|
195
195
|
readonly headers: Readonly<Record<string, string | undefined>>;
|
|
196
196
|
readonly clientId: number;
|
|
197
|
+
/**
|
|
198
|
+
* The app's DataStore, for a strategy that must READ to identify the caller.
|
|
199
|
+
*
|
|
200
|
+
* Without it, a DB-backed strategy — a session row, an API-key record, a PAT
|
|
201
|
+
* table — had to open a SECOND connection path beside the framework's, to the
|
|
202
|
+
* same database the request store opens a moment later. One adopter's
|
|
203
|
+
* `auth/db.ts` is 105 lines of exactly that: a second `ManagedRuntime` plus a
|
|
204
|
+
* `MysqlClient`, load-bearing for session lookup and their ApiKeyStore. Every
|
|
205
|
+
* DB-backed OIDC / SAML / PAT integration rebuilds it.
|
|
206
|
+
*
|
|
207
|
+
* It is the SAME value `auth.resolveScopes` receives — one store, handed to
|
|
208
|
+
* both, rather than a second narrower type for the same object. A read-only
|
|
209
|
+
* surface would be the better guarantee and it is not available cheaply here:
|
|
210
|
+
* `DataStore` is the driver SPI, and a strategy that writes during subject
|
|
211
|
+
* resolution is a design mistake the type system is not going to catch for
|
|
212
|
+
* you. Read users / sessions / keys; do not run domain writes.
|
|
213
|
+
*
|
|
214
|
+
* It is the BOOT store, not a request-scoped one — strategies resolve before
|
|
215
|
+
* a request store exists. `undefined` only while the store is still being
|
|
216
|
+
* built (`voltro dev` builds it after the auth chain; `voltro serve` before),
|
|
217
|
+
* and on an app with no store at all.
|
|
218
|
+
*/
|
|
219
|
+
readonly store?: DataStore;
|
|
197
220
|
}
|
|
198
221
|
|
|
199
222
|
/** Strategies that need server-side callbacks (OAuth code-exchange,
|
|
@@ -363,6 +386,17 @@ export declare const composeAuthStrategies: (strategies: ReadonlyArray<AuthStrat
|
|
|
363
386
|
* quickly a role change must take effect.
|
|
364
387
|
*/
|
|
365
388
|
readonly resolveScopes?: (subject: Subject, input: AuthStrategyInput) => Promise<ReadonlyArray<string>> | ReadonlyArray<string>;
|
|
389
|
+
/**
|
|
390
|
+
* Hands every strategy the app's store on `input.store`.
|
|
391
|
+
*
|
|
392
|
+
* A GETTER rather than a value, and that is the whole reason this composes
|
|
393
|
+
* across both boot paths unchanged: `voltro dev` builds the store AFTER the
|
|
394
|
+
* auth chain and `voltro serve` builds it BEFORE. A value captured here
|
|
395
|
+
* would be `undefined` forever in dev and correct in serve — a capability
|
|
396
|
+
* present in production and missing in development, which is the drift
|
|
397
|
+
* class this repo has been bitten by most.
|
|
398
|
+
*/
|
|
399
|
+
readonly getStore?: () => unknown;
|
|
366
400
|
}) => ((input: AuthStrategyInput) => Promise<Subject>);
|
|
367
401
|
|
|
368
402
|
/**
|
|
@@ -744,6 +778,23 @@ export declare const diffRows: (prev: ReadonlyArray<PatchRow>, next: ReadonlyArr
|
|
|
744
778
|
*/
|
|
745
779
|
export declare const effectiveScopes: (subject: Subject) => ReadonlyArray<string>;
|
|
746
780
|
|
|
781
|
+
/**
|
|
782
|
+
* Read the `_tag` off a thrown error value, or `undefined` when it has none.
|
|
783
|
+
*
|
|
784
|
+
* It lives here, beside `toRpc`, because `toRpc` is what puts the tag on the
|
|
785
|
+
* wire — one file owns both ends of that contract. It used to live in
|
|
786
|
+
* `@voltro/client`, and being there had a cost nobody could see from inside the
|
|
787
|
+
* framework: an app's shared error handler lived in a package that pulled only
|
|
788
|
+
* `@voltro/i18n`, so reading a tag meant taking a dependency on the whole client
|
|
789
|
+
* for seven lines. `_tag` is a wire concept, and protocol owns the wire.
|
|
790
|
+
*
|
|
791
|
+
* Works on a `Schema.TaggedError` instance AND on the plain `{ _tag: … }` object
|
|
792
|
+
* the wire actually emits — which is the distinction that matters at the call
|
|
793
|
+
* site: `instanceof` does NOT hold on the client, because what arrives there was
|
|
794
|
+
* decoded from JSON and never constructed. Match on the tag, not on the class.
|
|
795
|
+
*/
|
|
796
|
+
export declare const errorTag: (err: unknown) => string | undefined;
|
|
797
|
+
|
|
747
798
|
/** Normalize the `exposeAsTool` shorthand. `true` is only valid when the
|
|
748
799
|
* descriptor carries a top-level `description`; callers pass that in. */
|
|
749
800
|
export declare type ExposeAsTool = boolean | ExposeAsToolSpec;
|
|
@@ -2209,36 +2260,36 @@ export declare class SubjectService extends SubjectService_base {
|
|
|
2209
2260
|
|
|
2210
2261
|
declare const SubjectService_base: Context.TagClass<SubjectService, "@voltro/Subject", {
|
|
2211
2262
|
readonly id: string;
|
|
2212
|
-
readonly type: "user";
|
|
2213
2263
|
readonly tenantId: string;
|
|
2264
|
+
readonly type: "user";
|
|
2214
2265
|
readonly scopes?: readonly string[] | undefined;
|
|
2215
2266
|
readonly metadata?: {
|
|
2216
2267
|
readonly [x: string]: unknown;
|
|
2217
2268
|
} | undefined;
|
|
2218
2269
|
} | {
|
|
2219
2270
|
readonly id: string;
|
|
2220
|
-
readonly type: "apiKey";
|
|
2221
2271
|
readonly tenantId: string;
|
|
2272
|
+
readonly type: "apiKey";
|
|
2222
2273
|
readonly scopes?: readonly string[] | undefined;
|
|
2223
2274
|
readonly metadata?: {
|
|
2224
2275
|
readonly [x: string]: unknown;
|
|
2225
2276
|
} | undefined;
|
|
2226
2277
|
} | {
|
|
2227
2278
|
readonly id: string;
|
|
2228
|
-
readonly type: "serviceAccount";
|
|
2229
2279
|
readonly tenantId: string;
|
|
2280
|
+
readonly type: "serviceAccount";
|
|
2230
2281
|
readonly scopes?: readonly string[] | undefined;
|
|
2231
2282
|
readonly metadata?: {
|
|
2232
2283
|
readonly [x: string]: unknown;
|
|
2233
2284
|
} | undefined;
|
|
2234
2285
|
} | {
|
|
2235
2286
|
readonly id: null;
|
|
2236
|
-
readonly type: "anonymous";
|
|
2237
2287
|
readonly tenantId: string | null;
|
|
2288
|
+
readonly type: "anonymous";
|
|
2238
2289
|
} | {
|
|
2239
2290
|
readonly id: string;
|
|
2240
|
-
readonly type: "system";
|
|
2241
2291
|
readonly tenantId: null;
|
|
2292
|
+
readonly type: "system";
|
|
2242
2293
|
readonly scopes?: readonly string[] | undefined;
|
|
2243
2294
|
readonly metadata?: {
|
|
2244
2295
|
readonly [x: string]: unknown;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { C as e, D as t, E as n, S as r, T as i, _ as a, a as o, b as ee, c as te, d as ne, f as s, g as re, h as ie, i as ae, l as oe, m as se, n as ce, o as le, p as ue, r as de, s as fe, t as pe, u as me, v as he, w as ge, x as _e, y as ve } from "./serverErrorBus-DhIVDkCi.js";
|
|
2
|
-
import { a as ye, c as be, d as xe, f as Se, i as Ce, l as we, n as Te, o as Ee, p as De, r as Oe, s as ke, t as Ae, u as je } from "./auth-
|
|
2
|
+
import { a as ye, c as be, d as xe, f as Se, i as Ce, l as we, n as Te, o as Ee, p as De, r as Oe, s as ke, t as Ae, u as je } from "./auth-BPdyOBsd.js";
|
|
3
3
|
import { Context as Me, Layer as Ne, Schema as c } from "effect";
|
|
4
4
|
import { Rpc as l } from "@effect/rpc";
|
|
5
5
|
//#region src/rowPatch.ts
|
|
@@ -133,13 +133,17 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
133
133
|
error: y(e.error, t),
|
|
134
134
|
stream: !0
|
|
135
135
|
}), He = (e) => {
|
|
136
|
+
if (typeof e != "object" || !e) return;
|
|
137
|
+
let t = e._tag;
|
|
138
|
+
return typeof t == "string" ? t : void 0;
|
|
139
|
+
}, Ue = (e) => {
|
|
136
140
|
switch (e.kind) {
|
|
137
141
|
case "query": return x(e);
|
|
138
142
|
case "mutation": return S(e);
|
|
139
143
|
case "action": return C(e);
|
|
140
144
|
case "stream": return w(e);
|
|
141
145
|
}
|
|
142
|
-
},
|
|
146
|
+
}, We = (e) => {
|
|
143
147
|
let t = e.input, n = t === void 0 ? {} : { input: t }, r = e.output, i = r === void 0 ? {} : { output: r };
|
|
144
148
|
if (e.kind === "query") return {
|
|
145
149
|
kind: "query",
|
|
@@ -180,15 +184,15 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
180
184
|
..."shapeItem" in e && e.shapeItem !== void 0 ? { shapeItem: e.shapeItem } : {}
|
|
181
185
|
}))
|
|
182
186
|
};
|
|
183
|
-
},
|
|
187
|
+
}, Ge = (e) => e, Ke = (e) => e, qe = (e) => {
|
|
184
188
|
if (e.length !== 0) return (t, n) => e.reduceRight((e, t) => t(e, n), t);
|
|
185
|
-
},
|
|
189
|
+
}, Je = (e, t) => {
|
|
186
190
|
let n = Me.GenericTag(e);
|
|
187
191
|
return {
|
|
188
192
|
Tag: n,
|
|
189
193
|
Live: Ne.succeed(n, t)
|
|
190
194
|
};
|
|
191
|
-
},
|
|
195
|
+
}, Ye = (e, t, n) => {
|
|
192
196
|
if (!t) return { ok: !0 };
|
|
193
197
|
let r = T(n);
|
|
194
198
|
if (!r) return {
|
|
@@ -198,7 +202,7 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
198
202
|
let i = t.trim();
|
|
199
203
|
if (i === "*" || i === "") return { ok: !0 };
|
|
200
204
|
let a = i.split(/\s+/).filter((e) => e.length > 0);
|
|
201
|
-
for (let i of a) if (!
|
|
205
|
+
for (let i of a) if (!Xe(i, r)) return {
|
|
202
206
|
ok: !1,
|
|
203
207
|
reason: `plugin "${e}" requires framework ${t}, running ${n}`
|
|
204
208
|
};
|
|
@@ -211,7 +215,7 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
211
215
|
patch: Number(t[3]),
|
|
212
216
|
pre: t[4] ?? ""
|
|
213
217
|
} : null;
|
|
214
|
-
}, E = (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,
|
|
218
|
+
}, E = (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, Xe = (e, t) => {
|
|
215
219
|
if (e === "*") return !0;
|
|
216
220
|
if (e.startsWith("^")) {
|
|
217
221
|
let n = T(e.slice(1));
|
|
@@ -233,12 +237,12 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
233
237
|
}
|
|
234
238
|
let r = T(e);
|
|
235
239
|
return r ? E(t, r) === 0 : !1;
|
|
236
|
-
}, D = c.Literal("running", "succeeded", "failed", "cancelled", "suspended"),
|
|
240
|
+
}, D = c.Literal("running", "succeeded", "failed", "cancelled", "suspended"), Ze = c.Literal("cancel", "terminate", "abandon"), Qe = c.Struct({
|
|
237
241
|
id: c.String,
|
|
238
242
|
workflowName: c.String,
|
|
239
243
|
executionId: c.String,
|
|
240
244
|
status: c.Literal("running")
|
|
241
|
-
}),
|
|
245
|
+
}), O = c.Struct({
|
|
242
246
|
id: c.String,
|
|
243
247
|
tag: c.String,
|
|
244
248
|
executionId: c.String,
|
|
@@ -257,12 +261,12 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
257
261
|
durationMs: c.NullOr(c.Number),
|
|
258
262
|
traceId: c.NullOr(c.String),
|
|
259
263
|
parentExecutionId: c.NullOr(c.String),
|
|
260
|
-
parentClosePolicy: c.NullOr(
|
|
261
|
-
}),
|
|
264
|
+
parentClosePolicy: c.NullOr(Ze)
|
|
265
|
+
}), k = c.Struct({
|
|
262
266
|
tag: c.optional(c.String),
|
|
263
267
|
status: c.optional(D),
|
|
264
268
|
limit: c.optional(c.Number)
|
|
265
|
-
}),
|
|
269
|
+
}), A = c.Struct({
|
|
266
270
|
id: c.String,
|
|
267
271
|
runId: c.String,
|
|
268
272
|
stepName: c.String,
|
|
@@ -277,7 +281,7 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
277
281
|
startedAt: c.Date,
|
|
278
282
|
completedAt: c.NullOr(c.Date),
|
|
279
283
|
durationMs: c.NullOr(c.Number)
|
|
280
|
-
}),
|
|
284
|
+
}), j = c.Struct({
|
|
281
285
|
id: c.String,
|
|
282
286
|
runId: c.String,
|
|
283
287
|
eventType: c.String,
|
|
@@ -285,7 +289,7 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
285
289
|
occurredAt: c.Date,
|
|
286
290
|
stepName: c.NullOr(c.String),
|
|
287
291
|
attempt: c.NullOr(c.Number)
|
|
288
|
-
}),
|
|
292
|
+
}), M = c.Struct({
|
|
289
293
|
id: c.String,
|
|
290
294
|
name: c.String,
|
|
291
295
|
payload: c.Unknown,
|
|
@@ -293,7 +297,7 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
293
297
|
subject: c.NullOr(c.Unknown),
|
|
294
298
|
traceId: c.NullOr(c.String),
|
|
295
299
|
occurredAt: c.Date
|
|
296
|
-
}),
|
|
300
|
+
}), N = c.Struct({
|
|
297
301
|
id: c.String,
|
|
298
302
|
eventId: c.String,
|
|
299
303
|
eventName: c.String,
|
|
@@ -306,13 +310,13 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
306
310
|
errorMessage: c.NullOr(c.String),
|
|
307
311
|
createdAt: c.Date,
|
|
308
312
|
completedAt: c.NullOr(c.Date)
|
|
309
|
-
}),
|
|
313
|
+
}), P = c.Struct({ id: c.String }), F = c.Struct({ runId: c.String }), I = c.Struct({
|
|
310
314
|
name: c.optional(c.String),
|
|
311
315
|
limit: c.optional(c.Number)
|
|
312
|
-
}),
|
|
316
|
+
}), L = c.Struct({ eventId: c.String }), R = c.Struct({
|
|
313
317
|
workflowName: c.String,
|
|
314
318
|
executionId: c.String
|
|
315
|
-
}),
|
|
319
|
+
}), z = c.Struct({
|
|
316
320
|
id: c.String,
|
|
317
321
|
signalName: c.String,
|
|
318
322
|
payload: c.optional(c.Unknown)
|
|
@@ -326,49 +330,49 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
326
330
|
updateId: c.String,
|
|
327
331
|
completedEventId: c.String,
|
|
328
332
|
result: c.Unknown
|
|
329
|
-
}),
|
|
333
|
+
}), $e = g({
|
|
330
334
|
name: "__voltro.workflow.run",
|
|
331
335
|
source: "_voltro_workflow_runs",
|
|
332
|
-
input:
|
|
333
|
-
output: c.Array(
|
|
334
|
-
}),
|
|
336
|
+
input: P,
|
|
337
|
+
output: c.Array(O)
|
|
338
|
+
}), et = g({
|
|
335
339
|
name: "__voltro.workflow.runs",
|
|
336
340
|
source: "_voltro_workflow_runs",
|
|
337
|
-
input:
|
|
338
|
-
output: c.Array(
|
|
339
|
-
}),
|
|
341
|
+
input: k,
|
|
342
|
+
output: c.Array(O)
|
|
343
|
+
}), tt = g({
|
|
340
344
|
name: "__voltro.workflow.run.steps",
|
|
341
345
|
source: "_voltro_workflow_run_steps",
|
|
342
|
-
input:
|
|
343
|
-
output: c.Array(
|
|
344
|
-
}),
|
|
346
|
+
input: F,
|
|
347
|
+
output: c.Array(A)
|
|
348
|
+
}), nt = g({
|
|
345
349
|
name: "__voltro.workflow.run.events",
|
|
346
350
|
source: "_voltro_workflow_run_events",
|
|
347
|
-
input:
|
|
348
|
-
output: c.Array(
|
|
349
|
-
}),
|
|
351
|
+
input: F,
|
|
352
|
+
output: c.Array(j)
|
|
353
|
+
}), rt = g({
|
|
350
354
|
name: "__voltro.workflow.domainEvents",
|
|
351
355
|
source: "_voltro_workflow_events",
|
|
352
|
-
input:
|
|
353
|
-
output: c.Array(
|
|
354
|
-
}),
|
|
356
|
+
input: I,
|
|
357
|
+
output: c.Array(M)
|
|
358
|
+
}), it = g({
|
|
355
359
|
name: "__voltro.workflow.event.deliveries",
|
|
356
360
|
source: "_voltro_workflow_event_deliveries",
|
|
357
|
-
input:
|
|
358
|
-
output: c.Array(
|
|
359
|
-
}),
|
|
361
|
+
input: L,
|
|
362
|
+
output: c.Array(N)
|
|
363
|
+
}), at = v({
|
|
360
364
|
name: "__voltro.workflow.cancel",
|
|
361
|
-
input:
|
|
365
|
+
input: R,
|
|
362
366
|
output: c.Struct({ ok: c.Boolean })
|
|
363
|
-
}),
|
|
367
|
+
}), ot = v({
|
|
364
368
|
name: "__voltro.workflow.resume",
|
|
365
|
-
input:
|
|
369
|
+
input: R,
|
|
366
370
|
output: c.Struct({ ok: c.Boolean })
|
|
367
|
-
}),
|
|
371
|
+
}), st = v({
|
|
368
372
|
name: "__voltro.workflow.signal",
|
|
369
|
-
input:
|
|
373
|
+
input: z,
|
|
370
374
|
output: c.Struct({ eventId: c.String })
|
|
371
|
-
}),
|
|
375
|
+
}), ct = v({
|
|
372
376
|
name: "__voltro.workflow.update",
|
|
373
377
|
input: B,
|
|
374
378
|
output: V
|
|
@@ -382,33 +386,33 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
382
386
|
}), K = class extends c.TaggedError()("UndoNotFound", { invocationId: c.String }) {}, q = class extends c.TaggedError()("UndoForbidden", { invocationId: c.String }) {}, J = class extends c.TaggedError()("UndoConflict", {
|
|
383
387
|
invocationId: c.String,
|
|
384
388
|
reason: c.Literal("conflict", "action")
|
|
385
|
-
}) {}, Y = c.Union(K, q, J),
|
|
389
|
+
}) {}, Y = c.Union(K, q, J), lt = g({
|
|
386
390
|
name: H,
|
|
387
391
|
source: "_voltro_undo_log",
|
|
388
392
|
input: c.Struct({ limit: c.optional(c.Number) }),
|
|
389
393
|
output: c.Array(G)
|
|
390
|
-
}),
|
|
394
|
+
}), ut = _({
|
|
391
395
|
name: U,
|
|
392
396
|
input: c.Struct({ invocationId: c.String }),
|
|
393
397
|
output: c.Struct({ ok: c.Boolean }),
|
|
394
398
|
error: Y
|
|
395
|
-
}),
|
|
399
|
+
}), dt = _({
|
|
396
400
|
name: W,
|
|
397
401
|
input: c.Struct({ invocationId: c.String }),
|
|
398
402
|
output: c.Struct({ ok: c.Boolean }),
|
|
399
403
|
error: Y
|
|
400
|
-
}), X = "__voltro.connections.list",
|
|
404
|
+
}), X = "__voltro.connections.list", ft = "__voltro.connections.start", pt = "__voltro.connections.submitToken", mt = "__voltro.connections.disconnect", Z = c.Literal("oauth2", "pat"), ht = c.Literal("disconnected", "connected", "expired", "revoked", "error"), gt = c.Struct({
|
|
401
405
|
connectionId: c.String,
|
|
402
406
|
kind: Z,
|
|
403
407
|
label: c.String,
|
|
404
|
-
status:
|
|
408
|
+
status: ht,
|
|
405
409
|
accountId: c.NullOr(c.String),
|
|
406
410
|
accountLabel: c.NullOr(c.String),
|
|
407
411
|
scopes: c.Array(c.String),
|
|
408
412
|
expiresAt: c.NullOr(c.String),
|
|
409
413
|
lastError: c.NullOr(c.String),
|
|
410
414
|
connectedAt: c.NullOr(c.String)
|
|
411
|
-
}),
|
|
415
|
+
}), _t = class extends c.TaggedError()("ConnectionNotDeclared", { connectionId: c.String }) {}, vt = class extends c.TaggedError()("ConnectionSubjectRequired", { connectionId: c.String }) {}, yt = class extends c.TaggedError()("ConnectionKindMismatch", {
|
|
412
416
|
connectionId: c.String,
|
|
413
417
|
expected: Z,
|
|
414
418
|
actual: Z
|
|
@@ -416,13 +420,13 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
416
420
|
connectionId: c.String,
|
|
417
421
|
reason: c.String,
|
|
418
422
|
transient: c.Boolean
|
|
419
|
-
}) {}, $ = c.Union(
|
|
423
|
+
}) {}, $ = c.Union(_t, vt, yt, Q), bt = g({
|
|
420
424
|
name: X,
|
|
421
425
|
source: "_voltro_connections",
|
|
422
426
|
input: c.Struct({}),
|
|
423
|
-
output: c.Array(
|
|
424
|
-
}),
|
|
425
|
-
name:
|
|
427
|
+
output: c.Array(gt)
|
|
428
|
+
}), xt = v({
|
|
429
|
+
name: ft,
|
|
426
430
|
input: c.Struct({
|
|
427
431
|
connectionId: c.String,
|
|
428
432
|
redirectTo: c.optional(c.String)
|
|
@@ -432,22 +436,22 @@ var Pe = c.Union(c.String, c.Number), u = c.Record({
|
|
|
432
436
|
state: c.String
|
|
433
437
|
}),
|
|
434
438
|
error: $
|
|
435
|
-
}),
|
|
436
|
-
name:
|
|
439
|
+
}), St = _({
|
|
440
|
+
name: pt,
|
|
437
441
|
input: c.Struct({
|
|
438
442
|
connectionId: c.String,
|
|
439
443
|
token: c.String
|
|
440
444
|
}),
|
|
441
445
|
output: c.Struct({ ok: c.Boolean }),
|
|
442
446
|
error: $
|
|
443
|
-
}),
|
|
444
|
-
name:
|
|
447
|
+
}), Ct = _({
|
|
448
|
+
name: mt,
|
|
445
449
|
input: c.Struct({ connectionId: c.String }),
|
|
446
450
|
output: c.Struct({ ok: c.Boolean }),
|
|
447
451
|
error: $
|
|
448
|
-
}),
|
|
452
|
+
}), wt = (e) => {
|
|
449
453
|
let t = e instanceof Date ? e.getTime() : typeof e == "number" ? e : typeof e == "string" ? new Date(e).getTime() : 0;
|
|
450
454
|
return Number.isNaN(t) ? 0 : t;
|
|
451
|
-
},
|
|
455
|
+
}, Tt = 1;
|
|
452
456
|
//#endregion
|
|
453
|
-
export { te as ADMIN_SCOPE, oe as APIKEY_ISSUE_ORG_SCOPE, me as APIKEY_ISSUE_OTHER_SCOPE, ne as APIKEY_ISSUE_SELF_SCOPE, Ae as AuthMiddleware, X as CONNECTIONS_LIST_TAG,
|
|
457
|
+
export { te as ADMIN_SCOPE, oe as APIKEY_ISSUE_ORG_SCOPE, me as APIKEY_ISSUE_OTHER_SCOPE, ne as APIKEY_ISSUE_SELF_SCOPE, Ae as AuthMiddleware, X as CONNECTIONS_LIST_TAG, mt as CONNECTION_DISCONNECT_TAG, ft as CONNECTION_START_TAG, pt as CONNECTION_SUBMIT_TOKEN_TAG, Q as ConnectionHandshakeFailed, Te as ConnectionInfo, Oe as ConnectionInfoMiddleware, Z as ConnectionKind, yt as ConnectionKindMismatch, _t as ConnectionNotDeclared, gt as ConnectionState, ht as ConnectionStatus, vt as ConnectionSubjectRequired, Tt as PROTOCOL_VERSION, s as ScopeError, Ce as Subject, ye as SubjectService, U as UNDO_APPLY_TAG, H as UNDO_LOG_TAG, W as UNDO_REDO_TAG, Ee as Unauthenticated, J as UndoConflict, q as UndoForbidden, G as UndoLogEntry, K as UndoNotFound, R as WorkflowControlInputSchema, M as WorkflowDomainEventRowSchema, I as WorkflowDomainEventsInputSchema, L as WorkflowEventDeliveriesInputSchema, N as WorkflowEventDeliveryRowSchema, Ze as WorkflowParentClosePolicySchema, j as WorkflowRunEventRowSchema, Qe as WorkflowRunHandleSchema, P as WorkflowRunRefSchema, O as WorkflowRunRowSchema, D as WorkflowRunStatusSchema, A as WorkflowRunStepRowSchema, F as WorkflowRunTableRefSchema, k as WorkflowRunsInputSchema, z as WorkflowSignalInputSchema, B as WorkflowUpdateInputSchema, V as WorkflowUpdateResultSchema, C as actionToRpc, ue as advisoryResourceGuardWarning, ke as anonymousSubject, ze as applyRowPatch, be as assertAuthenticated, de as beginIdempotent, Ye as checkFrameworkCompat, se as checkGuards, ie as checkGuardsEffect, we as composeAuthStrategies, qe as composeRpcInterceptors, Ct as connectionDisconnectDescriptor, xt as connectionStartDescriptor, St as connectionSubmitTokenDescriptor, bt as connectionsListQueryDescriptor, v as defineAction, _ as defineMutation, Ge as definePlugin, Ke as definePluginRoute, Je as definePluginService, g as defineQuery, Ve as defineStream, Le as diffRows, re as effectiveScopes, He as errorTag, ae as failIdempotent, a as findAdvisoryResourceGuards, o as finishIdempotent, he as getPolicyGuardResolver, ve as getResourceScopeResolver, je as hasCallbackRoutes, ee as hasEffectiveScope, _e as hasScope, p as idToPath, le as idempotencyScope, Re as isIdKeyed, r as isPolicyCheck, Be as isPolicyGuard, xe as isSystemSubject, fe as memoryIdempotencyStore, S as mutationToRpc, We as normalizeDescriptor, Fe as pathToId, pe as publishServerError, x as queryToRpc, e as requireScope, d as rowPatchOpSchema, f as rowPatchSchema, ge as setEffectiveScopes, i as setPolicyGuardResolver, n as setResourceScopeResolver, w as streamToRpc, t as subjectScopes, ce as subscribeServerErrors, h as subscriptionEvent, Se as systemSubject, De as tenantScopedSubject, Ue as toRpc, wt as tsMs, ut as undoApplyDescriptor, lt as undoLogQueryDescriptor, dt as undoRedoDescriptor, at as workflowCancelDescriptor, rt as workflowDomainEventsQueryDescriptor, it as workflowEventDeliveriesQueryDescriptor, ot as workflowResumeDescriptor, nt as workflowRunEventsQueryDescriptor, $e as workflowRunQueryDescriptor, tt as workflowRunStepsQueryDescriptor, et as workflowRunsQueryDescriptor, st as workflowSignalDescriptor, ct as workflowUpdateDescriptor };
|
package/dist/jwt.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DataStore } from '@voltro/database';
|
|
1
2
|
import { Schema } from 'effect';
|
|
2
3
|
|
|
3
4
|
/** Pluggable auth strategy. Strategies are SYNC-fast on no-match
|
|
@@ -32,6 +33,29 @@ declare interface AuthStrategy {
|
|
|
32
33
|
declare interface AuthStrategyInput {
|
|
33
34
|
readonly headers: Readonly<Record<string, string | undefined>>;
|
|
34
35
|
readonly clientId: number;
|
|
36
|
+
/**
|
|
37
|
+
* The app's DataStore, for a strategy that must READ to identify the caller.
|
|
38
|
+
*
|
|
39
|
+
* Without it, a DB-backed strategy — a session row, an API-key record, a PAT
|
|
40
|
+
* table — had to open a SECOND connection path beside the framework's, to the
|
|
41
|
+
* same database the request store opens a moment later. One adopter's
|
|
42
|
+
* `auth/db.ts` is 105 lines of exactly that: a second `ManagedRuntime` plus a
|
|
43
|
+
* `MysqlClient`, load-bearing for session lookup and their ApiKeyStore. Every
|
|
44
|
+
* DB-backed OIDC / SAML / PAT integration rebuilds it.
|
|
45
|
+
*
|
|
46
|
+
* It is the SAME value `auth.resolveScopes` receives — one store, handed to
|
|
47
|
+
* both, rather than a second narrower type for the same object. A read-only
|
|
48
|
+
* surface would be the better guarantee and it is not available cheaply here:
|
|
49
|
+
* `DataStore` is the driver SPI, and a strategy that writes during subject
|
|
50
|
+
* resolution is a design mistake the type system is not going to catch for
|
|
51
|
+
* you. Read users / sessions / keys; do not run domain writes.
|
|
52
|
+
*
|
|
53
|
+
* It is the BOOT store, not a request-scoped one — strategies resolve before
|
|
54
|
+
* a request store exists. `undefined` only while the store is still being
|
|
55
|
+
* built (`voltro dev` builds it after the auth chain; `voltro serve` before),
|
|
56
|
+
* and on an app with no store at all.
|
|
57
|
+
*/
|
|
58
|
+
readonly store?: DataStore;
|
|
35
59
|
}
|
|
36
60
|
|
|
37
61
|
/**
|
package/dist/rest.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as e, i as t, o as n, r, t as i } from "./serverErrorBus-DhIVDkCi.js";
|
|
2
|
-
import { s as a } from "./auth-
|
|
2
|
+
import { s as a } from "./auth-BPdyOBsd.js";
|
|
3
3
|
import { Effect as o, Schema as s } from "effect";
|
|
4
4
|
//#region src/publicApi.ts
|
|
5
5
|
var c = (e, t) => t.method ?? (e === "query" ? "GET" : "POST"), l = (e, t) => t.path ?? `/${t.version ?? "v1"}/${e.replace(/\./g, "/")}`, u = (e, t, n) => {
|
package/dist/session.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.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",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@effect/sql": "^0.51.1",
|
|
56
|
-
"@voltro/database": "0.
|
|
56
|
+
"@voltro/database": "0.15.0",
|
|
57
57
|
"jose": "^6.2.3"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|