@voltro/web 0.30.0 → 0.30.1

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 CHANGED
@@ -39,6 +39,77 @@ _Changes staged for the next release accumulate here (rolled up from
39
39
 
40
40
  ---
41
41
 
42
+ ## [0.30.1] — 2026-08-09
43
+
44
+ ### Fixed
45
+
46
+ - **@voltro/testing, @voltro/sql-mysql** — **One integration suite was never skip-guarded, and the guard it called silently ran it anyway.** `describeIfAvailable(label, dependency, probe, suite)` was called with three arguments in `sql-mysql`'s `fileMigrationLedger.mariadb` suite, so `probe` bound to the SUITE body: `await probe()` executed it at file scope, its `beforeAll` and `test`s registered outside any `describe` and ran unconditionally, and the `describe.skipIf` underneath registered an empty shell. The file therefore passed when MariaDB happened to be up and hard-failed with `SqlError: MysqlClient: Failed to connect` when it was not — the exact opposite of the clean skip it was written to have, and a red `pnpm test` for anyone without the docker stack.
47
+
48
+ `tsc` could not catch it: every dialect package's tsconfig `include` lists the src glob only, so `__tests__/` is not typechecked at all — the same gap that lets an incomplete parity fixture compile. So the arity is checked at runtime now, and `describeIfAvailable` throws a `TypeError` naming what it got instead of quietly running the suite. One of roughly twenty call sites was wrong; nineteen were right, which is why nothing looked off.
49
+ - **@voltro/cli** — **Four things `voltro doctor` knew and would not tell you.** All reported by a consumer, all measured rather than guessed.
50
+
51
+ **The authz list was reachable by no route at all.** The human view truncated at 20 (`… and 14 more`) and `--json` had no `authz` section — measured, its keys were `root · scannedFiles · … · serverOnly`. Reading findings 21..n meant allowlisting the first 20, re-running, and resetting the file: a loop to read a list the tool already had. `--json` carries `authz` now (`counts`, `guardVocabulary`, `allowlist`, and every `unchecked` finding, never truncated), and the elision line names both the command and the field. The same defect, one section over, is recorded in `serverOnly`'s own comment — "the field was MISSING from `--json` entirely" — so this is that lesson applied rather than re-learned. Both surfaces read ONE scan (`scanAuthzForRoot`), because two derivations of one scan is how two views come to disagree about what was found.
52
+
53
+ **The allowlist could not tell "reviewed and safe" from "debt".** Its header says `This is DEBT, not approval` — which is right, and which made it the wrong place for the other thing people legitimately need to record: an executor a human has read and found genuinely open, constrained by something the scanner cannot see. It was also the ONLY place, so the reporter resorted to comment blocks around groups of lines — a convention inside a file parsed line by line, which the next `--write-authz-allowlist` would have flattened without a word. A line is now either `<tag>` (debt, unchanged) or `<tag> reviewed=<why>`, the reason REQUIRED — `reviewed=` with no why is the claim without the evidence and is refused, since a bare tag is the honest alternative and always available. Doctor counts and prints the two apart, and `--write-authz-allowlist` preserves reviewed lines instead of downgrading them.
54
+
55
+ **A hint that named 41% of the files was not a hint.** One hand-roll finding listed 2 641 of 6 374 files, and the reporter skipped the whole section because of it — including the lines pointing at 5 and 13 files, which were worth acting on that day. Findings now print FEWEST files first, and a finding above both a share (20%) and a floor (50 files) prints its ADVICE without the enumeration, marked as a codebase-wide pattern. Both bounds matter: the share is what makes it a pattern, the floor keeps a small app — where "3 of 8 files" is a large share and a perfectly readable list — out of it. The paths stay in `--json`.
56
+
57
+ **A translation catalog that is never loaded said nothing.** `src/locales/{code}.ts` is imported by the web codegen only when the app declares `locales:`. Without that line the files are inert — no import, no provider, no error — and from the inside a catalog that is never loaded looks exactly like one that works. The reporter carried `de.ts` + `en.ts` in TWO apps for months, never wired, and measured that neither boot nor doctor mentioned it. Doctor now names the orphaned codes and offers both ways out: the exact `locales: [...]` line to paste, or delete the files (which is what they did). It deliberately does NOT report the reverse — a declared locale with no file already fails loudly at codegen, and a second, weaker voice for a problem that has a loud one is noise.
58
+ - **@voltro/cli** — **`encryptSteps` was derived twice, once per boot path.** Six hand-mirrored lines in `dev.ts` and in `serveApi.ts` — read the flow control off the definition, compare `=== true`, build the cipher, spread the result or nothing. They agreed today and nothing kept them agreeing, which is the shape that produced the `_voltro_outbox` error loop and every dev/serve scar in `packages/cli/CLAUDE.md`. The asymmetry a drift would produce here is the bad direction: step payloads encrypted under `voltro dev` and plaintext under `voltro serve`, with the declaration reading as protection in both.
59
+
60
+ `stepPayloadCipherOptions(definition)` is the one derivation now, and it is slightly better than either copy it replaced: the workflow NAME in the boot-refusal message comes from the resolved control rather than from a second argument, so the flag and the name it is reported under are the same object. `flowControlParity.test.ts` pins that both paths call it AND that neither re-derives `encryptSteps === true` inline.
61
+ - **@voltro/runtime, @voltro/cli, @voltro/workflow** — **`debounce` never ran. Neither did a `batch` that flushed on its timeout — and `batch` could not be started at all.** Three defects, one boundary, all found by a consumer who adopted flow control against a live API and measured `attempts: 13, collapsed: 14, runs: 0` on a debounced workflow that never produced a run.
62
+
63
+ **1 — the drainer re-entered the admission boundary it had just cleared.** A deferred start is judged twice on purpose: once on arrival, once when the drainer reconsiders the pending row. The second judgement is the one that ADMITS, and the drainer then started the workflow *through the facade* — deliberately, so a queued run takes exactly the code path an immediate one does. But the facade's start IS the arrival path, and arrival is where a deferring control defers. So the admitted start was deferred straight back into the row it came from: `collapsed` up by one, the row still pending, the engine never reached, one wasted pass per second, forever. `debounce` was 100% broken; `batch` was broken whenever it flushed on the timeout rather than by filling. `throttle` and `concurrency` survived only by an ordering accident — the re-entrant arrival happened to re-admit because the ledger row and lease are written *after* the start returns. The drainer's start now carries an internal `admitted` marker that skips the gate: it is the APPLICATION of a decision already made, and everything the arrival path would have done (pause, singleton eviction, the ledger row and lease, consuming the intents) the drainer does around it.
64
+
65
+ **2 — a `batch:` workflow rejected every caller's start.** The declaration contract is explicit and enforced: the workflow's own `payload` is `{ items: Schema.Array(Item) }` while callers `start()` it with a SINGLE item, declared as `batch.item`. `batch.item` was required, asserted at declaration time — and then dropped during resolution and read by nothing. So the facade validated the caller's single item against the batch shape and threw `WorkflowPayloadError: missing required field(s): items` before the gate was ever reached. `batch:` was unusable end to end. The item schema is now carried through and is what an arriving start is judged by; a drained batch is judged by the workflow's own schema.
66
+
67
+ **3 — the two halves of that boundary could be wired half-right, in both boot paths.** `startPayloadSchema` is pinned beside `admitStart` in `flowControlParity.test.ts` as a separate assertion, because passing one and not the other fails silently and differently.
68
+
69
+ **Why no unit suite could see any of this.** `admissionDrainer.test.ts` fakes `startAdmitted`; `workflowRuntime.test.ts` fakes `admitStart`. Each is a complete test of its own half, and the defect lived strictly between them — the same shape as this repo's dev/serve parity scars, one level down. `flowControlDrainRoundTrip.test.ts` wires a real gate to a real facade over a real in-memory store and drives all four deferring controls from arrival to run. It was written red: debounce and batch-timeout failed, throttle and concurrency passed, which is exactly the diagnosis. It asserts the pending row is CONSUMED rather than merely that a run eventually happened — a debounce that re-collapses twice on the way is still broken, and `admitted: 1` alone would not say so.
70
+ - **@voltro/cli** — **`_voltro_outbox` was polled every five seconds by apps that never had it created.** Reported by a consumer as a permanent `Table doesn't exist` loop — and, they noted, "a permanent error loop that buries the real ones". The table appeared zero times in `voltro db plan`, which was correct for the gate as written and wrong for what the boot actually does.
71
+
72
+ The two predicates had drifted. The table was created when the app declared at least one `*.outbox.ts` handler; the delivery worker was STARTED when at least one handler existed *including the framework's own* `voltro.webhook.emit`, which is registered whenever the app has a webhook surface. So an app with webhooks and no handler file got the worker, got `ctx.outbox`, and got a `ctx.webhooks.emit` inside a mutation writing through a table that was never planned. The 0.30.0 note claiming such an app "keeps the in-memory callback" described the intent, not the code.
73
+
74
+ Widening the table's gate to match could not work: the webhook surface includes outgoing webhooks declared on EVENTS, and the migration path detects features by walking filenames, so it cannot see them without loading the app's modules. **`_voltro_outbox` and `_voltro_outbox_attempts` are therefore created for every sql app now** — small, dialect-neutral, empty unless something enqueues, the same trade `_voltro_wakeups` and the storage tables already take. Two empty tables against a class of divergence that has no symptom until production.
75
+
76
+ It also fixes `voltro migrate`, which never passed the flag at all — and, in the same sweep, `voltro migrate` never detected `*.connection.ts` either, so the credential-vault tables were created by `voltro dev` / `voltro db apply` and silently not by `voltro migrate`. Migrate carried its own copy of the feature-detection walk; it calls the shared `detectFeatureMix` now, so there is one walk and one answer.
77
+
78
+ **And the delivery worker no longer prints a wall.** An identical drain failure is reported once at `warn`, escalated ONCE to `error` after ~a minute of consecutive identical failures ("this is not transient. Enqueued effects are NOT being delivered"), and then suppressed until the cause CHANGES or it recovers — recovery says so, with how many passes it was broken for, because a failure that stopped being logged and one that got fixed must not read alike.
79
+
80
+ **The 0.30.0 codemod note said the opposite, and it is corrected in place.** It told users that an app declaring no `*.outbox.ts` "falls back to the in-memory callback" — the intent, not the code. Normally a note under a published version cannot be revised (`selectCodemods` filters `from < version <= to`, so anyone who has already crossed 0.30.0 will never see a correction, which is why corrections are re-issued under a version nobody has reached). That rule is about a changed *instruction*, where someone who acted on the old one has to hear the new one. This is a false statement of fact with nothing attached for a reader to undo — the table is created by the declarative differ on the next `voltro dev` boot or `voltro db apply` — so the alternative was leaving every future 0.29 → 0.31 upgrader a sentence that is simply untrue.
81
+ - **@voltro/web** — **The second half of the SSR `useId` divergence: the client boot rendered a sibling to the app that the server did not.** `VoltroRuntimeProvider` renders `{children}` alongside a chrome slot (`chromeMounted ? <>…overlays…</> : null`), while the server rendered the page tree with no boot wrapper at all. A parent with two children forks React's tree-id path; a parent with one does not — so this shifted every `useId` in the app exactly as the router provider did, one level further up.
82
+
83
+ It is filed separately from the router fix because the two are independent and **each is independently fatal**: measured on a pristine tree, fixing only the router still fails and fixing only this still fails. Both paths now render `RootChromeSlot`, one component owning the arity, with `chrome: null` on the server.
84
+
85
+ The irony is worth keeping, because it is what made the defect invisible: the `chromeMounted` gate was added so the first client render matches the server DOM. It does — and that is exactly why hydration SUCCEEDS, React keeps the server markup, nothing throws, and the only casualty is the ids. The gate did not cause the fork; the slot forks whether or not it renders anything.
86
+
87
+ **What must not change without re-measuring:** the number of forks above the page on each side. Nesting DEPTH is free — measured, any number of single-child providers above the router keeps ids aligned — but adding a sibling to the app on one path only (an overlay, a portal host, a second root element) reintroduces this. `ssrTreeIdParity.test.tsx` holds it in jsdom; `scripts/browser-ssr-hydration-ids.mjs` holds it in a real chromium against a real `voltro dev`.
88
+ - **@voltro/web** — **Every `useId` in an SSR app mismatched on hydration, on every page, since the route announcer was added.** Reported by a consumer against 0.30.0 and 0.29.0 with the two `dist` bundles read side by side — not a regression, and not something any of our tests could see.
89
+
90
+ The router provider took ONE child on the server (`createElement(RouterContext.Provider, { value }, tree)`) and TWO on the client (JSX with `{content}` and the announcer, which compiles to `jsxs` with a 2-element array). React derives `useId` from the path of ARRAY SLOTS down to a fiber: a single child does not fork, a 2-element array forks and places the subtree at index 0. So the entire tree below the router sat at a different tree id on the two sides, and every id generated beneath it differed.
91
+
92
+ **The failure is unusually quiet, which is why it lasted.** The second child is `announcerReady ? <RouteAnnouncer/> : null`, and `announcerReady` starts `false` — so the first client pass renders `null`, the DOM matches, hydration SUCCEEDS, and React keeps the server markup and merely warns about the attributes. Nothing breaks visibly; the console fills with `A tree hydrated but some attributes … didn't match` for every component that calls `useId`. With Radix that is every tooltip, dialog, accordion, collapsible, select and label.
93
+
94
+ **There were TWO such divergences, not one, and each is independently fatal.** The router provider is the one the reporter found by reading the bundles; one level further up, the client's `VoltroRuntimeProvider` rendered the app ALONGSIDE a chrome slot (`{children}{chromeMounted ? … : null}`) while the server rendered no boot wrapper at all. Measured on a pristine tree: fixing only the router still fails, fixing only the chrome slot still fails, fixing both passes. So a report that names one of them is not a partial diagnosis to be discounted — it is half of the answer, and the half nobody had.
95
+
96
+ Both paths now render ONE shared component at each level — `RouterProviderTree` for the router, `RootChromeSlot` for the boot — whose second slot is always present and `null` where there is nothing to put in it. The arity is identical by construction rather than by two call sites agreeing. `ssrTreeIdParity.test.tsx` renders one page through both paths and compares a `useId`, so a future change to the shape fails at the point of change instead of in a consumer's browser. It was written red first. Its FIRST version was the cautionary tale, though: it compared the server render against a bare `<Router>` and passed while the app was still broken, because the boot-level fork it did not model is the one that was left. It hydrates through the real `VoltroRuntimeProvider` now — every hydrating path in `mount.tsx` goes through it, so a bare router is not a shape that exists. A parity test that models less than the real boot proves only that the part it models agrees.
97
+
98
+ **The methodological trap is carried in the test, because it cost the reporter an hour and would cost the next person one:** reading the id back from the DOM shows the SERVER's value on both sides — hydration deliberately does not patch ids, which is the very thing the warning says. A harness built that way reports the bug as absent. The id has to be captured from the render that computed it, and a second test proves the harness would still catch a fork.
99
+
100
+ **Verified in a real browser, not only in jsdom.** `scripts/browser-ssr-hydration-ids.mjs` boots `voltro dev` on the SSR fixture from SOURCE, loads a `renderMode: 'ssr'` page, and asserts the client computes the same `useId` the server wrote AND that react-dom logs no mismatch. Removing either half of the fix makes it print the reporter's exact string — `A tree hydrated but some attributes of the server rendered HTML didn't match the client properties` — which is the only place that message can be observed at all: the DOM is identical, hydration succeeds, nothing throws, and there is no server-side signal.
101
+ - **@voltro/web, @voltro/cli** — **The server render discarded the request's query string.** `RenderPageOptions` had no `search`, and the SSR router context hardcoded `search: ''` with a comment noting that the client reads `window.location.search` on hydration. That is true, and it is precisely why the hardcoding was wrong: the client reading the real value is what turns a discarded query string into a divergence in an exported context value. The value was already computed in both per-request boot paths — the loaders receive it — and simply never reached the renderer.
102
+
103
+ `renderPageToHtml` / `renderPageToStream` take `search` now, and `voltro start` and `voltro dev` both pass it. `build.ts` deliberately does not: a static prerender has no request and one artefact serves every visitor, so `''` is the truthful value there rather than a missing wire — and `ssrI18nParity.test.ts` encodes that difference, asserting the two per-request renderers pass it while leaving the prerender out on purpose.
104
+
105
+ **Scope, stated rather than assumed:** `useSearchParams()` was ALREADY correct on the server. It reads `requestContext.url`, which both per-request paths populate with the full request url including the query. So this fixes `RouterContext.search` — exported, and readable by an app directly — and does not on its own explain a mismatch in a page that reaches the query through that hook. Reported alongside the `useId` defect by the same consumer.
106
+
107
+ ### Internal (no consumer-facing effect)
108
+
109
+ - **@voltro/database** — `pendingAttribution`'s boundedness test no longer scores its property on the wall clock. It asserts that 12 000 registrations leave at most 10 000 entries and says nothing about how long 12 000 iterations take — but under the default 5 s timeout it had quietly become an assertion about the machine as well, and went red inside a 24-task parallel run while the whole file finishes in 480 ms on its own. That is the "a test that measures the machine" producer recorded in `packages/cli/CLAUDE.md`, and the fix is to decouple the property from the clock (an explicit generous timeout) rather than to shrink the loop — 12 000 is chosen to overrun the 10 000 cap, so a smaller burst would weaken the only thing under test. A non-vacuity assertion came with it: a cap of zero satisfies `<= 10 000` while proving nothing.
110
+
111
+ ---
112
+
42
113
  ## [0.30.0] — 2026-08-08
43
114
 
44
115
  ### ⚠ BREAKING
@@ -17,7 +17,7 @@ var l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Set(), d = [], f = ()
17
17
  };
18
18
  }, h = () => d, g = (e) => (u.add(e), () => {
19
19
  u.delete(e);
20
- }), _ = {
20
+ }), _ = ({ children: e, chrome: t = null }) => /* @__PURE__ */ c(o, { children: [e, t] }), v = {
21
21
  error: {
22
22
  brandTag: "voltro · runtime error",
23
23
  stackTrace: "Stack trace",
@@ -46,7 +46,7 @@ var l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Set(), d = [], f = ()
46
46
  " — the directory tree maps to the URL, the suffix decides what is a route at all."
47
47
  ] })
48
48
  }
49
- }, v = e("fallbackStrings", _), y = (e, t) => ({
49
+ }, y = e("fallbackStrings", v), b = (e, t) => ({
50
50
  error: {
51
51
  ...e.error,
52
52
  ...t.error
@@ -56,17 +56,17 @@ var l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Set(), d = [], f = ()
56
56
  ...t.notFound
57
57
  }
58
58
  });
59
- function b(e) {
60
- let t = n(v), i = r(() => y(t, e.strings), [t, e.strings]);
61
- return /* @__PURE__ */ s(v.Provider, {
59
+ function x(e) {
60
+ let t = n(y), i = r(() => b(t, e.strings), [t, e.strings]);
61
+ return /* @__PURE__ */ s(y.Provider, {
62
62
  value: i,
63
63
  children: e.children
64
64
  });
65
65
  }
66
- var x = (e) => {
67
- let t = n(v);
68
- return r(() => e ? y(t, e) : t, [t, e]);
69
- }, S = async (e) => {
66
+ var S = (e) => {
67
+ let t = n(y);
68
+ return r(() => e ? b(t, e) : t, [t, e]);
69
+ }, C = async (e) => {
70
70
  if (typeof navigator < "u" && navigator.clipboard?.writeText) try {
71
71
  return await navigator.clipboard.writeText(e), "native";
72
72
  } catch {}
@@ -82,7 +82,7 @@ var x = (e) => {
82
82
  } finally {
83
83
  document.body.removeChild(t);
84
84
  }
85
- }, C = {
85
+ }, w = {
86
86
  background: "oklch(0.155 0.005 285)",
87
87
  backgroundElevated: "oklch(0.19 0.006 285)",
88
88
  foreground: "oklch(0.98 0.005 285)",
@@ -97,7 +97,7 @@ var x = (e) => {
97
97
  danger: "oklch(0.7 0.21 22)",
98
98
  dangerDim: "oklch(0.5 0.18 22 / 60%)",
99
99
  info: "oklch(0.7 0.18 240)"
100
- }, w = "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", T = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", E = `
100
+ }, T = "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", E = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace", D = `
101
101
  [data-vweb-fb="root"] *,
