@voltro/web 0.1.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 +52 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +347 -0
- package/dist/defaultFallbacks-Bg_X-9Wv.js +541 -0
- package/dist/frameworkBoot-DBOFujAI.js +596 -0
- package/dist/frameworkBoot.d.ts +64 -0
- package/dist/frameworkBoot.js +2 -0
- package/dist/hooks.d.ts +28 -0
- package/dist/hooks.js +14 -0
- package/dist/index.d.ts +1077 -0
- package/dist/index.js +141 -0
- package/dist/mount-BuFzlw1e.js +74 -0
- package/dist/mount.d.ts +70 -0
- package/dist/mount.js +2 -0
- package/dist/serverContext-D5-Dmh8d.js +741 -0
- package/dist/ssr.d.ts +322 -0
- package/dist/ssr.js +76 -0
- package/package.json +68 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1077 @@
|
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { Context } from 'react';
|
|
3
|
+
import { ImgHTMLAttributes } from 'react';
|
|
4
|
+
import { MouseEvent as MouseEvent_2 } from 'react';
|
|
5
|
+
import { ReactElement } from 'react';
|
|
6
|
+
import { ReactNode } from 'react';
|
|
7
|
+
import { Rpc } from '@effect/rpc';
|
|
8
|
+
import { RpcGroup } from '@effect/rpc';
|
|
9
|
+
|
|
10
|
+
/** Decision outcome for a document-level anchor click. */
|
|
11
|
+
export declare type AnchorNavigationDecision = {
|
|
12
|
+
readonly kind: 'spa';
|
|
13
|
+
readonly to: string;
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: 'native';
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Map of api name → resolved typed rpc client. Installed by the
|
|
20
|
+
* framework's mount() bootstrap. Consumers read it via useAppClient().
|
|
21
|
+
*/
|
|
22
|
+
export declare type AppClients = ReadonlyMap<string, unknown>;
|
|
23
|
+
|
|
24
|
+
export declare const AppClientsContext: Context<AppClients | null>;
|
|
25
|
+
|
|
26
|
+
/** A navigation the blocker is holding back. `retry()` proceeds with the
|
|
27
|
+
* original navigation; `reset()` cancels it and clears the block. */
|
|
28
|
+
export declare interface BlockedNavigation {
|
|
29
|
+
/** The `to` argument of the held-back `navigate` call. */
|
|
30
|
+
readonly to: string;
|
|
31
|
+
/** The options of the held-back call (e.g. `{ replace: true }`). */
|
|
32
|
+
readonly opts?: NavigateOptions;
|
|
33
|
+
/** Proceed with the blocked navigation (bypasses the guard once). */
|
|
34
|
+
readonly retry: () => void;
|
|
35
|
+
/** Cancel the blocked navigation and return to the idle state. */
|
|
36
|
+
readonly reset: () => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** A registered navigation guard. Returns `true` to BLOCK the pending
|
|
40
|
+
* navigation. `to`/`opts` describe where the user is trying to go. */
|
|
41
|
+
export declare type BlockerFn = (next: {
|
|
42
|
+
readonly to: string;
|
|
43
|
+
readonly opts?: NavigateOptions;
|
|
44
|
+
}) => boolean;
|
|
45
|
+
|
|
46
|
+
/** The state a `useBlocker` returns. `blocked` is `false` until a navigation
|
|
47
|
+
* is held back; when true, `retry()`/`reset()` proceed or cancel it. */
|
|
48
|
+
export declare type BlockerState = {
|
|
49
|
+
readonly blocked: false;
|
|
50
|
+
} | {
|
|
51
|
+
readonly blocked: true;
|
|
52
|
+
/** Where the user is trying to go. */
|
|
53
|
+
readonly to: string;
|
|
54
|
+
/** Proceed with the held-back navigation. */
|
|
55
|
+
readonly retry: () => void;
|
|
56
|
+
/** Cancel the held-back navigation. */
|
|
57
|
+
readonly reset: () => void;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/** A breadcrumb entry — the last one is rendered as the current page. */
|
|
61
|
+
export declare interface BlogBreadcrumb {
|
|
62
|
+
readonly label: string;
|
|
63
|
+
readonly href?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The blog/changelog content shell. Centered max-width container with a
|
|
68
|
+
* sticky header (brand + top nav), optional breadcrumbs, and an optional
|
|
69
|
+
* footer.
|
|
70
|
+
*/
|
|
71
|
+
export declare const BlogLayout: (props: BlogLayoutProps) => ReactNode;
|
|
72
|
+
|
|
73
|
+
export declare interface BlogLayoutProps {
|
|
74
|
+
readonly brand: ReactNode;
|
|
75
|
+
readonly topNav: ReadonlyArray<BlogNavItem>;
|
|
76
|
+
readonly children: ReactNode;
|
|
77
|
+
/** Active path for top-nav + breadcrumb highlighting. */
|
|
78
|
+
readonly pathname?: string;
|
|
79
|
+
/** Optional node pinned to the right of the header, after the nav — e.g. a
|
|
80
|
+
* language switcher or action buttons. */
|
|
81
|
+
readonly headerRight?: ReactNode;
|
|
82
|
+
/** Router link component (e.g. `PlainLink` from @voltro/web). */
|
|
83
|
+
readonly Link?: ComponentType<BlogLinkProps>;
|
|
84
|
+
readonly footer?: ReactNode;
|
|
85
|
+
readonly breadcrumbs?: ReadonlyArray<BlogBreadcrumb>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Minimal shape a link component must satisfy (matches PlainLinkProps). */
|
|
89
|
+
export declare interface BlogLinkProps {
|
|
90
|
+
readonly to: string;
|
|
91
|
+
readonly children: ReactNode;
|
|
92
|
+
readonly className?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** A top-nav entry. */
|
|
96
|
+
export declare interface BlogNavItem {
|
|
97
|
+
readonly label: string;
|
|
98
|
+
readonly href: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export declare interface BrowserConsoleBridge {
|
|
102
|
+
/** Restore `console.*`, flush pending entries one last time. */
|
|
103
|
+
readonly dispose: () => void;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export declare interface BrowserConsoleBridgeOptions {
|
|
107
|
+
/** URL the CLI mounts `/_voltro/inspect/clientLog` on. Pass the
|
|
108
|
+
* same origin the page is loaded from in the common case
|
|
109
|
+
* (`window.location.origin`); the CLI's webDev middleware accepts
|
|
110
|
+
* it without CORS preflight. */
|
|
111
|
+
readonly baseUrl: string;
|
|
112
|
+
/** Voltro app name (the `name:` from `app.config.ts`). Carried in
|
|
113
|
+
* every batch so multi-app dev setups can attribute log lines. */
|
|
114
|
+
readonly appName: string;
|
|
115
|
+
/** Override flush interval (default 400ms). Mainly for tests. */
|
|
116
|
+
readonly flushAfterMs?: number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Structured result of running a route's loaders: the page (leaf) loader's
|
|
121
|
+
* data plus each chain segment's loader data, keyed by segment index.
|
|
122
|
+
*/
|
|
123
|
+
export declare interface ChainLoaderData {
|
|
124
|
+
readonly page: unknown;
|
|
125
|
+
readonly segments: Readonly<Record<number, unknown>>;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
declare interface CompiledRoute extends PageDescriptor {
|
|
129
|
+
readonly regex: RegExp;
|
|
130
|
+
/** Names of capture groups, in match order. Catch-all params capture
|
|
131
|
+
* a `/`-joined string of the remaining path segments. */
|
|
132
|
+
readonly paramNames: ReadonlyArray<string>;
|
|
133
|
+
/** Sort priority — lower wins. Static segments rank above dynamic,
|
|
134
|
+
* dynamic above catch-all. Ensures `/users/me` beats `/users/[id]` and
|
|
135
|
+
* both beat `/users/[...rest]`. */
|
|
136
|
+
readonly priority: number;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export declare const compileRoute: (route: PageDescriptor) => CompiledRoute;
|
|
140
|
+
|
|
141
|
+
/** The English defaults. A consumer overrides any subtree via
|
|
142
|
+
* {@link FallbackStringsProvider} or a per-component `strings` prop;
|
|
143
|
+
* unspecified keys fall through to these. */
|
|
144
|
+
export declare const defaultFallbackStrings: FallbackStrings;
|
|
145
|
+
|
|
146
|
+
export declare interface DevStatus {
|
|
147
|
+
/** Stable id; re-pushing the same id replaces the entry (lets a
|
|
148
|
+
* caller update its label without dropping + re-creating). */
|
|
149
|
+
readonly id: string;
|
|
150
|
+
readonly kind: DevStatusKind;
|
|
151
|
+
/** Short label rendered inside the pill, e.g. "Compiling…". Kept
|
|
152
|
+
* under ~24 chars so the pill stays visually compact. */
|
|
153
|
+
readonly label: string;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export declare type DevStatusKind = 'compiling' | 'loading' | 'reconnecting' | 'error';
|
|
157
|
+
|
|
158
|
+
export declare interface ErrorBoundaryProps {
|
|
159
|
+
readonly error: unknown;
|
|
160
|
+
readonly reset: () => void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
declare const EXTERNAL_URL_BRAND: unique symbol;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Escape-hatch for any URL the codegen can't model: cross-origin
|
|
167
|
+
* (`https://example.com`), `mailto:`, `tel:`, hash-only (`#section`),
|
|
168
|
+
* or a sibling-app route the workspace scanner missed.
|
|
169
|
+
*
|
|
170
|
+
* The wrapper is a no-op at runtime — the brand exists purely for
|
|
171
|
+
* compile-time `<Link to=…>` checks. The point is to force a deliberate
|
|
172
|
+
* choice at the call-site instead of letting any string slip past.
|
|
173
|
+
*/
|
|
174
|
+
export declare const externalUrl: (url: string) => VoltroExternalUrl;
|
|
175
|
+
|
|
176
|
+
/** Every user-facing string the shipped fallback chrome renders. Scalars are
|
|
177
|
+
* literals; anything that interpolates a value is a function so a locale can
|
|
178
|
+
* reorder. English defaults live in {@link defaultFallbackStrings}. */
|
|
179
|
+
export declare interface FallbackStrings {
|
|
180
|
+
/** <DefaultErrorFallback> — the runtime-error diagnostic card. */
|
|
181
|
+
readonly error: {
|
|
182
|
+
/** The muted brand tag at the top-right of the card. */
|
|
183
|
+
readonly brandTag: string;
|
|
184
|
+
/** The collapsible stack-trace disclosure summary. */
|
|
185
|
+
readonly stackTrace: string;
|
|
186
|
+
/** Primary action — in-place ErrorBoundary reset. */
|
|
187
|
+
readonly retry: string;
|
|
188
|
+
/** Secondary action — hard `window.location.reload()`. */
|
|
189
|
+
readonly reload: string;
|
|
190
|
+
/** Copy-to-clipboard button, idle label. */
|
|
191
|
+
readonly copy: string;
|
|
192
|
+
/** Copy button label after a successful clipboard write. */
|
|
193
|
+
readonly copied: string;
|
|
194
|
+
/** Copy button label after BOTH clipboard paths failed (points at the
|
|
195
|
+
* revealed manual-select block). */
|
|
196
|
+
readonly copyFailed: string;
|
|
197
|
+
/** The footer hint's bold heading. */
|
|
198
|
+
readonly serverContextHeading: string;
|
|
199
|
+
/** The footer hint prose. `command` is the pre-formatted `<code>` element
|
|
200
|
+
* for `voltro logs --since 30s` — a locale reorders the sentence around
|
|
201
|
+
* it but never translates the command itself. */
|
|
202
|
+
readonly serverContextHint: (command: ReactNode) => ReactNode;
|
|
203
|
+
};
|
|
204
|
+
/** <DefaultNotFound> — the route-miss (404) card. */
|
|
205
|
+
readonly notFound: {
|
|
206
|
+
/** The pill tag at the top of the card. */
|
|
207
|
+
readonly badge: string;
|
|
208
|
+
/** The headline. */
|
|
209
|
+
readonly heading: string;
|
|
210
|
+
/** The body prose. `path` is the current pathname `<code>`, `pagesDir` the
|
|
211
|
+
* `src/pages/` `<code>` — a locale reorders the sentence around both. */
|
|
212
|
+
readonly body: (path: ReactNode, pagesDir: ReactNode) => ReactNode;
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** Provide localized (or otherwise overridden) fallback strings to the shipped
|
|
217
|
+
* default ErrorBoundary / NotFound chrome below. Overrides are deep-merged onto
|
|
218
|
+
* the English defaults — supply only the sections/keys you change. Nesting
|
|
219
|
+
* providers merges onto the parent. */
|
|
220
|
+
export declare function FallbackStringsProvider(props: {
|
|
221
|
+
readonly strings: PartialFallbackStrings;
|
|
222
|
+
readonly children: ReactNode;
|
|
223
|
+
}): ReactNode;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Pick the not-found descriptor whose prefix is the longest match for
|
|
227
|
+
* `pathname`. `''` (root) always matches as a last resort. Returns null
|
|
228
|
+
* only if there are no descriptors at all.
|
|
229
|
+
*/
|
|
230
|
+
export declare const findNotFound: (notFounds: ReadonlyArray<NotFoundDescriptor>, pathname: string) => NotFoundDescriptor | null;
|
|
231
|
+
|
|
232
|
+
/** Browser-style printf substitution over a console call's args. Exported
|
|
233
|
+
* for direct unit coverage; the bridge below is its only runtime caller. */
|
|
234
|
+
export declare const formatPrintf: (args: ReadonlyArray<unknown>) => FormattedCall;
|
|
235
|
+
|
|
236
|
+
declare interface FormattedCall {
|
|
237
|
+
readonly message: string;
|
|
238
|
+
readonly rest: ReadonlyArray<unknown>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Look up a registered island Component by name. Returns undefined if
|
|
243
|
+
* the name isn't registered (typically means the user forgot to import
|
|
244
|
+
* the island file in this bundle).
|
|
245
|
+
*/
|
|
246
|
+
export declare const getIslandComponent: (name: string) => ComponentType<unknown> | undefined;
|
|
247
|
+
|
|
248
|
+
/** Read the latest snapshot. Returns a frozen-ish reference safe to
|
|
249
|
+
* use as a `useSyncExternalStore` getSnapshot result. */
|
|
250
|
+
export declare const getRouteSnapshot: () => RouteSnapshot;
|
|
251
|
+
|
|
252
|
+
/** Read the current set of statuses. Returns a STABLE reference —
|
|
253
|
+
* same array until the next mutation, so the result is safe to use
|
|
254
|
+
* as a `useSyncExternalStore` snapshot. */
|
|
255
|
+
export declare const getStatuses: () => ReadonlyArray<DevStatus>;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Client-side runtime: scan the document for `[data-voltro-island]`
|
|
259
|
+
* markers and schedule each for hydration per its strategy. Called once
|
|
260
|
+
* at mount time by `@voltro/web/mount` when the page declares
|
|
261
|
+
* `interactive: 'islands'`. Idempotent — running twice is a no-op (we
|
|
262
|
+
* mark elements as visited).
|
|
263
|
+
*/
|
|
264
|
+
export declare const hydrateIslandsOnPage: () => void;
|
|
265
|
+
|
|
266
|
+
export declare type HydrateStrategy =
|
|
267
|
+
/** Hydrate as soon as the client runtime mounts (after main script load). */
|
|
268
|
+
'load'
|
|
269
|
+
/** Hydrate when the browser is idle (via `requestIdleCallback`, with a
|
|
270
|
+
* setTimeout fallback for browsers that lack it). */
|
|
271
|
+
| 'idle'
|
|
272
|
+
/** Hydrate when the element scrolls into the viewport (IntersectionObserver). */
|
|
273
|
+
| 'visible'
|
|
274
|
+
/** Hydrate on the first pointer / keyboard interaction with the element. */
|
|
275
|
+
| 'interaction'
|
|
276
|
+
/** Never hydrate. Useful for fully-static islands (e.g. SSR-only data
|
|
277
|
+
* display that never changes). */
|
|
278
|
+
| 'never';
|
|
279
|
+
|
|
280
|
+
declare const Image_2: ({ src, alt, width, height, fill, sizes, priority, loader, placeholder, blurDataURL, style, ...rest }: ImageProps) => ReactElement;
|
|
281
|
+
export { Image_2 as Image }
|
|
282
|
+
|
|
283
|
+
/** App-level default loader for every `<Image>` below it. A per-image
|
|
284
|
+
* `loader` prop overrides this. */
|
|
285
|
+
export declare const ImageConfigProvider: ({ loader, children, }: {
|
|
286
|
+
readonly loader: ImageLoader;
|
|
287
|
+
readonly children: ReactNode;
|
|
288
|
+
}) => ReactElement;
|
|
289
|
+
|
|
290
|
+
/** Maps a logical src + a target pixel width to a concrete URL. The hook
|
|
291
|
+
* for on-the-fly resizing — e.g. `({src,width}) => \`${src}?w=${width}\``
|
|
292
|
+
* against an image CDN or the storage serve endpoint. */
|
|
293
|
+
export declare type ImageLoader = (params: {
|
|
294
|
+
readonly src: string;
|
|
295
|
+
readonly width: number;
|
|
296
|
+
}) => string;
|
|
297
|
+
|
|
298
|
+
export declare interface ImageProps extends Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'width' | 'height' | 'loading' | 'srcSet'> {
|
|
299
|
+
readonly src: string;
|
|
300
|
+
/** Required — accessibility. Use `alt=""` for purely decorative images. */
|
|
301
|
+
readonly alt: string;
|
|
302
|
+
/** Intrinsic width in px. Required unless `fill`. */
|
|
303
|
+
readonly width?: number;
|
|
304
|
+
/** Intrinsic height in px. Required unless `fill`. */
|
|
305
|
+
readonly height?: number;
|
|
306
|
+
/** Absolutely fill the nearest positioned ancestor (object-fit: cover).
|
|
307
|
+
* Use instead of width/height when the container sizes the image. */
|
|
308
|
+
readonly fill?: boolean;
|
|
309
|
+
/** `sizes` media hint, e.g. `(max-width:768px) 100vw, 50vw`. Defaults to
|
|
310
|
+
* `100vw` under `fill`. */
|
|
311
|
+
readonly sizes?: string;
|
|
312
|
+
/** Above-the-fold / LCP image: eager-load + high fetch priority. */
|
|
313
|
+
readonly priority?: boolean;
|
|
314
|
+
/** Per-image URL loader (overrides the ImageConfigProvider default). */
|
|
315
|
+
readonly loader?: ImageLoader;
|
|
316
|
+
/** `'blur'` paints `blurDataURL` behind the image until it loads. */
|
|
317
|
+
readonly placeholder?: 'blur' | 'empty';
|
|
318
|
+
readonly blurDataURL?: string;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
export declare const installBrowserConsoleBridge: (options: BrowserConsoleBridgeOptions) => BrowserConsoleBridge;
|
|
322
|
+
|
|
323
|
+
export declare const installServerLogRelay: (options: ServerLogRelayOptions) => ServerLogRelay;
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* How the client-side JS hydrates a server-rendered page.
|
|
327
|
+
*
|
|
328
|
+
* - 'full' — Hydrate the entire page tree as one React root.
|
|
329
|
+
* Default for `'spa'` pages and the safe default for
|
|
330
|
+
* `'static'` pages that haven't been thought through.
|
|
331
|
+
* Subscriptions + interactivity work everywhere.
|
|
332
|
+
* - 'islands' — Skip the full-tree hydration. The client runtime
|
|
333
|
+
* only hydrates `<div data-voltro-island>` markers
|
|
334
|
+
* emitted by `island()`. Other parts of the page stay
|
|
335
|
+
* pure static HTML with no React lifecycle running.
|
|
336
|
+
* Best perf for content-heavy pages with isolated
|
|
337
|
+
* interactive zones.
|
|
338
|
+
*
|
|
339
|
+
* Defaults to `'full'` when not specified.
|
|
340
|
+
*/
|
|
341
|
+
export declare type InteractiveMode = 'full' | 'islands' | 'none';
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Wrap a Component as an island. The returned Component renders the
|
|
345
|
+
* inner content wrapped in a marker `<div data-voltro-island>` that
|
|
346
|
+
* carries the island's name + props + hydrate strategy. The same
|
|
347
|
+
* call also registers the Component under its name so the client
|
|
348
|
+
* runtime can find it when hydrating.
|
|
349
|
+
*/
|
|
350
|
+
export declare const island: <P extends Record<string, unknown>>(Component: ComponentType<P>, options: IslandOptions) => ComponentType<P>;
|
|
351
|
+
|
|
352
|
+
export declare interface IslandOptions {
|
|
353
|
+
/** Stable id of this island. Must be unique within an app. The framework
|
|
354
|
+
* uses it to match the server-rendered marker with the client-side
|
|
355
|
+
* Component. */
|
|
356
|
+
readonly name: string;
|
|
357
|
+
/** When the client runtime should hydrate this island. Defaults to
|
|
358
|
+
* `visible` — matches Astro's default and is the best perf/UX balance. */
|
|
359
|
+
readonly hydrate?: HydrateStrategy;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** Brand guard — true for any `NotFoundError`, even one minted by a
|
|
363
|
+
* duplicate class identity in another bundle. */
|
|
364
|
+
export declare const isNotFound: (e: unknown) => e is NotFoundError;
|
|
365
|
+
|
|
366
|
+
/** Brand guard — true for any `RedirectError`, even cross-bundle. */
|
|
367
|
+
export declare const isRedirect: (e: unknown) => e is RedirectError;
|
|
368
|
+
|
|
369
|
+
/** Lazy variant of `pageRoute`: takes a thunk that dynamically imports the page
|
|
370
|
+
* instead of an already-imported module. The chunk loads only when the route
|
|
371
|
+
* first matches — the initial bundle no longer pulls in every page. */
|
|
372
|
+
export declare const lazyPageRoute: (pattern: string, load: () => Promise<Record<string, unknown>>, extras?: {
|
|
373
|
+
readonly chain?: ReadonlyArray<RouteSegment>;
|
|
374
|
+
}) => PageDescriptor;
|
|
375
|
+
|
|
376
|
+
export declare const Link: ({ to, children, onClick, prefetch: prefetchProp, replace, onMouseEnter, onFocus, ...anchorProps }: LinkProps) => ReactNode;
|
|
377
|
+
|
|
378
|
+
export declare interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href' | 'onClick'> {
|
|
379
|
+
readonly to: VoltroUrl;
|
|
380
|
+
readonly children: ReactNode;
|
|
381
|
+
readonly onClick?: (event: MouseEvent_2<HTMLAnchorElement>) => void;
|
|
382
|
+
/** When set, pre-warm the destination's loader on hover/focus. */
|
|
383
|
+
readonly prefetch?: boolean;
|
|
384
|
+
/** Swap the current history entry instead of pushing a new one —
|
|
385
|
+
* mirror of `NavigateOptions.replace`. */
|
|
386
|
+
readonly replace?: boolean;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export declare class LoaderCache {
|
|
390
|
+
private readonly entries;
|
|
391
|
+
get(key: string): LoaderEntry | undefined;
|
|
392
|
+
/**
|
|
393
|
+
* Start a loader run for `key` if one isn't already in flight (or settled).
|
|
394
|
+
* Returns the entry — callers can wait on `.promise` for pending entries.
|
|
395
|
+
*
|
|
396
|
+
* `run` is invoked SYNCHRONOUSLY so callers (and tests) can observe its
|
|
397
|
+
* single-call semantics without first flushing a microtask. Sync throws
|
|
398
|
+
* are caught and surface as a rejected entry just like async rejections.
|
|
399
|
+
*/
|
|
400
|
+
start<T>(key: string, run: (signal: AbortSignal) => Promise<T> | T): LoaderEntry<T>;
|
|
401
|
+
/** Abort a pending loader; preserves settled (success/error) entries. */
|
|
402
|
+
abort(key: string): void;
|
|
403
|
+
/** Drop entries matching `pattern` (any params). */
|
|
404
|
+
invalidate(pattern: string): void;
|
|
405
|
+
/** Drop every entry. Test-only escape hatch. */
|
|
406
|
+
clear(): void;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
export declare const loaderCacheKey: (pattern: string, params: Readonly<Record<string, string>>) => string;
|
|
410
|
+
|
|
411
|
+
export declare interface LoaderContext {
|
|
412
|
+
readonly params: Readonly<Record<string, string>>;
|
|
413
|
+
readonly pathname: string;
|
|
414
|
+
/** Aborted when the user navigates away before the loader resolves. */
|
|
415
|
+
readonly signal: AbortSignal;
|
|
416
|
+
/** Request headers when the loader runs server-side via `voltro
|
|
417
|
+
* start`. Lowercased keys. Empty `{}` for client-side loader
|
|
418
|
+
* invocations (browser-fetch goes through the rpc layer, not the
|
|
419
|
+
* loader). Use this for tenant resolution, auth headers, etc.
|
|
420
|
+
* until the auth slice ships a typed Subject. */
|
|
421
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
422
|
+
/** Server-side one-shot rpc fetch. Invokes any backend rpc (queries
|
|
423
|
+
* included — the FIRST snapshot of a streaming query is resolved and
|
|
424
|
+
* returned) over the api's HTTP rpc surface (`POST /rpc`), forwarding
|
|
425
|
+
* the request's `cookie` header so the api resolves the same Subject +
|
|
426
|
+
* tenant the WS path would. Bound to the app's single / first api.
|
|
427
|
+
*
|
|
428
|
+
* Present ONLY when the loader runs server-side (`voltro start` /
|
|
429
|
+
* `voltro dev` SSR). `undefined` for client-side loader invocations —
|
|
430
|
+
* in the browser, use `useSubscription` in the component for live data
|
|
431
|
+
* instead; the loader's `query` is for SSR first-paint + `meta`. */
|
|
432
|
+
readonly query?: <T = unknown>(tag: string, input?: Record<string, unknown>) => Promise<T>;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/* Excluded from this release type: LoaderDataContext */
|
|
436
|
+
|
|
437
|
+
declare type LoaderEntry<T = unknown> = {
|
|
438
|
+
readonly status: 'pending';
|
|
439
|
+
readonly promise: Promise<void>;
|
|
440
|
+
readonly controller: AbortController;
|
|
441
|
+
} | {
|
|
442
|
+
readonly status: 'success';
|
|
443
|
+
readonly data: T;
|
|
444
|
+
} | {
|
|
445
|
+
readonly status: 'error';
|
|
446
|
+
readonly error: unknown;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
export declare type LoaderFn<T = unknown> = (ctx: LoaderContext) => Promise<T> | T;
|
|
450
|
+
|
|
451
|
+
export declare const matchRoute: (compiled: ReadonlyArray<CompiledRoute>, pathname: string) => RouteMatch | null;
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Boot the framework's React stack against the given app. Resolves the
|
|
455
|
+
* typed rpc client for each api in parallel, renders the user's component
|
|
456
|
+
* tree inside the framework runtime + clients providers, and in dev mode
|
|
457
|
+
* auto-recovers each client in-place when its dev server restarts.
|
|
458
|
+
*/
|
|
459
|
+
export declare const mount: (App: ComponentType, options: MountOptions) => void;
|
|
460
|
+
|
|
461
|
+
export declare interface MountApiSpec<Rpcs extends Rpc.Any = Rpc.Any> {
|
|
462
|
+
/** The RpcGroup this api speaks. */
|
|
463
|
+
readonly group: RpcGroup.RpcGroup<Rpcs>;
|
|
464
|
+
/** Per-rpc-tag descriptor metadata: `{ kind, source?, targets? }`. The
|
|
465
|
+
* framework's auto-optimistic flow reads this to know which mutation
|
|
466
|
+
* affects which cached query. The CLI's codegen emits it as
|
|
467
|
+
* `appDescriptors` next to `appGroup`. */
|
|
468
|
+
readonly descriptors?: Readonly<Record<string, {
|
|
469
|
+
readonly kind: 'query' | 'mutation' | 'action' | 'stream' | 'workflow';
|
|
470
|
+
readonly source?: string;
|
|
471
|
+
readonly targets?: ReadonlyArray<{
|
|
472
|
+
readonly table: string;
|
|
473
|
+
readonly op: 'insert' | 'update' | 'delete';
|
|
474
|
+
readonly order?: 'prepend' | 'append';
|
|
475
|
+
}>;
|
|
476
|
+
}>>;
|
|
477
|
+
/** Explicit WebSocket URL for this api. Required for external apis
|
|
478
|
+
* (where there's no Vite proxy). Defaults to same-origin `/ws/<name>`
|
|
479
|
+
* for workspace apis served via the framework's dev proxy. */
|
|
480
|
+
readonly wsUrl?: string;
|
|
481
|
+
/** Auth headers sent with EVERY rpc request over the socket — this is how
|
|
482
|
+
* a cross-origin api authenticates WS subscriptions. They ride in the
|
|
483
|
+
* @effect/rpc message frame, NOT the WS handshake (a browser can't set
|
|
484
|
+
* upgrade headers). Pass a thunk to fetch a rotating token fresh on every
|
|
485
|
+
* (re)connect, e.g. `headers: async () => ({ authorization: 'Bearer ' +
|
|
486
|
+
* (await supabase.auth.getSession()).data.session?.access_token })`; a
|
|
487
|
+
* static object is also accepted. Do NOT hardcode a secret literal — it
|
|
488
|
+
* ships to the client and is visible in source; resolve it at runtime. */
|
|
489
|
+
readonly headers?: ResolvableHeaders;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export declare interface MountOptions {
|
|
493
|
+
/** Map of api name → spec. The name is the lookup key for hooks
|
|
494
|
+
* (`useAppClient<T>(name)`, `useSubscription(name, ...)`, etc.). */
|
|
495
|
+
readonly apis: Record<string, MountApiSpec>;
|
|
496
|
+
/** Override the root element id. Defaults to 'root'. */
|
|
497
|
+
readonly rootId?: string;
|
|
498
|
+
/** Optional in-page devtools overlay component. Wired up by the
|
|
499
|
+
* CLI's generated `main.tsx` when `import.meta.env.DEV` is truthy
|
|
500
|
+
* and the app hasn't set `disableDevtools` in app.config.ts.
|
|
501
|
+
* Renders INSIDE the FrameworkBoot tree so its hooks
|
|
502
|
+
* (useFrameworkRuntimes, useLoaderData, …) resolve normally.
|
|
503
|
+
* PascalCase because it is a React component reference rendered
|
|
504
|
+
* as <Devtools/>. */
|
|
505
|
+
readonly Devtools?: ComponentType;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/** Options for an imperative navigation. `replace` swaps the current
|
|
509
|
+
* history entry instead of pushing a new one — what a loader redirect
|
|
510
|
+
* wants so Back doesn't bounce the user onto the page that redirected. */
|
|
511
|
+
export declare interface NavigateOptions {
|
|
512
|
+
readonly replace?: boolean;
|
|
513
|
+
/** Set to `false` to preserve the current scroll position. Defaults to true. */
|
|
514
|
+
readonly scroll?: boolean;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export declare const NavigationIndicator: () => ReactNode;
|
|
518
|
+
|
|
519
|
+
/** Construct-and-throw sugar for `NotFoundError`. Typed `: never` so
|
|
520
|
+
* control-flow narrows at the call site (`const x = row ?? notFound()`). */
|
|
521
|
+
export declare const notFound: (detail?: string) => never;
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Stand-alone descriptor for URL prefixes that have no concrete page but
|
|
525
|
+
* SHOULD respond to unmatched subpaths with a scoped `not-found.tsx`. The
|
|
526
|
+
* router picks the longest-prefix match.
|
|
527
|
+
*/
|
|
528
|
+
export declare interface NotFoundDescriptor {
|
|
529
|
+
/** Path prefix this not-found is responsible for. `''` = root. */
|
|
530
|
+
readonly prefix: string;
|
|
531
|
+
readonly Component: ComponentType;
|
|
532
|
+
readonly chain?: ReadonlyArray<RouteSegment> | undefined;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/** Thrown from a loader to render the scoped `not-found.tsx` subtree
|
|
536
|
+
* (client nav) / emit a 404 (SSR / SSG). */
|
|
537
|
+
export declare class NotFoundError extends Error {
|
|
538
|
+
readonly _voltroControl: "not-found";
|
|
539
|
+
constructor(detail?: string);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export declare interface PageDescriptor<TLoaderData = unknown> {
|
|
543
|
+
/** URL pattern, e.g. `/`, `/about`, `/users/[id]`, `/docs/[...slug]`. */
|
|
544
|
+
readonly pattern: string;
|
|
545
|
+
/** The page component. Present on eager routes; on a LAZY route (see
|
|
546
|
+
* `load`) it's filled by the Router once the page chunk has loaded. */
|
|
547
|
+
readonly Component?: ComponentType | undefined;
|
|
548
|
+
/** LAZY route: a thunk that dynamically imports the page module. When set,
|
|
549
|
+
* the page chunk is fetched only when this route first matches, so the
|
|
550
|
+
* initial bundle never imports every page. The Router resolves
|
|
551
|
+
* `Component` / `loader` / `meta` / … from the loaded module; the layout
|
|
552
|
+
* `chain` stays eager. */
|
|
553
|
+
readonly load?: (() => Promise<Record<string, unknown>>) | undefined;
|
|
554
|
+
/** Static meta, or a function of `{ params, loaderData, locale }` —
|
|
555
|
+
* so a document title can read what the page loader fetched (the
|
|
556
|
+
* dynamic metadata case) AND can localise per-locale at SSG time
|
|
557
|
+
* for URL-prefix i18n routing. `loaderData` is the page loader's
|
|
558
|
+
* result; `locale` is the active i18n locale during pre-render
|
|
559
|
+
* (driven by `params.locale` for `[locale]/...` routes, falling
|
|
560
|
+
* back to the app's defaultLocale). On the client, `locale` is
|
|
561
|
+
* the URL-derived locale when the route is locale-prefixed,
|
|
562
|
+
* otherwise the framework's resolved locale (cookie / Accept-
|
|
563
|
+
* Language / default). */
|
|
564
|
+
readonly meta?: PageMeta | ((ctx: {
|
|
565
|
+
readonly params: Readonly<Record<string, string>>;
|
|
566
|
+
readonly loaderData: unknown;
|
|
567
|
+
readonly locale: string;
|
|
568
|
+
}) => PageMeta) | undefined;
|
|
569
|
+
/** Async data preload; result available via useLoaderData<T>(). */
|
|
570
|
+
readonly loader?: LoaderFn<TLoaderData> | undefined;
|
|
571
|
+
/** Catches render + loader errors for THIS leaf. Tighter scope than `chain[*].Error`. */
|
|
572
|
+
readonly ErrorBoundary?: ComponentType<ErrorBoundaryProps> | undefined;
|
|
573
|
+
/** Shown while the loader is in flight (first navigation to the route). */
|
|
574
|
+
readonly Pending?: ComponentType | undefined;
|
|
575
|
+
/**
|
|
576
|
+
* Outer-to-inner layer chain. Each segment maps to one directory level
|
|
577
|
+
* (or route group) on the way from the pages root down to this leaf.
|
|
578
|
+
* Codegen produces this from the discovery walk; user code rarely
|
|
579
|
+
* writes it by hand.
|
|
580
|
+
*/
|
|
581
|
+
readonly chain?: ReadonlyArray<RouteSegment> | undefined;
|
|
582
|
+
/** How the page's HTML is produced. Defaults to 'static' so pages
|
|
583
|
+
* pre-render by default — SEO + first-paint speed are wins users
|
|
584
|
+
* pay nothing for. Opt out with `'spa'` for pages that need a
|
|
585
|
+
* per-request render. */
|
|
586
|
+
readonly renderMode?: RenderMode | undefined;
|
|
587
|
+
/** How the page's client-side JS hydrates. Defaults to `'full'`
|
|
588
|
+
* (whole tree). Pages designed around the islands pattern should
|
|
589
|
+
* set this to `'islands'` so only marked interactive zones
|
|
590
|
+
* hydrate; the rest stays pure static HTML. */
|
|
591
|
+
readonly interactive?: InteractiveMode | undefined;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export declare interface PageMeta {
|
|
595
|
+
readonly title?: string;
|
|
596
|
+
readonly description?: string;
|
|
597
|
+
/** Canonical URL for this page. Emitted as `<link rel="canonical">`.
|
|
598
|
+
* Relative paths are accepted — search engines resolve them
|
|
599
|
+
* against the document's base URL. Use this on duplicated routes
|
|
600
|
+
* (locale variants, alternate URL forms) to declare the preferred
|
|
601
|
+
* one indexable URL. */
|
|
602
|
+
readonly canonical?: string;
|
|
603
|
+
/** Arbitrary additional meta tags. `name` OR `property` is required. */
|
|
604
|
+
readonly tags?: ReadonlyArray<{
|
|
605
|
+
readonly name?: string;
|
|
606
|
+
readonly property?: string;
|
|
607
|
+
readonly content: string;
|
|
608
|
+
}>;
|
|
609
|
+
/** Arbitrary additional `<link>` tags. Use for `alternate` hreflang,
|
|
610
|
+
* prev/next pagination, RSS feeds, manifest, etc. (For the common
|
|
611
|
+
* canonical case, prefer the dedicated `canonical` field above.) */
|
|
612
|
+
readonly links?: ReadonlyArray<{
|
|
613
|
+
readonly rel: string;
|
|
614
|
+
readonly href: string;
|
|
615
|
+
readonly hreflang?: string;
|
|
616
|
+
readonly type?: string;
|
|
617
|
+
readonly title?: string;
|
|
618
|
+
}>;
|
|
619
|
+
/** JSON-LD structured-data payloads. Each entry is emitted as a
|
|
620
|
+
* separate `<script type="application/ld+json">` so search engines
|
|
621
|
+
* and LLM crawlers can pick up rich metadata (Article, Product,
|
|
622
|
+
* SoftwareApplication, BreadcrumbList, FAQPage, etc.). Anything
|
|
623
|
+
* JSON-serialisable is accepted; `@context` + `@type` are
|
|
624
|
+
* authored, not synthesised. */
|
|
625
|
+
readonly jsonLd?: ReadonlyArray<Record<string, unknown>>;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
export declare const pageRoute: (pattern: string, mod: Record<string, unknown>, extras?: {
|
|
629
|
+
readonly chain?: ReadonlyArray<RouteSegment>;
|
|
630
|
+
}) => PageDescriptor;
|
|
631
|
+
|
|
632
|
+
export declare const parseCookieHeader: (raw: string | undefined) => Record<string, string>;
|
|
633
|
+
|
|
634
|
+
/** A caller may override any subtree, not the whole object — deep-partial per
|
|
635
|
+
* section (each section is a flat bag of literals/functions, so a shallow
|
|
636
|
+
* per-section merge is exact). */
|
|
637
|
+
export declare type PartialFallbackStrings = {
|
|
638
|
+
readonly [K in keyof FallbackStrings]?: Partial<FallbackStrings[K]>;
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
/** Default: no resizing — every candidate width resolves to the same URL.
|
|
642
|
+
* Still emits a (degenerate) srcSet so swapping in a real loader is a
|
|
643
|
+
* one-line change with no call-site churn. */
|
|
644
|
+
export declare const passthroughImageLoader: ImageLoader;
|
|
645
|
+
|
|
646
|
+
export declare const PlainLink: (props: PlainLinkProps) => ReactNode;
|
|
647
|
+
|
|
648
|
+
export declare interface PlainLinkProps {
|
|
649
|
+
readonly to: string;
|
|
650
|
+
readonly children: ReactNode;
|
|
651
|
+
readonly className?: string;
|
|
652
|
+
readonly style?: React.CSSProperties;
|
|
653
|
+
readonly onClick?: (event: MouseEvent_2<HTMLAnchorElement>) => void;
|
|
654
|
+
readonly prefetch?: boolean;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Preload the page module for the route matching `pathname`, when that route is
|
|
659
|
+
* lazy. `load()` fetches the chunk AND — via the codegen's lazy thunk — records
|
|
660
|
+
* the module in the app's `refs.pages` registry, so the NEXT `buildRoutes()`
|
|
661
|
+
* yields an EAGER route for it.
|
|
662
|
+
*
|
|
663
|
+
* The generated `main.tsx` awaits this for the current URL BEFORE `hydrateRoot`
|
|
664
|
+
* whenever it's hydrating pre-rendered HTML. Without it, the client's first
|
|
665
|
+
* render resolves a lazy leaf to `null` (its chunk hasn't loaded yet) while the
|
|
666
|
+
* SSG/SSR HTML rendered the full page — a `<main>`-content hydration mismatch
|
|
667
|
+
* (React #418) that regenerates the tree and shifts every `useId` beneath it.
|
|
668
|
+
* With the module preloaded, the first client render matches the server exactly.
|
|
669
|
+
*
|
|
670
|
+
* No-op for eager routes (already resolvable) or an unmatched path. A failed
|
|
671
|
+
* chunk load is swallowed here — it surfaces via the leaf error boundary when
|
|
672
|
+
* the route renders.
|
|
673
|
+
*/
|
|
674
|
+
export declare const preloadRouteModule: (routes: ReadonlyArray<PageDescriptor>, pathname: string) => Promise<void>;
|
|
675
|
+
|
|
676
|
+
/** Publish a status. Returns the dispose handle — call it when the
|
|
677
|
+
* underlying work finishes. Safe to call dispose twice. */
|
|
678
|
+
export declare const pushStatus: (status: DevStatus) => (() => void);
|
|
679
|
+
|
|
680
|
+
export declare const ReconnectContext: Context<(() => void) | null>;
|
|
681
|
+
|
|
682
|
+
/** Construct-and-throw sugar for `RedirectError`. Typed `: never`. */
|
|
683
|
+
export declare const redirect: (location: string, opts?: {
|
|
684
|
+
readonly status?: 303 | 307 | 308;
|
|
685
|
+
}) => never;
|
|
686
|
+
|
|
687
|
+
/** Thrown from a loader to redirect: 3xx + `Location` on SSR, a
|
|
688
|
+
* client-side `navigate(..., { replace: true })` on the SPA path. */
|
|
689
|
+
export declare class RedirectError extends Error {
|
|
690
|
+
readonly _voltroControl: "redirect";
|
|
691
|
+
readonly location: string;
|
|
692
|
+
readonly status: 303 | 307 | 308;
|
|
693
|
+
constructor(location: string, opts?: {
|
|
694
|
+
readonly status?: 303 | 307 | 308;
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
export declare const registerClientTrace: (traceId: string) => void;
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* How a page's HTML is produced.
|
|
702
|
+
*
|
|
703
|
+
* - 'static' — Pre-rendered to flat HTML at build time. Default.
|
|
704
|
+
* Shipped as `dist/<route>/index.html`. JS bundle
|
|
705
|
+
* still loads to hydrate islands + handle SPA-style
|
|
706
|
+
* navigation, but the first paint is instant + SEO
|
|
707
|
+
* friendly. Loaders MUST be side-effect-free + must
|
|
708
|
+
* not require per-request context (subject, headers).
|
|
709
|
+
* - 'spa' — Client-only rendering. No HTML pre-render; the
|
|
710
|
+
* shipped HTML is a shell that mounts on the
|
|
711
|
+
* client. Useful for pages that genuinely need a
|
|
712
|
+
* fresh client render every load (most reactive
|
|
713
|
+
* dashboards).
|
|
714
|
+
*
|
|
715
|
+
* - 'ssr' — Rendered on every request, server-side, via `voltro
|
|
716
|
+
* start`. Loaders run per request with access to
|
|
717
|
+
* `headers` + `query` (the api HTTP rpc); the resolved
|
|
718
|
+
* HTML is sent fresh each time. Use for personalised /
|
|
719
|
+
* tenant-scoped first paint.
|
|
720
|
+
* - 'isr' — Like 'ssr' on the first request, then cached and
|
|
721
|
+
* revalidated in the background on a TTL.
|
|
722
|
+
*/
|
|
723
|
+
export declare type RenderMode = 'static' | 'spa' | 'ssr' | 'isr';
|
|
724
|
+
|
|
725
|
+
/**
|
|
726
|
+
* Auth headers for an api's WS rpc client. @effect/rpc sends these in the
|
|
727
|
+
* per-request MESSAGE frame (NOT the WS upgrade — a browser can't set upgrade
|
|
728
|
+
* headers), so the server's AuthMiddleware sees them on every call; this is
|
|
729
|
+
* what lets a cross-origin api authenticate WS subscriptions. A thunk is
|
|
730
|
+
* resolved fresh on every (re)connect, so a rotating token (e.g. a Supabase
|
|
731
|
+
* access token) is pulled anew per connect instead of freezing at first mount.
|
|
732
|
+
*/
|
|
733
|
+
declare type ResolvableHeaders = Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Decide whether a click on (or inside) an anchor should be handled by the
|
|
737
|
+
* SPA router or left to the browser. Pure — no side effects, no
|
|
738
|
+
* `preventDefault`. The caller performs the navigation + preventDefault
|
|
739
|
+
* when the result is `'spa'`.
|
|
740
|
+
*
|
|
741
|
+
* Returns `'native'` (browser handles it) when ANY of:
|
|
742
|
+
* - the event was already handled (`defaultPrevented` — a `<Link>` ran)
|
|
743
|
+
* - it's not a plain left-click (middle/right click, any modifier key)
|
|
744
|
+
* - no enclosing `<a>`, or the anchor has no `href`
|
|
745
|
+
* - the anchor targets another browsing context (`target` ≠ `_self`)
|
|
746
|
+
* - the anchor is a download, `rel="external"`, or opts out (`data-no-spa`)
|
|
747
|
+
* - the resolved URL is cross-origin
|
|
748
|
+
* - the resolved URL is a pure in-page hash on the current page
|
|
749
|
+
*
|
|
750
|
+
* Otherwise returns `'spa'` with the path+search+hash to navigate to.
|
|
751
|
+
*
|
|
752
|
+
* `currentHref` is the document's current URL (`location.href`) — passed in
|
|
753
|
+
* so the function is deterministic and unit-testable.
|
|
754
|
+
*/
|
|
755
|
+
export declare const resolveAnchorNavigation: (event: {
|
|
756
|
+
readonly defaultPrevented: boolean;
|
|
757
|
+
readonly button: number;
|
|
758
|
+
readonly metaKey: boolean;
|
|
759
|
+
readonly ctrlKey: boolean;
|
|
760
|
+
readonly shiftKey: boolean;
|
|
761
|
+
readonly altKey: boolean;
|
|
762
|
+
readonly target: EventTarget | null;
|
|
763
|
+
}, currentHref: string) => AnchorNavigationDecision;
|
|
764
|
+
|
|
765
|
+
export declare const resolveMeta: (meta: PageDescriptor["meta"], params: Readonly<Record<string, string>>, loaderData?: unknown, locale?: string) => PageMeta | null;
|
|
766
|
+
|
|
767
|
+
declare const ROUTE_URL_BRAND: unique symbol;
|
|
768
|
+
|
|
769
|
+
export declare interface RouteMatch {
|
|
770
|
+
readonly route: CompiledRoute;
|
|
771
|
+
readonly params: Readonly<Record<string, string>>;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
export declare const Router: ({ routes, notFounds, notFound: NotFound, errorFallback: ErrorFallback, }: RouterProps) => ReactNode;
|
|
775
|
+
|
|
776
|
+
/* Excluded from this release type: RouterContext */
|
|
777
|
+
|
|
778
|
+
declare interface RouterContextValue {
|
|
779
|
+
readonly pathname: string;
|
|
780
|
+
/** `search + hash` of the current URL (e.g. `?tab=skills#x`). Tracked in
|
|
781
|
+
* state so query-param-only navigations (same pathname) still re-render
|
|
782
|
+
* consumers — `useSearchParams`/`useLocation` would otherwise go stale
|
|
783
|
+
* until an unrelated re-render. */
|
|
784
|
+
readonly search: string;
|
|
785
|
+
readonly navigate: (to: string, opts?: NavigateOptions) => void;
|
|
786
|
+
readonly params: Readonly<Record<string, string>>;
|
|
787
|
+
readonly prefetch: (to: string) => void;
|
|
788
|
+
/** Internal: loader data for the active match. */
|
|
789
|
+
readonly loaderData: unknown;
|
|
790
|
+
/** Internal: register a navigation guard; returns an unregister fn.
|
|
791
|
+
* Consumed by `useBlocker`. */
|
|
792
|
+
readonly registerBlocker: (id: symbol, fn: BlockerFn) => () => void;
|
|
793
|
+
/** Internal: the navigation currently held back by a blocker, if any.
|
|
794
|
+
* Consumed by `useBlocker` to surface `retry`/`reset` to the caller. */
|
|
795
|
+
readonly blocked: BlockedNavigation | null;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
export declare interface RouterProps {
|
|
799
|
+
readonly routes: ReadonlyArray<PageDescriptor>;
|
|
800
|
+
/**
|
|
801
|
+
* Per-prefix not-found descriptors. Router picks the longest-prefix
|
|
802
|
+
* match when no concrete route hits. `notFound` (component) is the
|
|
803
|
+
* fallback for paths without any matching prefix.
|
|
804
|
+
*/
|
|
805
|
+
readonly notFounds?: ReadonlyArray<NotFoundDescriptor>;
|
|
806
|
+
readonly notFound?: ComponentType;
|
|
807
|
+
/** Fallback ErrorBoundary used when neither the leaf nor its chain supplies one. */
|
|
808
|
+
readonly errorFallback?: ComponentType<ErrorBoundaryProps>;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* One layer in a page's nesting chain. Each represents a directory level
|
|
813
|
+
* in the file-convention layout (or a route group).
|
|
814
|
+
*
|
|
815
|
+
* Layouts wrap their children — outer layers wrap inner ones. Errors are
|
|
816
|
+
* caught by the deepest layer with an `Error` component; if none, the
|
|
817
|
+
* error bubbles up the chain. NotFound applies when an URL is under this
|
|
818
|
+
* layer's scope but no concrete page matches.
|
|
819
|
+
*
|
|
820
|
+
* The chain persists across navigation within its scope — React preserves
|
|
821
|
+
* the layout component instances because their position in the tree is
|
|
822
|
+
* stable for any leaf within that subtree.
|
|
823
|
+
*/
|
|
824
|
+
export declare interface RouteSegment {
|
|
825
|
+
/** Outer-vs-inner ordering label (debug only). */
|
|
826
|
+
readonly id?: string | undefined;
|
|
827
|
+
readonly Layout?: ComponentType<{
|
|
828
|
+
children: ReactNode;
|
|
829
|
+
}> | undefined;
|
|
830
|
+
readonly Error?: ComponentType<ErrorBoundaryProps> | undefined;
|
|
831
|
+
readonly Pending?: ComponentType | undefined;
|
|
832
|
+
readonly NotFound?: ComponentType | undefined;
|
|
833
|
+
/** Async data preload for THIS layout level. Runs in parallel with the
|
|
834
|
+
* page loader (and the other layouts') before mount; the result is
|
|
835
|
+
* reachable via `useLoaderData<T>()` from INSIDE this layout (each
|
|
836
|
+
* level sees its own loader's data). A `layout.tsx` exports it like a
|
|
837
|
+
* page does — codegen wires it onto the segment. Server-side it
|
|
838
|
+
* receives request `headers` (session/tenant resolution). */
|
|
839
|
+
readonly loader?: LoaderFn | undefined;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
declare interface RouteSnapshot {
|
|
843
|
+
readonly pathname: string;
|
|
844
|
+
readonly params: Readonly<Record<string, string>>;
|
|
845
|
+
/** Loader-resolved data for the active route. `undefined` when the
|
|
846
|
+
* route has no loader OR the loader rejected. */
|
|
847
|
+
readonly loaderData: unknown;
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
/** The next search-params value: either the params object directly, or an
|
|
851
|
+
* updater that receives the CURRENT `URLSearchParams` and returns the next. */
|
|
852
|
+
export declare type SearchParamsInit = URLSearchParams | Readonly<Record<string, string>> | ((current: URLSearchParams) => URLSearchParams | Record<string, string>);
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Cache key for a layout (chain segment) loader. Distinct from the page's
|
|
856
|
+
* key for the same (pattern, params) so a layout's data and the page's
|
|
857
|
+
* never collide. `index` is the segment's position in the outer→inner
|
|
858
|
+
* chain. Defined above `loaderCacheKey`'s body via the shared formatter.
|
|
859
|
+
*/
|
|
860
|
+
export declare const segmentLoaderKey: (pattern: string, index: number, params: Readonly<Record<string, string>>) => string;
|
|
861
|
+
|
|
862
|
+
export declare interface ServerLogRelay {
|
|
863
|
+
readonly dispose: () => void;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
export declare interface ServerLogRelayOptions {
|
|
867
|
+
/** HTTP(S) origin the CLI exposes the inspect stream on. */
|
|
868
|
+
readonly baseUrl: string;
|
|
869
|
+
/** Short label included in every emitted prefix — typically the api
|
|
870
|
+
* name ("cloud") or "web". Helps disambiguate multi-source streams. */
|
|
871
|
+
readonly originLabel: string;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
export declare const ServerRequestContext: Context<ServerRequestContextValue | null>;
|
|
875
|
+
|
|
876
|
+
export declare interface ServerRequestContextValue {
|
|
877
|
+
readonly cookies: Readonly<Record<string, string>>;
|
|
878
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
879
|
+
/** Raw request URL as it came off the wire (path + query). Useful
|
|
880
|
+
* for SSR pages that need to read `?q=…` style search params
|
|
881
|
+
* without touching anything client-only. Empty string for build-
|
|
882
|
+
* time SSG renders where there is no incoming request. */
|
|
883
|
+
readonly url: string;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
export declare const ServerRequestProvider: ({ value, children, }: {
|
|
887
|
+
readonly value: ServerRequestContextValue;
|
|
888
|
+
readonly children: ReactNode;
|
|
889
|
+
}) => ReactNode;
|
|
890
|
+
|
|
891
|
+
/** Push a new route snapshot. Router calls this from its render
|
|
892
|
+
* effect; observers re-render on the next microtask. */
|
|
893
|
+
export declare const setRouteSnapshot: (next: RouteSnapshot) => void;
|
|
894
|
+
|
|
895
|
+
/** How a `useSetSearchParams` write should update history. Defaults to
|
|
896
|
+
* `replace` — a filter/tab tweak shouldn't stack a Back entry per keystroke. */
|
|
897
|
+
export declare interface SetSearchParamsOptions {
|
|
898
|
+
/** Push a new history entry instead of replacing the current one. */
|
|
899
|
+
readonly push?: boolean;
|
|
900
|
+
/** Set to `false` to preserve the current scroll position. Defaults to true. */
|
|
901
|
+
readonly scroll?: boolean;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Stable-sort compiled routes by ascending priority, so that more-specific
|
|
906
|
+
* routes (lower priority) match before less-specific ones. Static beats
|
|
907
|
+
* dynamic; dynamic beats catch-all. Within the same priority bucket,
|
|
908
|
+
* declaration order is preserved.
|
|
909
|
+
*/
|
|
910
|
+
export declare const sortRoutesByPriority: (compiled: ReadonlyArray<CompiledRoute>) => ReadonlyArray<CompiledRoute>;
|
|
911
|
+
|
|
912
|
+
/** Subscribe to snapshot changes. Returns the unsubscribe function. */
|
|
913
|
+
export declare const subscribeRouteSnapshot: (cb: () => void) => (() => void);
|
|
914
|
+
|
|
915
|
+
/** Subscribe to changes. Returns the unsubscribe function. */
|
|
916
|
+
export declare const subscribeStatuses: (cb: () => void) => (() => void);
|
|
917
|
+
|
|
918
|
+
/** Subscribe to surfaced trace errors. Returns an unsubscribe fn. */
|
|
919
|
+
export declare const subscribeTraceErrors: (listener: TraceErrorListener) => () => void;
|
|
920
|
+
|
|
921
|
+
export declare interface TraceErrorEvent {
|
|
922
|
+
/** Trace id (32 hex) — the same id `voltro logs --trace <id>` accepts. */
|
|
923
|
+
readonly traceId: string;
|
|
924
|
+
/** Span that failed (e.g. `rpc.mutation`, `webhook/stripe`). */
|
|
925
|
+
readonly spanName: string;
|
|
926
|
+
/** Which api the failed span ran in (the relay's originLabel). */
|
|
927
|
+
readonly api: string;
|
|
928
|
+
/** Error message from the span status. */
|
|
929
|
+
readonly message: string;
|
|
930
|
+
/** When the overlay received it. */
|
|
931
|
+
readonly ts: number;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
declare type TraceErrorListener = (event: TraceErrorEvent) => void;
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* Returns the typed rpc client mounted under `apiName`. Generic narrows
|
|
938
|
+
* the result to the caller's group; pass `typeof appGroup` from the api's
|
|
939
|
+
* generated rpcGroup module:
|
|
940
|
+
*
|
|
941
|
+
* import type { appGroup } from '@app/api/rpcGroup'
|
|
942
|
+
* import type { RpcClient, RpcGroup } from '@effect/rpc'
|
|
943
|
+
* type ApiClient = RpcClient.RpcClient<RpcGroup.Rpcs<typeof appGroup>>
|
|
944
|
+
*
|
|
945
|
+
* const client = useAppClient<ApiClient>('api')
|
|
946
|
+
* const { data } = useSubscription('api', () => client.todos.list({}), [])
|
|
947
|
+
*
|
|
948
|
+
* Throws if no api is mounted under `apiName` (typo, missing entry in
|
|
949
|
+
* app.config.ts, or app not mounted yet).
|
|
950
|
+
*/
|
|
951
|
+
export declare const useAppClient: <Client>(apiName: string) => Client;
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Block SPA navigations while `shouldBlock` is true — the unsaved-changes
|
|
955
|
+
* guard. When the user tries to leave (a `<Link>` click, an intercepted
|
|
956
|
+
* anchor, or an imperative `navigate`) and `shouldBlock` returns true, the
|
|
957
|
+
* navigation is HELD and this hook returns `{ blocked: true, retry, reset }`
|
|
958
|
+
* so you can render a confirm prompt: `retry()` proceeds, `reset()` stays.
|
|
959
|
+
* A full-page unload (tab close / reload) additionally triggers the browser's
|
|
960
|
+
* native `beforeunload` prompt while any blocker is active.
|
|
961
|
+
*
|
|
962
|
+
* ```tsx
|
|
963
|
+
* const blocker = useBlocker(form.isDirty)
|
|
964
|
+
* // …
|
|
965
|
+
* {blocker.blocked && (
|
|
966
|
+
* <ConfirmDialog
|
|
967
|
+
* message="Discard unsaved changes?"
|
|
968
|
+
* onConfirm={blocker.retry}
|
|
969
|
+
* onCancel={blocker.reset}
|
|
970
|
+
* />
|
|
971
|
+
* )}
|
|
972
|
+
* ```
|
|
973
|
+
*
|
|
974
|
+
* Pass a boolean or a predicate. A predicate sees the pending `{ to, opts }`
|
|
975
|
+
* so you can allow some destinations (e.g. don't block leaving to `/logout`).
|
|
976
|
+
*/
|
|
977
|
+
export declare const useBlocker: (shouldBlock: boolean | BlockerFn) => BlockerState;
|
|
978
|
+
|
|
979
|
+
/** Read the active fallback strings. Returns the English defaults when no
|
|
980
|
+
* provider is mounted, so the chrome works with zero setup. A per-component
|
|
981
|
+
* `strings` prop is deep-merged on top of whatever this returns. */
|
|
982
|
+
export declare const useFallbackStrings: (override?: PartialFallbackStrings) => FallbackStrings;
|
|
983
|
+
|
|
984
|
+
/**
|
|
985
|
+
* Returns the loader data for the nearest level in the render tree: inside
|
|
986
|
+
* a `layout.tsx` it's that layout's loader result; inside the page it's
|
|
987
|
+
* the page loader's. Narrowed to the caller's type.
|
|
988
|
+
*/
|
|
989
|
+
export declare const useLoaderData: <T>() => T;
|
|
990
|
+
|
|
991
|
+
export declare const useLocation: () => string;
|
|
992
|
+
|
|
993
|
+
export declare const useNavigate: () => RouterContextValue["navigate"];
|
|
994
|
+
|
|
995
|
+
export declare const useParams: <T extends Record<string, string> = Record<string, string>>() => T;
|
|
996
|
+
|
|
997
|
+
/** Imperatively pre-warm a route's loader. No-op if the route has no loader. */
|
|
998
|
+
export declare const usePrefetch: () => ((to: string) => void);
|
|
999
|
+
|
|
1000
|
+
export declare const useReconnect: () => (() => void);
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Read the request's URL search params as a standard `URLSearchParams`.
|
|
1004
|
+
*
|
|
1005
|
+
* Server-side (SSR / ISR) it parses the query off the request URL the
|
|
1006
|
+
* SSR pipeline captured — so a `renderMode: 'ssr'` page or layout can
|
|
1007
|
+
* read `?q=…` during `renderToString` with no client round-trip and
|
|
1008
|
+
* with the value already correct in the first paint. Client-side it
|
|
1009
|
+
* reads `window.location.search`. Both sides return the native
|
|
1010
|
+
* `URLSearchParams`, so call sites are identical:
|
|
1011
|
+
*
|
|
1012
|
+
* ```tsx
|
|
1013
|
+
* const params = useSearchParams()
|
|
1014
|
+
* const q = params.get('q') ?? ''
|
|
1015
|
+
* ```
|
|
1016
|
+
*
|
|
1017
|
+
* Resolves the params for the CURRENT render. SPA pages that must react
|
|
1018
|
+
* to router-pushed query changes without a reload should re-render via
|
|
1019
|
+
* the router (e.g. `useNavigate`/`useLocation`) — this hook then
|
|
1020
|
+
* re-resolves on that render.
|
|
1021
|
+
*/
|
|
1022
|
+
export declare const useSearchParams: () => URLSearchParams;
|
|
1023
|
+
|
|
1024
|
+
export declare const useServerRequest: () => ServerRequestContextValue | null;
|
|
1025
|
+
|
|
1026
|
+
/**
|
|
1027
|
+
* The WRITE half of `useSearchParams` — returns a setter that updates the
|
|
1028
|
+
* query string on the current pathname via the router's `navigate`
|
|
1029
|
+
* (`navigate` + rebuilt `?…`), so the URL changes AND every router-context
|
|
1030
|
+
* consumer re-renders immediately (same path that `withQuery` links take).
|
|
1031
|
+
*
|
|
1032
|
+
* ```tsx
|
|
1033
|
+
* const params = useSearchParams() // read (SSR-aware)
|
|
1034
|
+
* const setParams = useSetSearchParams() // write
|
|
1035
|
+
* setParams({ tab: 'skills' }) // → ?tab=skills (replace)
|
|
1036
|
+
* setParams((p) => { p.set('page', '2'); return p }, { push: true })
|
|
1037
|
+
* ```
|
|
1038
|
+
*
|
|
1039
|
+
* Defaults to a history REPLACE (a filter tweak shouldn't stack Back entries);
|
|
1040
|
+
* pass `{ push: true }` for a distinct history entry. Client-only — during SSR
|
|
1041
|
+
* there is no history to write, so read `useSearchParams()` off the request URL
|
|
1042
|
+
* instead and mutate on the client after hydration.
|
|
1043
|
+
*/
|
|
1044
|
+
export declare const useSetSearchParams: () => ((next: SearchParamsInit, opts?: SetSearchParamsOptions) => void);
|
|
1045
|
+
|
|
1046
|
+
/** A URL minted by `externalUrl()` — anchored, hash, or cross-origin. */
|
|
1047
|
+
export declare type VoltroExternalUrl = string & {
|
|
1048
|
+
readonly [EXTERNAL_URL_BRAND]: 'external';
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
/** A URL minted by the app's generated `routes` builder. */
|
|
1052
|
+
export declare type VoltroRouteUrl = string & {
|
|
1053
|
+
readonly [ROUTE_URL_BRAND]: 'route';
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
export declare type VoltroUrl = VoltroRouteUrl | VoltroExternalUrl;
|
|
1057
|
+
|
|
1058
|
+
export declare const WEB_NAME: "framework-web";
|
|
1059
|
+
|
|
1060
|
+
/** Append a `#hash` to a route URL. Preserves the brand. */
|
|
1061
|
+
export declare const withHash: (url: VoltroRouteUrl, hash: string) => VoltroRouteUrl;
|
|
1062
|
+
|
|
1063
|
+
/**
|
|
1064
|
+
* Append a query string to a route URL. Preserves the brand so the
|
|
1065
|
+
* result still flows through `<Link to=…>`. Pass `undefined` values
|
|
1066
|
+
* to omit a param entirely.
|
|
1067
|
+
*
|
|
1068
|
+
* withQuery(routes['/_/p/[orgSlug]/[projectSlug]']({…}), { env: 'prod' })
|
|
1069
|
+
* //→ '/_/p/acme/web?env=prod' (typed as VoltroRouteUrl)
|
|
1070
|
+
*/
|
|
1071
|
+
export declare const withQuery: (url: VoltroRouteUrl, params: Readonly<Record<string, string | number | undefined>>) => VoltroRouteUrl;
|
|
1072
|
+
|
|
1073
|
+
|
|
1074
|
+
export * from "@voltro/client";
|
|
1075
|
+
export * from "@voltro/ui";
|
|
1076
|
+
|
|
1077
|
+
export { }
|