102
102
  [data-vweb-fb="root"] *::before,
103
103
  [data-vweb-fb="root"] *::after {
@@ -116,19 +116,19 @@ var x = (e) => {
116
116
  [data-vweb-fb-btn]:hover { transform: translateY(-1px); }
117
117
  [data-vweb-fb-btn]:active { transform: translateY(0); }
118
118
  [data-vweb-fb-btn="primary"] {
119
- background-color: ${C.accent};
120
- color: ${C.background};
119
+ background-color: ${w.accent};
120
+ color: ${w.background};
121
121
  font-weight: 600;
122
- box-shadow: 0 4px 16px -2px ${C.accentGlow};
122
+ box-shadow: 0 4px 16px -2px ${w.accentGlow};
123
123
  }
124
- [data-vweb-fb-btn="primary"]:hover { background-color: ${C.accentDim}; }
124
+ [data-vweb-fb-btn="primary"]:hover { background-color: ${w.accentDim}; }
125
125
  [data-vweb-fb-btn="secondary"] {
126
- border-color: ${C.borderStrong};
127
- color: ${C.foreground};
126
+ border-color: ${w.borderStrong};
127
+ color: ${w.foreground};
128
128
  }
129
129
  [data-vweb-fb-btn="secondary"]:hover {
130
- background-color: ${C.backgroundElevated};
131
- border-color: ${C.accent};
130
+ background-color: ${w.backgroundElevated};
131
+ border-color: ${w.accent};
132
132
  }
133
133
  [data-vweb-fb-summary] {
134
134
  cursor: pointer;
@@ -138,7 +138,7 @@ var x = (e) => {
138
138
  align-items: center;
139
139
  gap: 8px;
140
140
  padding: 6px 0;
141
- color: ${C.foregroundMuted};
141
+ color: ${w.foregroundMuted};
142
142
  font-size: 12px;
143
143
  font-weight: 500;
144
144
  letter-spacing: 0.02em;
@@ -150,12 +150,12 @@ var x = (e) => {
150
150
  display: inline-block;
151
151
  font-size: 10px;
152
152
  transition: transform 120ms ease;
153
- color: ${C.foregroundSubtle};
153
+ color: ${w.foregroundSubtle};
154
154
  }
155
155
  [data-vweb-fb="root"] details[open] > [data-vweb-fb-summary]::before {
156
156
  transform: rotate(90deg);
157
157
  }
158
- [data-vweb-fb-summary]:hover { color: ${C.foreground}; }
158
+ [data-vweb-fb-summary]:hover { color: ${w.foreground}; }
159
159
  @keyframes vwebFbFadeIn {
160
160
  from { opacity: 0; }
161
161
  to { opacity: 1; }
@@ -176,7 +176,7 @@ var x = (e) => {
176
176
  animation: none;
177
177
  }
178
178
  }
179
- `, D = () => /* @__PURE__ */ s("style", { dangerouslySetInnerHTML: { __html: E } }), O = (e) => {
179
+ `, O = () => /* @__PURE__ */ s("style", { dangerouslySetInnerHTML: { __html: D } }), k = (e) => {
180
180
  if (e instanceof Error) return {
181
181
  name: e.name || "Error",
182
182
  message: e.message || "(no message)",
@@ -191,7 +191,7 @@ var x = (e) => {
191
191
  let t = e;
192
192
  return {
193
193
  name: typeof t.name == "string" ? t.name : "Error",
194
- message: typeof t.message == "string" ? t.message : k(e),
194
+ message: typeof t.message == "string" ? t.message : A(e),
195
195
  stack: typeof t.stack == "string" ? t.stack : void 0
196
196
  };
197
197
  }
@@ -200,23 +200,23 @@ var x = (e) => {
200
200
  message: String(e),
201
201
  stack: void 0
202
202
  };
203
- }, k = (e) => {
203
+ }, A = (e) => {
204
204
  try {
205
205
  return JSON.stringify(e, null, 2);
206
206
  } catch {
207
207
  return String(e);
208
208
  }
209
- }, A = (e, t) => {
209
+ }, j = (e, t) => {
210
210
  let n = e.indexOf("\n");
211
211
  return n === -1 ? e === t ? "" : e : e.slice(0, n).trim() === t.trim() ? e.slice(n + 1).replace(/^\n+/, "") : e;
212
- }, j = (e, t) => {
212
+ }, M = (e, t) => {
213
213
  if (!e) return [];
214
- let n = A(e, t);
214
+ let n = j(e, t);
215
215
  return n.length === 0 ? [] : n.split("\n");
216
- }, M = ({ tone: e, children: t }) => {
216
+ }, N = ({ tone: e, children: t }) => {
217
217
  let n = {
218
- danger: C.danger,
219
- info: C.info
218
+ danger: w.danger,
219
+ info: w.info
220
220
  }[e], r = `radial-gradient(ellipse 900px 700px at 50% 50%, color-mix(in oklch, ${n} 12%, transparent) 0%, color-mix(in oklch, ${n} 4%, transparent) 30%, oklch(0.08 0.005 285) 80%)`;
221
221
  return /* @__PURE__ */ c("div", {
222
222
  "data-vweb-fb": "root",
@@ -224,13 +224,13 @@ var x = (e) => {
224
224
  position: "fixed",
225
225
  inset: 0,
226
226
  zIndex: 2147483646,
227
- color: C.foreground,
228
- fontFamily: w,
227
+ color: w.foreground,
228
+ fontFamily: T,
229
229
  fontSize: 14,
230
230
  lineHeight: 1.5
231
231
  },
232
232
  children: [
233
- /* @__PURE__ */ s(D, {}),
233
+ /* @__PURE__ */ s(O, {}),
234
234
  /* @__PURE__ */ s("div", {
235
235
  "data-vweb-fb": "backdrop",
236
236
  style: {
@@ -256,11 +256,11 @@ var x = (e) => {
256
256
  style: {
257
257
  width: "100%",
258
258
  maxWidth: 720,
259
- background: C.card,
260
- border: `1px solid ${C.border}`,
259
+ background: w.card,
260
+ border: `1px solid ${w.border}`,
261
261
  borderRadius: 14,
262
262
  overflow: "hidden",
263
- boxShadow: `0 32px 80px -16px oklch(0 0 0 / 75%), 0 8px 24px oklch(0 0 0 / 50%), 0 0 0 1px ${C.border}, 0 0 60px -10px color-mix(in oklch, ${n} 25%, transparent)`
263
+ boxShadow: `0 32px 80px -16px oklch(0 0 0 / 75%), 0 8px 24px oklch(0 0 0 / 50%), 0 0 0 1px ${w.border}, 0 0 60px -10px color-mix(in oklch, ${n} 25%, transparent)`
264
264
  },
265
265
  children: [/* @__PURE__ */ s("div", { style: {
266
266
  height: 2,
@@ -270,10 +270,10 @@ var x = (e) => {
270
270
  })
271
271
  ]
272
272
  });
273
- }, N = ({ error: e, reset: n, strings: o }) => {
274
- let l = x(o).error, u = r(() => O(e), [e]), d = `${u.name}: ${u.message}`, f = r(() => j(u.stack, d), [u.stack, d]), [p, m] = a("idle"), h = i(null), g = t(async () => {
275
- let e = u.stack ? A(u.stack, d) : "";
276
- if (await S([
273
+ }, P = ({ error: e, reset: n, strings: o }) => {
274
+ let l = S(o).error, u = r(() => k(e), [e]), d = `${u.name}: ${u.message}`, f = r(() => M(u.stack, d), [u.stack, d]), [p, m] = a("idle"), h = i(null), g = t(async () => {
275
+ let e = u.stack ? j(u.stack, d) : "";
276
+ if (await C([
277
277
  d,
278
278
  "",
279
279
  e.length > 0 ? e : "(no stack)"
@@ -291,7 +291,7 @@ var x = (e) => {
291
291
  }, [u, d]), _ = t(() => {
292
292
  typeof window < "u" && window.location.reload();
293
293
  }, []);
294
- return /* @__PURE__ */ c(M, {
294
+ return /* @__PURE__ */ c(N, {
295
295
  tone: "danger",
296
296
  children: [
297
297
  /* @__PURE__ */ c("div", {
@@ -308,20 +308,20 @@ var x = (e) => {
308
308
  gap: 8,
309
309
  padding: "4px 10px",
310
310
  borderRadius: 999,
311
- background: `${C.danger} / 15%`,
312
- backgroundColor: `color-mix(in oklch, ${C.danger} 18%, transparent)`,
313
- color: C.danger,
311
+ background: `${w.danger} / 15%`,
312
+ backgroundColor: `color-mix(in oklch, ${w.danger} 18%, transparent)`,
313
+ color: w.danger,
314
314
  fontSize: 11,
315
315
  fontWeight: 600,
316
316
  letterSpacing: "0.06em",
317
317
  textTransform: "uppercase",
318
- border: `1px solid color-mix(in oklch, ${C.danger} 30%, transparent)`
318
+ border: `1px solid color-mix(in oklch, ${w.danger} 30%, transparent)`
319
319
  },
320
320
  children: ["▲ ", u.name]
321
321
  }), /* @__PURE__ */ s("div", {
322
322
  style: {
323
323
  fontSize: 11,
324
- color: C.foregroundSubtle,
324
+ color: w.foregroundSubtle,
325
325
  letterSpacing: "0.04em"
326
326
  },
327
327
  children: l.brandTag
@@ -333,7 +333,7 @@ var x = (e) => {
333
333
  fontSize: 20,
334
334
  fontWeight: 600,
335
335
  lineHeight: 1.35,
336
- color: C.foreground,
336
+ color: w.foreground,
337
337
  wordBreak: "break-word"
338
338
  },
339
339
  children: u.message
@@ -348,13 +348,13 @@ var x = (e) => {
348
348
  style: {
349
349
  margin: "8px 0 0",
350
350
  padding: 12,
351
- background: C.background,
352
- border: `1px solid ${C.border}`,
351
+ background: w.background,
352
+ border: `1px solid ${w.border}`,
353
353
  borderRadius: 8,
354
- fontFamily: T,
354
+ fontFamily: E,
355
355
  fontSize: 12,
356
356
  lineHeight: 1.6,
357
- color: C.foregroundMuted,
357
+ color: w.foregroundMuted,
358
358
  overflowX: "auto",
359
359
  whiteSpace: "pre",
360
360
  maxHeight: 320,
@@ -366,15 +366,15 @@ var x = (e) => {
366
366
  let [, r, i, a] = n;
367
367
  return /* @__PURE__ */ c("div", { children: [
368
368
  /* @__PURE__ */ s("span", {
369
- style: { color: C.foregroundSubtle },
369
+ style: { color: w.foregroundSubtle },
370
370
  children: r
371
371
  }),
372
372
  /* @__PURE__ */ s("span", {
373
- style: { color: C.foreground },
373
+ style: { color: w.foreground },
374
374
  children: i
375
375
  }),
376
376
  a ? /* @__PURE__ */ c("span", {
377
- style: { color: C.foregroundSubtle },
377
+ style: { color: w.foregroundSubtle },
378
378
  children: [" ", a]
379
379
  }) : null
380
380
  ] }, t);
@@ -410,8 +410,8 @@ var x = (e) => {
410
410
  borderColor: "oklch(0.72 0.15 162 / 50%)",
411
411
  color: "oklch(0.72 0.15 162)"
412
412
  } : p === "failed" ? {
413
- borderColor: `${C.danger} / 50%`,
414
- color: C.danger
413
+ borderColor: `${w.danger} / 50%`,
414
+ color: w.danger
415
415
  } : void 0,
416
416
  children: p === "copied" ? l.copied : p === "failed" ? l.copyFailed : l.copy
417
417
  })
@@ -423,13 +423,13 @@ var x = (e) => {
423
423
  style: {
424
424
  margin: "12px 24px 0",
425
425
  padding: 12,
426
- background: C.background,
427
- border: `1px dashed ${C.danger}`,
426
+ background: w.background,
427
+ border: `1px dashed ${w.danger}`,
428
428
  borderRadius: 8,
429
- fontFamily: T,
429
+ fontFamily: E,
430
430
  fontSize: 12,
431
431
  lineHeight: 1.5,
432
- color: C.foreground,
432
+ color: w.foreground,
433
433
  maxHeight: 200,
434
434
  overflow: "auto",
435
435
  whiteSpace: "pre-wrap",
@@ -437,7 +437,7 @@ var x = (e) => {
437
437
  userSelect: "text"
438
438
  },
439
439
  children: (() => {
440
- let e = u.stack ? A(u.stack, d) : "";
440
+ let e = u.stack ? j(u.stack, d) : "";
441
441
  return `${d}\n\n${e.length > 0 ? e : "(no stack)"}`;
442
442
  })()
443
443
  }) : null,
@@ -446,59 +446,59 @@ var x = (e) => {
446
446
  margin: "20px 24px",
447
447
  padding: "12px 14px",
448
448
  borderRadius: 8,
449
- background: C.backgroundElevated,
450
- border: `1px solid ${C.border}`,
451
- color: C.foregroundMuted,
449
+ background: w.backgroundElevated,
450
+ border: `1px solid ${w.border}`,
451
+ color: w.foregroundMuted,
452
452
  fontSize: 12,
453
453
  lineHeight: 1.6
454
454
  },
455
455
  children: [/* @__PURE__ */ s("div", {
456
456
  style: {
457
457
  marginBottom: 4,
458
- color: C.foreground,
458
+ color: w.foreground,
459
459
  fontWeight: 500
460
460
  },
461
461
  children: l.serverContextHeading
462
462
  }), l.serverContextHint(/* @__PURE__ */ s("code", {
463
463
  style: {
464
- fontFamily: T,
464
+ fontFamily: E,
465
465
  fontSize: 12,
466
466
  padding: "2px 6px",
467
467
  borderRadius: 4,
468
- background: C.background,
469
- border: `1px solid ${C.border}`,
470
- color: C.accent
468
+ background: w.background,
469
+ border: `1px solid ${w.border}`,
470
+ color: w.accent
471
471
  },
472
472
  children: "voltro logs --since 30s"
473
473
  }))]
474
474
  })
475
475
  ]
476
476
  });
477
- }, P = (e) => {
478
- let t = x(e.strings).notFound, n = /* @__PURE__ */ s("code", {
477
+ }, F = (e) => {
478
+ let t = S(e.strings).notFound, n = /* @__PURE__ */ s("code", {
479
479
  style: {
480
- fontFamily: T,
480
+ fontFamily: E,
481
481
  fontSize: 13,
482
482
  padding: "2px 6px",
483
483
  borderRadius: 4,
484
- background: C.background,
485
- border: `1px solid ${C.border}`,
486
- color: C.foreground
484
+ background: w.background,
485
+ border: `1px solid ${w.border}`,
486
+ color: w.foreground
487
487
  },
488
488
  children: typeof window < "u" ? window.location.pathname : "(unknown)"
489
489
  }), r = /* @__PURE__ */ s("code", {
490
490
  style: {
491
- fontFamily: T,
491
+ fontFamily: E,
492
492
  fontSize: 13,
493
493
  padding: "2px 6px",
494
494
  borderRadius: 4,
495
- background: C.background,
496
- border: `1px solid ${C.border}`,
497
- color: C.foregroundMuted
495
+ background: w.background,
496
+ border: `1px solid ${w.border}`,
497
+ color: w.foregroundMuted
498
498
  },
499
499
  children: "*.page.tsx"
500
500
  });
501
- return /* @__PURE__ */ c(M, {
501
+ return /* @__PURE__ */ c(N, {
502
502
  tone: "info",
503
503
  children: [
504
504
  /* @__PURE__ */ s("div", {
@@ -508,13 +508,13 @@ var x = (e) => {
508
508
  display: "inline-flex",
509
509
  padding: "4px 10px",
510
510
  borderRadius: 999,
511
- backgroundColor: `color-mix(in oklch, ${C.info} 18%, transparent)`,
512
- color: C.info,
511
+ backgroundColor: `color-mix(in oklch, ${w.info} 18%, transparent)`,
512
+ color: w.info,
513
513
  fontSize: 11,
514
514
  fontWeight: 600,
515
515
  letterSpacing: "0.06em",
516
516
  textTransform: "uppercase",
517
- border: `1px solid color-mix(in oklch, ${C.info} 30%, transparent)`
517
+ border: `1px solid color-mix(in oklch, ${w.info} 30%, transparent)`
518
518
  },
519
519
  children: t.badge
520
520
  })
@@ -524,14 +524,14 @@ var x = (e) => {
524
524
  margin: "16px 24px 4px",
525
525
  fontSize: 22,
526
526
  fontWeight: 600,
527
- color: C.foreground
527
+ color: w.foreground
528
528
  },
529
529
  children: t.heading
530
530
  }),
531
531
  /* @__PURE__ */ s("p", {
532
532
  style: {
533
533
  margin: "0 24px 24px",
534
- color: C.foregroundMuted,
534
+ color: w.foregroundMuted,
535
535
  fontSize: 14,
536
536
  lineHeight: 1.6
537
537
  },
@@ -541,4 +541,4 @@ var x = (e) => {
541
541
  });
542
542
  };
543
543
  //#endregion
544
- export { _ as a, m as c, b as i, g as l, P as n, x as o, S as r, h as s, N as t };
544
+ export { v as a, h as c, x as i, m as l, F as n, S as o, C as r, _ as s, P as t, g as u };