@rsc-kit/core 0.7.1 → 0.8.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/README.md +4 -4
- package/dist/action.d.ts +0 -1
- package/dist/action.js.map +1 -1
- package/dist/build-rsc-vite.d.ts +0 -1
- package/dist/build-rsc-vite.js.map +1 -1
- package/dist/cache.d.ts +0 -1
- package/dist/cache.js.map +1 -1
- package/dist/devServer.d.ts +0 -1
- package/dist/devServer.js.map +1 -1
- package/dist/devUrls.d.ts +0 -1
- package/dist/devUrls.js.map +1 -1
- package/dist/embed.d.ts +0 -1
- package/dist/embed.js.map +1 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.js.map +1 -1
- package/dist/export.d.ts +0 -1
- package/dist/export.js.map +1 -1
- package/dist/files.d.ts +0 -1
- package/dist/files.js.map +1 -1
- package/dist/headers.d.ts +20 -1
- package/dist/headers.js +20 -0
- package/dist/headers.js.map +1 -1
- package/dist/host.d.ts +11 -1
- package/dist/host.js +60 -0
- package/dist/host.js.map +1 -1
- package/dist/hostCalls.d.ts +0 -1
- package/dist/hostCalls.js.map +1 -1
- package/dist/js/ActivityRouter.d.ts +0 -1
- package/dist/js/ActivityRouter.js.map +1 -1
- package/dist/js/DocumentTitle.d.ts +0 -1
- package/dist/js/DocumentTitle.js.map +1 -1
- package/dist/js/Form.d.ts +0 -1
- package/dist/js/Form.js.map +1 -1
- package/dist/js/Link.d.ts +0 -1
- package/dist/js/Link.js.map +1 -1
- package/dist/js/PathnameProvider.d.ts +0 -1
- package/dist/js/PathnameProvider.js.map +1 -1
- package/dist/js/RedirectBoundary.d.ts +0 -1
- package/dist/js/RedirectBoundary.js.map +1 -1
- package/dist/js/SegmentBoundary.d.ts +0 -1
- package/dist/js/SegmentBoundary.js +14 -7
- package/dist/js/SegmentBoundary.js.map +1 -1
- package/dist/js/SlotBoundary.d.ts +0 -1
- package/dist/js/SlotBoundary.js.map +1 -1
- package/dist/js/createViteRscApp.d.ts +0 -1
- package/dist/js/createViteRscApp.js +21 -1
- package/dist/js/createViteRscApp.js.map +1 -1
- package/dist/js/errors.d.ts +0 -1
- package/dist/js/errors.js.map +1 -1
- package/dist/js/navigate.d.ts +2 -2
- package/dist/js/navigate.js +40 -7
- package/dist/js/navigate.js.map +1 -1
- package/dist/js/onlineStore.d.ts +0 -1
- package/dist/js/onlineStore.js.map +1 -1
- package/dist/js/pagePayload.d.ts +0 -1
- package/dist/js/pagePayload.js.map +1 -1
- package/dist/js/queryClient.d.ts +26 -0
- package/dist/js/queryClient.js +173 -0
- package/dist/js/queryClient.js.map +1 -0
- package/dist/js/router.d.ts +0 -1
- package/dist/js/router.js.map +1 -1
- package/dist/js/section.d.ts +0 -1
- package/dist/js/section.js.map +1 -1
- package/dist/js/segmentStore.d.ts +41 -2
- package/dist/js/segmentStore.js +49 -3
- package/dist/js/segmentStore.js.map +1 -1
- package/dist/js/slotStore.d.ts +0 -1
- package/dist/js/slotStore.js.map +1 -1
- package/dist/js/standardSchema.d.ts +0 -1
- package/dist/js/standardSchema.js.map +1 -1
- package/dist/js/useForm.d.ts +0 -1
- package/dist/js/useForm.js.map +1 -1
- package/dist/js/useLinkStatus.d.ts +0 -1
- package/dist/js/useLinkStatus.js.map +1 -1
- package/dist/js/useOnline.d.ts +0 -1
- package/dist/js/useOnline.js.map +1 -1
- package/dist/js/usePathname.d.ts +0 -1
- package/dist/js/usePathname.js.map +1 -1
- package/dist/js/useSearchParams.d.ts +0 -1
- package/dist/js/useSearchParams.js.map +1 -1
- package/dist/manifest.d.ts +0 -1
- package/dist/manifest.js.map +1 -1
- package/dist/nondeterminism.d.ts +0 -1
- package/dist/nondeterminism.js.map +1 -1
- package/dist/prerender.d.ts +0 -1
- package/dist/prerender.js.map +1 -1
- package/dist/query.d.ts +36 -0
- package/dist/query.js +74 -0
- package/dist/query.js.map +1 -0
- package/dist/redirect.d.ts +0 -1
- package/dist/redirect.js.map +1 -1
- package/dist/redirectDigest.d.ts +0 -1
- package/dist/redirectDigest.js.map +1 -1
- package/dist/request.d.ts +0 -1
- package/dist/request.js.map +1 -1
- package/dist/revalidate.d.ts +0 -1
- package/dist/revalidate.js.map +1 -1
- package/dist/routes.d.ts +0 -1
- package/dist/routes.js.map +1 -1
- package/dist/routing.d.ts +0 -1
- package/dist/routing.js.map +1 -1
- package/dist/runtime.d.ts +0 -1
- package/dist/runtime.js.map +1 -1
- package/dist/safeUrl.d.ts +0 -1
- package/dist/safeUrl.js.map +1 -1
- package/dist/vite.d.ts +45 -1
- package/dist/vite.js +352 -10
- package/dist/vite.js.map +1 -1
- package/package.json +14 -9
- package/dist/action.d.ts.map +0 -1
- package/dist/build-rsc-vite.d.ts.map +0 -1
- package/dist/cache.d.ts.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -84
- package/dist/cli.js.map +0 -1
- package/dist/devServer.d.ts.map +0 -1
- package/dist/devUrls.d.ts.map +0 -1
- package/dist/embed.d.ts.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/export.d.ts.map +0 -1
- package/dist/files.d.ts.map +0 -1
- package/dist/headers.d.ts.map +0 -1
- package/dist/host.d.ts.map +0 -1
- package/dist/hostCalls.d.ts.map +0 -1
- package/dist/js/ActivityRouter.d.ts.map +0 -1
- package/dist/js/DocumentTitle.d.ts.map +0 -1
- package/dist/js/Form.d.ts.map +0 -1
- package/dist/js/Link.d.ts.map +0 -1
- package/dist/js/PathnameProvider.d.ts.map +0 -1
- package/dist/js/RedirectBoundary.d.ts.map +0 -1
- package/dist/js/SegmentBoundary.d.ts.map +0 -1
- package/dist/js/SlotBoundary.d.ts.map +0 -1
- package/dist/js/createViteRscApp.d.ts.map +0 -1
- package/dist/js/errors.d.ts.map +0 -1
- package/dist/js/navigate.d.ts.map +0 -1
- package/dist/js/onlineStore.d.ts.map +0 -1
- package/dist/js/pagePayload.d.ts.map +0 -1
- package/dist/js/router.d.ts.map +0 -1
- package/dist/js/section.d.ts.map +0 -1
- package/dist/js/segmentStore.d.ts.map +0 -1
- package/dist/js/slotStore.d.ts.map +0 -1
- package/dist/js/standardSchema.d.ts.map +0 -1
- package/dist/js/useForm.d.ts.map +0 -1
- package/dist/js/useLinkStatus.d.ts.map +0 -1
- package/dist/js/useOnline.d.ts.map +0 -1
- package/dist/js/usePathname.d.ts.map +0 -1
- package/dist/js/useSearchParams.d.ts.map +0 -1
- package/dist/manifest.d.ts.map +0 -1
- package/dist/nondeterminism.d.ts.map +0 -1
- package/dist/prerender.d.ts.map +0 -1
- package/dist/redirect.d.ts.map +0 -1
- package/dist/redirectDigest.d.ts.map +0 -1
- package/dist/request.d.ts.map +0 -1
- package/dist/revalidate.d.ts.map +0 -1
- package/dist/routes.d.ts.map +0 -1
- package/dist/routing.d.ts.map +0 -1
- package/dist/runtime.d.ts.map +0 -1
- package/dist/safeUrl.d.ts.map +0 -1
- package/dist/vite.d.ts.map +0 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// The browser half of `query()`: send a read as a GET.
|
|
2
|
+
//
|
|
3
|
+
// That is the whole of it. There is no cache here, no batching and no
|
|
4
|
+
// deduplication, because TanStack Query and SWR already do those and do them
|
|
5
|
+
// better — this owns the transport and they own everything above it.
|
|
6
|
+
//
|
|
7
|
+
// The awkward fact it is built around: a server function's id is not readable
|
|
8
|
+
// from the client. React keeps it in a module-private WeakMap and exposes no
|
|
9
|
+
// getter, so there is no `ref.$$id` to build a url from. What the client *can*
|
|
10
|
+
// do is call the reference and be handed the id by React itself — the stub's
|
|
11
|
+
// whole body is `callServer(id, args)`, and this package already owns
|
|
12
|
+
// `callServer`.
|
|
13
|
+
//
|
|
14
|
+
// So a read opens a one-shot slot and calls the reference; the transport claims
|
|
15
|
+
// the slot and sends a GET instead of posting. The slot is also what says this
|
|
16
|
+
// is a read at all: calling `getListings(kind)` directly is still a POST, and
|
|
17
|
+
// `fetchQuery(getListings, [kind])` is the same call as a GET. Nothing has to
|
|
18
|
+
// know a list of query ids, on either side of the build.
|
|
19
|
+
/** Where reads are answered. Must match HEADER.queryPath on the server. */
|
|
20
|
+
const QUERY_PATH = '/_rsc/query';
|
|
21
|
+
/**
|
|
22
|
+
* Sent on every read, and required by the endpoint.
|
|
23
|
+
*
|
|
24
|
+
* Not decoration. A GET with no unusual header is a *simple* request, so any
|
|
25
|
+
* page anywhere can trigger one with `<img src="…/_rsc/query?…">` and it goes
|
|
26
|
+
* out with the visitor's cookies — CORS stops the attacker reading the answer,
|
|
27
|
+
* but the read still runs. This header is not CORS-safelisted, so the browser
|
|
28
|
+
* preflights it, and nothing here answers a preflight.
|
|
29
|
+
*
|
|
30
|
+
* That restores exactly the protection a POST had: a `POST` carrying
|
|
31
|
+
* `X-RSC-Action` is non-simple for the same reason.
|
|
32
|
+
*/
|
|
33
|
+
const QUERY_HEADER = 'X-RSC-Query';
|
|
34
|
+
/**
|
|
35
|
+
* How long a url may get before the read goes as a POST instead.
|
|
36
|
+
*
|
|
37
|
+
* Conservative. Proxies and CDNs start refusing somewhere between 8k and 16k,
|
|
38
|
+
* and the failure is a 414 from a machine that is not ours.
|
|
39
|
+
*/
|
|
40
|
+
const MAX_URL = 6_000;
|
|
41
|
+
/**
|
|
42
|
+
* The slot a read opens before calling the reference.
|
|
43
|
+
*
|
|
44
|
+
* One-shot and synchronous: React's stub calls `callServer` in its own body
|
|
45
|
+
* with nothing awaited in between, so exactly one transport call can claim it.
|
|
46
|
+
*/
|
|
47
|
+
let slot = null;
|
|
48
|
+
/**
|
|
49
|
+
* Claim the open slot, if a read opened one.
|
|
50
|
+
*
|
|
51
|
+
* Called from the app's `callServer` before it posts. Returns null when this is
|
|
52
|
+
* an ordinary action, which is every call that did not come through
|
|
53
|
+
* `fetchQuery`.
|
|
54
|
+
*/
|
|
55
|
+
export function claimRead(id, args) {
|
|
56
|
+
if (!slot || slot.claimed)
|
|
57
|
+
return null;
|
|
58
|
+
slot.claimed = true;
|
|
59
|
+
slot.promise = send(id, args);
|
|
60
|
+
return slot.promise;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Read a query, over GET.
|
|
64
|
+
*
|
|
65
|
+
* Hand this to a cache library and let it decide everything else:
|
|
66
|
+
*
|
|
67
|
+
* queryFn: () => fetchQuery(getListings, [kind])
|
|
68
|
+
* useSWR(['listings', kind], () => fetchQuery(getListings, [kind]))
|
|
69
|
+
*
|
|
70
|
+
* Every call goes to the server, which is what a fetcher needs — staleness,
|
|
71
|
+
* revalidation, retries, polling and deduplication all belong to the library
|
|
72
|
+
* holding the answer, not to the thing that fetches it.
|
|
73
|
+
*/
|
|
74
|
+
export function fetchQuery(reference, args = []) {
|
|
75
|
+
if (typeof window === 'undefined') {
|
|
76
|
+
// React's SSR runtime refuses a server-function call during the initial
|
|
77
|
+
// render, and reaching a query's id means calling its reference — so this
|
|
78
|
+
// cannot work here. Raised with the fix in it rather than left to surface
|
|
79
|
+
// as React's more general message about fetch waterfalls.
|
|
80
|
+
//
|
|
81
|
+
// A cache library runs its fetcher in an effect, so the server render never
|
|
82
|
+
// reaches this; a server component that wants the data during render should
|
|
83
|
+
// await the query directly, which needs none of this.
|
|
84
|
+
throw new Error('A query was read during server rendering. Read it in a server component with await, or through a cache library, whose fetcher runs after hydration.');
|
|
85
|
+
}
|
|
86
|
+
const opened = { claimed: false, promise: null };
|
|
87
|
+
slot = opened;
|
|
88
|
+
try {
|
|
89
|
+
// React's stub reaches callServer synchronously, so the slot is claimed by
|
|
90
|
+
// the time this returns. The stub's own promise is discarded: the one the
|
|
91
|
+
// transport made is the one that settles with the answer.
|
|
92
|
+
;
|
|
93
|
+
reference(...args);
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
// Cleared here and nowhere else, so a reference that throws on the way in
|
|
97
|
+
// does not leave the slot open for the next read to claim by accident.
|
|
98
|
+
// Nothing is returned from this block: a `return` in `finally` discards
|
|
99
|
+
// whatever the `try` was throwing, which would turn a reference that threw
|
|
100
|
+
// synchronously into the misleading bound-reference error below.
|
|
101
|
+
slot = null;
|
|
102
|
+
}
|
|
103
|
+
if (!opened.claimed || !opened.promise) {
|
|
104
|
+
// The only way here is a reference whose call path is asynchronous, which
|
|
105
|
+
// today means one that was `.bind()`-ed. Loud, because the quiet version is
|
|
106
|
+
// a read that silently went out as a POST.
|
|
107
|
+
throw new Error('A query was called through a bound reference. Pass the exported query itself, unbound.');
|
|
108
|
+
}
|
|
109
|
+
return opened.promise;
|
|
110
|
+
}
|
|
111
|
+
async function send(id, args) {
|
|
112
|
+
const encoded = await encode(args);
|
|
113
|
+
// encodeReply answers with FormData the moment an argument holds a File, and
|
|
114
|
+
// a url cannot carry one. Rather than refuse a call that would have worked as
|
|
115
|
+
// an action, the read falls back to a POST: it stops being cacheable, which
|
|
116
|
+
// is the only thing it loses.
|
|
117
|
+
if (typeof encoded !== 'string')
|
|
118
|
+
return await post(id, args, 'an argument contained a File');
|
|
119
|
+
const url = `${QUERY_PATH}?id=${encodeURIComponent(id)}&args=${encodeURIComponent(encoded)}`;
|
|
120
|
+
if (url.length > MAX_URL)
|
|
121
|
+
return await post(id, args, 'the arguments are too large for a url');
|
|
122
|
+
const res = await fetch(url, {
|
|
123
|
+
headers: {
|
|
124
|
+
[QUERY_HEADER]: '1',
|
|
125
|
+
Accept: 'text/x-component',
|
|
126
|
+
// Where the read came from. Same reason an action sends it: a host that
|
|
127
|
+
// guards by route needs to know which page is asking.
|
|
128
|
+
'X-RSC-Referer': window.location.pathname + window.location.search,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
if (!res.ok || !res.body) {
|
|
132
|
+
throw new Error((await res.text().catch(() => '')) || `Query failed: ${res.status}`);
|
|
133
|
+
}
|
|
134
|
+
return await deserialize(res.body);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The same read, as an action.
|
|
138
|
+
*
|
|
139
|
+
* Reached only when the arguments cannot ride in a url. Warned about in
|
|
140
|
+
* development rather than silently tolerated, because the read still works and
|
|
141
|
+
* the thing that changed — it is no longer a GET, so nothing can cache it — is
|
|
142
|
+
* otherwise invisible.
|
|
143
|
+
*/
|
|
144
|
+
async function post(id, args, why) {
|
|
145
|
+
if (import.meta.env?.DEV) {
|
|
146
|
+
console.warn(`[rsc-kit] A query was sent as a POST because ${why}. It will not be cached.`);
|
|
147
|
+
}
|
|
148
|
+
return await asAction(id, args);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The Flight codec and the action transport, installed by the app bootstrap.
|
|
152
|
+
*
|
|
153
|
+
* Injected rather than imported, and not for testing: this module is reached
|
|
154
|
+
* from client components, so importing the browser runtime here would pull a
|
|
155
|
+
* second copy of it into that graph — two client-reference registries, where
|
|
156
|
+
* components resolve to undefined with nothing logged. The bootstrap already
|
|
157
|
+
* holds the one true copy.
|
|
158
|
+
*/
|
|
159
|
+
let deserialize = () => {
|
|
160
|
+
throw new Error('No Flight decoder installed. createViteRscApp() sets one up.');
|
|
161
|
+
};
|
|
162
|
+
let encode = () => {
|
|
163
|
+
throw new Error('No Flight encoder installed. createViteRscApp() sets one up.');
|
|
164
|
+
};
|
|
165
|
+
let asAction = () => {
|
|
166
|
+
throw new Error('No action transport installed. createViteRscApp() sets one up.');
|
|
167
|
+
};
|
|
168
|
+
export function setQueryCodec(codec) {
|
|
169
|
+
deserialize = codec.deserialize;
|
|
170
|
+
encode = codec.encode;
|
|
171
|
+
asAction = codec.asAction;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=queryClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryClient.js","sourceRoot":"","sources":["../../src/js/queryClient.ts"],"names":[],"mappings":"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","sourcesContent":["// The browser half of `query()`: send a read as a GET.\n//\n// That is the whole of it. There is no cache here, no batching and no\n// deduplication, because TanStack Query and SWR already do those and do them\n// better — this owns the transport and they own everything above it.\n//\n// The awkward fact it is built around: a server function's id is not readable\n// from the client. React keeps it in a module-private WeakMap and exposes no\n// getter, so there is no `ref.$$id` to build a url from. What the client *can*\n// do is call the reference and be handed the id by React itself — the stub's\n// whole body is `callServer(id, args)`, and this package already owns\n// `callServer`.\n//\n// So a read opens a one-shot slot and calls the reference; the transport claims\n// the slot and sends a GET instead of posting. The slot is also what says this\n// is a read at all: calling `getListings(kind)` directly is still a POST, and\n// `fetchQuery(getListings, [kind])` is the same call as a GET. Nothing has to\n// know a list of query ids, on either side of the build.\n\n/** Where reads are answered. Must match HEADER.queryPath on the server. */\nconst QUERY_PATH = '/_rsc/query'\n\n/**\n * Sent on every read, and required by the endpoint.\n *\n * Not decoration. A GET with no unusual header is a *simple* request, so any\n * page anywhere can trigger one with `<img src=\"…/_rsc/query?…\">` and it goes\n * out with the visitor's cookies — CORS stops the attacker reading the answer,\n * but the read still runs. This header is not CORS-safelisted, so the browser\n * preflights it, and nothing here answers a preflight.\n *\n * That restores exactly the protection a POST had: a `POST` carrying\n * `X-RSC-Action` is non-simple for the same reason.\n */\nconst QUERY_HEADER = 'X-RSC-Query'\n\n/**\n * How long a url may get before the read goes as a POST instead.\n *\n * Conservative. Proxies and CDNs start refusing somewhere between 8k and 16k,\n * and the failure is a 414 from a machine that is not ours.\n */\nconst MAX_URL = 6_000\n\ntype Reader = (...args: unknown[]) => Promise<unknown>\n\n/**\n * The slot a read opens before calling the reference.\n *\n * One-shot and synchronous: React's stub calls `callServer` in its own body\n * with nothing awaited in between, so exactly one transport call can claim it.\n */\nlet slot: { claimed: boolean; promise: Promise<unknown> | null } | null = null\n\n/**\n * Claim the open slot, if a read opened one.\n *\n * Called from the app's `callServer` before it posts. Returns null when this is\n * an ordinary action, which is every call that did not come through\n * `fetchQuery`.\n */\nexport function claimRead(id: string, args: unknown[]): Promise<unknown> | null {\n if (!slot || slot.claimed) return null\n\n slot.claimed = true\n slot.promise = send(id, args)\n\n return slot.promise\n}\n\n/**\n * Read a query, over GET.\n *\n * Hand this to a cache library and let it decide everything else:\n *\n * queryFn: () => fetchQuery(getListings, [kind])\n * useSWR(['listings', kind], () => fetchQuery(getListings, [kind]))\n *\n * Every call goes to the server, which is what a fetcher needs — staleness,\n * revalidation, retries, polling and deduplication all belong to the library\n * holding the answer, not to the thing that fetches it.\n */\nexport function fetchQuery<Data>(\n reference: (...args: never[]) => Promise<Data>,\n args: unknown[] = [],\n): Promise<Data> {\n if (typeof window === 'undefined') {\n // React's SSR runtime refuses a server-function call during the initial\n // render, and reaching a query's id means calling its reference — so this\n // cannot work here. Raised with the fix in it rather than left to surface\n // as React's more general message about fetch waterfalls.\n //\n // A cache library runs its fetcher in an effect, so the server render never\n // reaches this; a server component that wants the data during render should\n // await the query directly, which needs none of this.\n throw new Error(\n 'A query was read during server rendering. Read it in a server component with await, or through a cache library, whose fetcher runs after hydration.',\n )\n }\n\n const opened = { claimed: false, promise: null as Promise<unknown> | null }\n\n slot = opened\n\n try {\n // React's stub reaches callServer synchronously, so the slot is claimed by\n // the time this returns. The stub's own promise is discarded: the one the\n // transport made is the one that settles with the answer.\n ;(reference as unknown as Reader)(...(args as unknown[]))\n } finally {\n // Cleared here and nowhere else, so a reference that throws on the way in\n // does not leave the slot open for the next read to claim by accident.\n // Nothing is returned from this block: a `return` in `finally` discards\n // whatever the `try` was throwing, which would turn a reference that threw\n // synchronously into the misleading bound-reference error below.\n slot = null\n }\n\n if (!opened.claimed || !opened.promise) {\n // The only way here is a reference whose call path is asynchronous, which\n // today means one that was `.bind()`-ed. Loud, because the quiet version is\n // a read that silently went out as a POST.\n throw new Error(\n 'A query was called through a bound reference. Pass the exported query itself, unbound.',\n )\n }\n\n return opened.promise as Promise<Data>\n}\n\nasync function send(id: string, args: unknown[]): Promise<unknown> {\n const encoded = await encode(args)\n\n // encodeReply answers with FormData the moment an argument holds a File, and\n // a url cannot carry one. Rather than refuse a call that would have worked as\n // an action, the read falls back to a POST: it stops being cacheable, which\n // is the only thing it loses.\n if (typeof encoded !== 'string') return await post(id, args, 'an argument contained a File')\n\n const url = `${QUERY_PATH}?id=${encodeURIComponent(id)}&args=${encodeURIComponent(encoded)}`\n\n if (url.length > MAX_URL) return await post(id, args, 'the arguments are too large for a url')\n\n const res = await fetch(url, {\n headers: {\n [QUERY_HEADER]: '1',\n Accept: 'text/x-component',\n // Where the read came from. Same reason an action sends it: a host that\n // guards by route needs to know which page is asking.\n 'X-RSC-Referer': window.location.pathname + window.location.search,\n },\n })\n\n if (!res.ok || !res.body) {\n throw new Error((await res.text().catch(() => '')) || `Query failed: ${res.status}`)\n }\n\n return await deserialize(res.body)\n}\n\n/**\n * The same read, as an action.\n *\n * Reached only when the arguments cannot ride in a url. Warned about in\n * development rather than silently tolerated, because the read still works and\n * the thing that changed — it is no longer a GET, so nothing can cache it — is\n * otherwise invisible.\n */\nasync function post(id: string, args: unknown[], why: string): Promise<unknown> {\n if (import.meta.env?.DEV) {\n console.warn(`[rsc-kit] A query was sent as a POST because ${why}. It will not be cached.`)\n }\n\n return await asAction(id, args)\n}\n\n/**\n * The Flight codec and the action transport, installed by the app bootstrap.\n *\n * Injected rather than imported, and not for testing: this module is reached\n * from client components, so importing the browser runtime here would pull a\n * second copy of it into that graph — two client-reference registries, where\n * components resolve to undefined with nothing logged. The bootstrap already\n * holds the one true copy.\n */\nlet deserialize: (stream: ReadableStream) => Promise<unknown> = () => {\n throw new Error('No Flight decoder installed. createViteRscApp() sets one up.')\n}\n\nlet encode: (args: unknown[]) => Promise<string | FormData> = () => {\n throw new Error('No Flight encoder installed. createViteRscApp() sets one up.')\n}\n\nlet asAction: (id: string, args: unknown[]) => Promise<unknown> = () => {\n throw new Error('No action transport installed. createViteRscApp() sets one up.')\n}\n\nexport function setQueryCodec(codec: {\n deserialize: (stream: ReadableStream) => Promise<unknown>\n encode: (args: unknown[]) => Promise<string | FormData>\n asAction: (id: string, args: unknown[]) => Promise<unknown>\n}): void {\n deserialize = codec.deserialize\n encode = codec.encode\n asAction = codec.asAction\n}\n"]}
|
package/dist/js/router.d.ts
CHANGED
package/dist/js/router.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/js/router.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,UAAU,KAAK,CACnB,GAAS,EACT,IAA4B;IAE5B,MAAM,GAAG,GAAI,MAAc,CAAC,cAAc,CAAC;IAE3C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,CAAC;IAED,OAAO,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAS,EAAE,UAAmB;IACrD,MAAM,EAAE,GAAI,MAAc,CAAC,cAAc,CAAC;IAE1C,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,CAAC;IAED,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAC,MAAe;IACrC,MAAM,EAAE,GAAI,MAAc,CAAC,aAAa,CAAC;IAEzC,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,CAAC;IAED,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC5C,eAAe,MAAM,CAAC"}
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/js/router.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,UAAU,KAAK,CACnB,GAAS,EACT,IAA4B;IAE5B,MAAM,GAAG,GAAI,MAAc,CAAC,cAAc,CAAC;IAE3C,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,CAAC;IAED,OAAO,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAS,EAAE,UAAmB;IACrD,MAAM,EAAE,GAAI,MAAc,CAAC,cAAc,CAAC;IAE1C,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,CAAC;IAED,EAAE,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO,CAAC,MAAe;IACrC,MAAM,EAAE,GAAI,MAAc,CAAC,aAAa,CAAC;IAEzC,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;IAC9F,CAAC;IAED,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;AACpB,CAAC;AAED,MAAM,MAAM,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;AAC5C,eAAe,MAAM,CAAC","sourcesContent":["/**\n * Programmatic navigation API for use from client components.\n *\n * Reads from window globals set by createViteRscApp, since client components\n * are built in a separate build graph and cannot directly import navigate.ts.\n */\n\nimport type { Href } from \"../routes.js\";\n\nexport function visit(\n url: Href,\n opts?: { replace?: boolean }\n): Promise<void> {\n const nav = (window as any).__rsc_navigate;\n\n if (!nav) {\n throw new Error(\"RSC navigation not initialized. Ensure createViteRscApp has been called.\");\n }\n\n return nav(url, opts);\n}\n\nexport function prefetch(url: Href, cacheForMs?: number): void {\n const fn = (window as any).__rsc_prefetch;\n\n if (!fn) {\n throw new Error(\"RSC navigation not initialized. Ensure createViteRscApp has been called.\");\n }\n\n fn(url, cacheForMs);\n}\n\n/**\n * Ask the server for part of the current page again.\n *\n * refresh() the page, leaving the layouts mounted\n * refresh('all') the whole document\n * refresh('orders') one parallel slot\n *\n * The page form leaves the layouts alone, so anything living in one — a count\n * in a sidebar, say — will not move until you ask for 'all'.\n */\nexport function refresh(target?: string): Promise<void> {\n const fn = (window as any).__rsc_refresh;\n\n if (!fn) {\n throw new Error(\"RSC navigation not initialized. Ensure createViteRscApp has been called.\");\n }\n\n return fn(target);\n}\n\nconst router = { visit, prefetch, refresh };\nexport default router;\n"]}
|
package/dist/js/section.d.ts
CHANGED
|
@@ -13,4 +13,3 @@ export declare function section<P extends Record<string, unknown>>(name: string,
|
|
|
13
13
|
* any url, bounded only by whatever guard happened to sit on the url asked for.
|
|
14
14
|
*/
|
|
15
15
|
export declare function sectionComponent(exported: unknown): ComponentType<Record<string, unknown>> | undefined;
|
|
16
|
-
//# sourceMappingURL=section.d.ts.map
|
package/dist/js/section.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"section.js","sourceRoot":"","sources":["../../src/js/section.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,6EAA6E;AAC7E,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AAE5D,MAAM,UAAU,OAAO,CACrB,IAAY,EACZ,SAA2B;IAE3B,2EAA2E;IAC3E,qDAAqD;IACrD,SAAS,OAAO,CAAC,KAAQ;QACvB,sEAAsE;QACtE,oDAAoD;QACpD,OAAO,aAAa,CAClB,YAA+C,EAC/C,EAAE,IAAI,EAAE,EACR,aAAa,CAAC,SAAiC,EAAE,KAAc,CAAC,CACjE,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,WAAW,GAAG,WAAW,IAAI,GAAG,CAAC;IACzC,sEAAsE;IACrE,OAA8C,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IAEnE,OAAO,OAA2B,CAAC;AACrC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAiB;IAEjB,IAAI,OAAO,QAAQ,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IAErD,OAAQ,QAA+C,CAAC,KAAK,CAEhD,CAAC;AAChB,CAAC"}
|
|
1
|
+
{"version":3,"file":"section.js","sourceRoot":"","sources":["../../src/js/section.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,6EAA6E;AAC7E,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AAE5D,MAAM,UAAU,OAAO,CACrB,IAAY,EACZ,SAA2B;IAE3B,2EAA2E;IAC3E,qDAAqD;IACrD,SAAS,OAAO,CAAC,KAAQ;QACvB,sEAAsE;QACtE,oDAAoD;QACpD,OAAO,aAAa,CAClB,YAA+C,EAC/C,EAAE,IAAI,EAAE,EACR,aAAa,CAAC,SAAiC,EAAE,KAAc,CAAC,CACjE,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,WAAW,GAAG,WAAW,IAAI,GAAG,CAAC;IACzC,sEAAsE;IACrE,OAA8C,CAAC,KAAK,CAAC,GAAG,SAAS,CAAC;IAEnE,OAAO,OAA2B,CAAC;AACrC,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAAiB;IAEjB,IAAI,OAAO,QAAQ,KAAK,UAAU;QAAE,OAAO,SAAS,CAAC;IAErD,OAAQ,QAA+C,CAAC,KAAK,CAEhD,CAAC;AAChB,CAAC","sourcesContent":["import { createElement } from \"react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { SlotBoundary } from \"./SlotBoundary\";\n\n/**\n * Mark a region of a page as separately refreshable.\n *\n * Deliberately not a client module. The boundary it wraps things in is one,\n * but the things it wraps are server components — an async component that\n * fetches its own data. Marking this file \"use client\" would turn every one of\n * them into a client reference, which async components cannot be.\n *\n * export default section('orders', async function Orders() { ... })\n *\n * The page renders it like any component. What it buys is a name the server\n * can address: `Rsc::revalidate('orders')` re-renders this and nothing else,\n * and the answer replaces it in place.\n *\n * The name has to be unique within the page. It does not have to be unique in\n * the app: the component is carried on the wrapper it returns, so the server\n * resolves a section through the module the route declares rather than by\n * looking the name up in a registry every section in the app also writes to.\n * Two pages may both call theirs 'stats'.\n */\n\n/** Where the unwrapped component hangs off the wrapper section() returns. */\nconst INNER = Symbol.for('@rsc-kit/core.section-component');\n\nexport function section<P extends Record<string, unknown>>(\n name: string,\n Component: ComponentType<P>,\n): ComponentType<P> {\n // The boundary wraps the component here rather than at the call site, so a\n // page renders a section exactly like anything else.\n function Section(props: P): ReactNode {\n // createElement cannot line its overloads up with a component generic\n // over its own props. The call is the ordinary one.\n return createElement(\n SlotBoundary as ComponentType<{ name: string }>,\n { name },\n createElement(Component as ComponentType<never>, props as never),\n );\n }\n\n Section.displayName = `Section(${name})`;\n // Reached through the route's own module, never through a shared map.\n (Section as unknown as Record<symbol, unknown>)[INNER] = Component;\n\n return Section as ComponentType<P>;\n}\n\n/**\n * The component inside a section module, without its boundary.\n *\n * Revalidation renders this rather than the wrapper: the client swaps what is\n * inside the boundary, so returning the wrapper would nest a new boundary\n * inside the old one on every refresh.\n *\n * Takes the module's own export rather than a name. A name-keyed registry is\n * written to by every section in the app at bundle load — the generated entry\n * imports them all eagerly — so a lookup by name reached any page's region from\n * any url, bounded only by whatever guard happened to sit on the url asked for.\n */\nexport function sectionComponent(\n exported: unknown,\n): ComponentType<Record<string, unknown>> | undefined {\n if (typeof exported !== 'function') return undefined;\n\n return (exported as unknown as Record<symbol, unknown>)[INNER] as\n | ComponentType<Record<string, unknown>>\n | undefined;\n}\n"]}
|
|
@@ -9,12 +9,42 @@
|
|
|
9
9
|
* Entries are keyed by page (the URL, or its intercept variant), so a boundary
|
|
10
10
|
* can hold several and reveal one. Empty is meaningful: a boundary with nothing
|
|
11
11
|
* stored renders the children the server gave it.
|
|
12
|
+
*
|
|
13
|
+
* A store rather than state, and not a preference — three things rule state out.
|
|
14
|
+
* A boundary is inserted between every layout level, so there are several and a
|
|
15
|
+
* navigation targets one by depth; they are separated by server components, so
|
|
16
|
+
* no setter can be threaded down to them, because a function does not cross
|
|
17
|
+
* that boundary; and navigate.ts is a plain module with no component instance
|
|
18
|
+
* to call one on. Addressing a component you hold no reference to is what an
|
|
19
|
+
* external store is for.
|
|
20
|
+
*
|
|
21
|
+
* Underneath it is the wire protocol. A partial navigation sends only the
|
|
22
|
+
* segment that changed — see the X-RSC-Segments headers — so there is nothing
|
|
23
|
+
* for a root to re-render with even if it held the state. Next.js keeps its
|
|
24
|
+
* router in useState and derives every segment from it; that is the same trade
|
|
25
|
+
* in the other direction.
|
|
26
|
+
*
|
|
27
|
+
* What it costs: React pins an external store's updates to synchronous
|
|
28
|
+
* priority, because a store cannot be safely time-sliced. Synchronous is never
|
|
29
|
+
* a transition, and anything that only runs for one — React's <ViewTransition>
|
|
30
|
+
* among them — never ran for a navigation.
|
|
31
|
+
*
|
|
32
|
+
* Which is why SegmentBoundary does not read this with useSyncExternalStore
|
|
33
|
+
* any more. The store still does the addressing, which is the part only it can
|
|
34
|
+
* do; the boundary copies into state, so the render is a transition. See the
|
|
35
|
+
* view transitions guide.
|
|
12
36
|
*/
|
|
13
37
|
type Tree = unknown;
|
|
14
38
|
type Listener = () => void;
|
|
15
39
|
interface Entry {
|
|
16
40
|
key: string;
|
|
17
41
|
tree: Tree;
|
|
42
|
+
/**
|
|
43
|
+
* When this tree arrived, so a link can decide whether it is still worth
|
|
44
|
+
* revealing. The back button never asks — it means "the page I was on",
|
|
45
|
+
* however long ago that was.
|
|
46
|
+
*/
|
|
47
|
+
at: number;
|
|
18
48
|
}
|
|
19
49
|
/**
|
|
20
50
|
* Immutable: useSyncExternalStore compares snapshots by identity, so a new
|
|
@@ -57,7 +87,17 @@ export declare function seedSegment(depth: number, key: string, tree: Tree): voi
|
|
|
57
87
|
* deeper boundary, so they delegate to whatever it is showing. One that does
|
|
58
88
|
* hold the key is switched to it, since that is a real change at its level.
|
|
59
89
|
*/
|
|
60
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Reveal a page still being held, if it is worth revealing.
|
|
92
|
+
*
|
|
93
|
+
* `maxAge` is what a link passes and the back button does not. Going back is
|
|
94
|
+
* unambiguous — it names a moment, and the page from that moment is the right
|
|
95
|
+
* answer however old. A link says "go here", and answering it with a tree from
|
|
96
|
+
* twenty minutes ago is stale data presented as fresh, which is the objection
|
|
97
|
+
* this design started with. Recent enough, and it is the same page you were
|
|
98
|
+
* just on, with the form you were filling in still filled in.
|
|
99
|
+
*/
|
|
100
|
+
export declare function restoreSegments(key: string, maxAge?: number): boolean;
|
|
61
101
|
/**
|
|
62
102
|
* Drop everything, so boundaries fall back to their server-given children.
|
|
63
103
|
*
|
|
@@ -66,4 +106,3 @@ export declare function restoreSegments(key: string): boolean;
|
|
|
66
106
|
*/
|
|
67
107
|
export declare function clearSegments(): void;
|
|
68
108
|
export {};
|
|
69
|
-
//# sourceMappingURL=segmentStore.d.ts.map
|
package/dist/js/segmentStore.js
CHANGED
|
@@ -9,6 +9,30 @@
|
|
|
9
9
|
* Entries are keyed by page (the URL, or its intercept variant), so a boundary
|
|
10
10
|
* can hold several and reveal one. Empty is meaningful: a boundary with nothing
|
|
11
11
|
* stored renders the children the server gave it.
|
|
12
|
+
*
|
|
13
|
+
* A store rather than state, and not a preference — three things rule state out.
|
|
14
|
+
* A boundary is inserted between every layout level, so there are several and a
|
|
15
|
+
* navigation targets one by depth; they are separated by server components, so
|
|
16
|
+
* no setter can be threaded down to them, because a function does not cross
|
|
17
|
+
* that boundary; and navigate.ts is a plain module with no component instance
|
|
18
|
+
* to call one on. Addressing a component you hold no reference to is what an
|
|
19
|
+
* external store is for.
|
|
20
|
+
*
|
|
21
|
+
* Underneath it is the wire protocol. A partial navigation sends only the
|
|
22
|
+
* segment that changed — see the X-RSC-Segments headers — so there is nothing
|
|
23
|
+
* for a root to re-render with even if it held the state. Next.js keeps its
|
|
24
|
+
* router in useState and derives every segment from it; that is the same trade
|
|
25
|
+
* in the other direction.
|
|
26
|
+
*
|
|
27
|
+
* What it costs: React pins an external store's updates to synchronous
|
|
28
|
+
* priority, because a store cannot be safely time-sliced. Synchronous is never
|
|
29
|
+
* a transition, and anything that only runs for one — React's <ViewTransition>
|
|
30
|
+
* among them — never ran for a navigation.
|
|
31
|
+
*
|
|
32
|
+
* Which is why SegmentBoundary does not read this with useSyncExternalStore
|
|
33
|
+
* any more. The store still does the addressing, which is the part only it can
|
|
34
|
+
* do; the boundary copies into state, so the render is a transition. See the
|
|
35
|
+
* view transitions guide.
|
|
12
36
|
*/
|
|
13
37
|
/** Pages kept alive per boundary. Four covers ordinary back-and-forth. */
|
|
14
38
|
export const RETENTION = 4;
|
|
@@ -46,7 +70,10 @@ function retain(entries, order, activeKey) {
|
|
|
46
70
|
}
|
|
47
71
|
function put(depth, key, tree) {
|
|
48
72
|
const state = depths.get(depth);
|
|
49
|
-
const entries = [
|
|
73
|
+
const entries = [
|
|
74
|
+
...(state?.entries ?? []).filter((entry) => entry.key !== key),
|
|
75
|
+
{ key, tree, at: Date.now() },
|
|
76
|
+
];
|
|
50
77
|
const order = [...(state?.order ?? []).filter((k) => k !== key), key];
|
|
51
78
|
depths.set(depth, retain(entries, order, key));
|
|
52
79
|
}
|
|
@@ -80,7 +107,7 @@ export function seedSegment(depth, key, tree) {
|
|
|
80
107
|
}
|
|
81
108
|
// Older than whatever is showing, so it goes to the front of the eviction
|
|
82
109
|
// order — and crucially does not become the active page.
|
|
83
|
-
depths.set(depth, retain([...state.entries, { key, tree }], [key, ...state.order.filter((k) => k !== key)], state.activeKey));
|
|
110
|
+
depths.set(depth, retain([...state.entries, { key, tree, at: Date.now() }], [key, ...state.order.filter((k) => k !== key)], state.activeKey));
|
|
84
111
|
notify(depth);
|
|
85
112
|
}
|
|
86
113
|
/**
|
|
@@ -96,10 +123,29 @@ export function seedSegment(depth, key, tree) {
|
|
|
96
123
|
* deeper boundary, so they delegate to whatever it is showing. One that does
|
|
97
124
|
* hold the key is switched to it, since that is a real change at its level.
|
|
98
125
|
*/
|
|
99
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Reveal a page still being held, if it is worth revealing.
|
|
128
|
+
*
|
|
129
|
+
* `maxAge` is what a link passes and the back button does not. Going back is
|
|
130
|
+
* unambiguous — it names a moment, and the page from that moment is the right
|
|
131
|
+
* answer however old. A link says "go here", and answering it with a tree from
|
|
132
|
+
* twenty minutes ago is stale data presented as fresh, which is the objection
|
|
133
|
+
* this design started with. Recent enough, and it is the same page you were
|
|
134
|
+
* just on, with the form you were filling in still filled in.
|
|
135
|
+
*/
|
|
136
|
+
export function restoreSegments(key, maxAge) {
|
|
100
137
|
const holding = [...depths.keys()].filter((d) => depths.get(d).entries.some((entry) => entry.key === key));
|
|
101
138
|
if (holding.length === 0)
|
|
102
139
|
return false;
|
|
140
|
+
if (maxAge !== undefined) {
|
|
141
|
+
const ages = holding.flatMap((d) => depths.get(d).entries.filter((entry) => entry.key === key).map((entry) => entry.at));
|
|
142
|
+
// The oldest layer decides: revealing a fresh page under a stale layout
|
|
143
|
+
// would be a chain nobody rendered together.
|
|
144
|
+
// >= rather than >, so a window of 0 means never rather than "only within
|
|
145
|
+
// the same millisecond".
|
|
146
|
+
if (Date.now() - Math.min(...ages) >= maxAge)
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
103
149
|
const anchor = Math.max(...holding);
|
|
104
150
|
for (const d of [...depths.keys()].filter((d) => d > anchor)) {
|
|
105
151
|
depths.delete(d);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"segmentStore.js","sourceRoot":"","sources":["../../src/js/segmentStore.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"segmentStore.js","sourceRoot":"","sources":["../../src/js/segmentStore.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * What each segment boundary is showing, and what it is keeping alive behind it.\n *\n * A navigation replaces one segment. Keeping the previous one mounted — hidden,\n * not unmounted — is what lets going back restore it with its client state:\n * the half-typed form, the open disclosure, the scrolled list. Unmounting\n * throws all of that away, which is what replacing the root used to do.\n *\n * Entries are keyed by page (the URL, or its intercept variant), so a boundary\n * can hold several and reveal one. Empty is meaningful: a boundary with nothing\n * stored renders the children the server gave it.\n *\n * A store rather than state, and not a preference — three things rule state out.\n * A boundary is inserted between every layout level, so there are several and a\n * navigation targets one by depth; they are separated by server components, so\n * no setter can be threaded down to them, because a function does not cross\n * that boundary; and navigate.ts is a plain module with no component instance\n * to call one on. Addressing a component you hold no reference to is what an\n * external store is for.\n *\n * Underneath it is the wire protocol. A partial navigation sends only the\n * segment that changed — see the X-RSC-Segments headers — so there is nothing\n * for a root to re-render with even if it held the state. Next.js keeps its\n * router in useState and derives every segment from it; that is the same trade\n * in the other direction.\n *\n * What it costs: React pins an external store's updates to synchronous\n * priority, because a store cannot be safely time-sliced. Synchronous is never\n * a transition, and anything that only runs for one — React's <ViewTransition>\n * among them — never ran for a navigation.\n *\n * Which is why SegmentBoundary does not read this with useSyncExternalStore\n * any more. The store still does the addressing, which is the part only it can\n * do; the boundary copies into state, so the render is a transition. See the\n * view transitions guide.\n */\n\ntype Tree = unknown\ntype Listener = () => void\n\ninterface Entry {\n key: string\n tree: Tree\n /**\n * When this tree arrived, so a link can decide whether it is still worth\n * revealing. The back button never asks — it means \"the page I was on\",\n * however long ago that was.\n */\n at: number\n}\n\n/**\n * Immutable: useSyncExternalStore compares snapshots by identity, so a new\n * object per read reads as \"changed every render\" and loops forever. Every\n * mutation replaces this wholesale; nothing edits one in place.\n */\ninterface DepthState {\n readonly entries: readonly Entry[]\n readonly activeKey: string\n /** Most recently shown last; eviction takes from the front. */\n readonly order: readonly string[]\n}\n\n/** Pages kept alive per boundary. Four covers ordinary back-and-forth. */\nexport const RETENTION = 4\n\nconst depths = new Map<number, DepthState>()\nconst listeners = new Map<number, Set<Listener>>()\n\nfunction notify(depth: number): void {\n for (const listener of listeners.get(depth) ?? []) listener()\n}\n\nexport function subscribeToSegment(depth: number, listener: Listener): () => void {\n let set = listeners.get(depth)\n\n if (!set) {\n set = new Set()\n listeners.set(depth, set)\n }\n\n set.add(listener)\n\n return () => {\n set!.delete(listener)\n if (set!.size === 0) listeners.delete(depth)\n }\n}\n\n/** Everything a boundary at this depth needs to render, or null for \"use children\". */\nexport function getSegmentState(depth: number): DepthState | null {\n return depths.get(depth) ?? null\n}\n\n/** Apply the retention window to a candidate state. */\nfunction retain(entries: readonly Entry[], order: readonly string[], activeKey: string): DepthState {\n const kept = order.slice(-RETENTION)\n\n return {\n entries: entries.filter((entry) => kept.includes(entry.key)),\n order: kept,\n activeKey,\n }\n}\n\nfunction put(depth: number, key: string, tree: Tree): void {\n const state = depths.get(depth)\n const entries = [\n ...(state?.entries ?? []).filter((entry) => entry.key !== key),\n { key, tree, at: Date.now() },\n ]\n const order = [...(state?.order ?? []).filter((k) => k !== key), key]\n\n depths.set(depth, retain(entries, order, key))\n}\n\n/**\n * Show `tree` at `depth` for `key`, retaining what was there.\n *\n * Deeper segments belonged to the page being replaced; leaving them would\n * render the previous page inside the new one.\n */\nexport function setSegment(depth: number, key: string, tree: Tree): void {\n put(depth, key, tree)\n\n const stale = [...depths.keys()].filter((d) => d > depth)\n for (const d of stale) depths.delete(d)\n\n notify(depth)\n for (const d of stale) notify(d)\n}\n\n/**\n * Record the children the server rendered, so the page you arrived on can be\n * returned to later. Never changes what is showing.\n */\nexport function seedSegment(depth: number, key: string, tree: Tree): void {\n const state = depths.get(depth)\n\n if (state?.entries.some((entry) => entry.key === key)) return\n\n if (!state) {\n put(depth, key, tree)\n notify(depth)\n\n return\n }\n\n\n // Older than whatever is showing, so it goes to the front of the eviction\n // order — and crucially does not become the active page.\n depths.set(\n depth,\n retain(\n [...state.entries, { key, tree, at: Date.now() }],\n [key, ...state.order.filter((k) => k !== key)],\n state.activeKey,\n ),\n )\n\n notify(depth)\n}\n\n/**\n * Reveal a page the boundaries are still holding, without asking the server.\n *\n * Restoring is anchored on the deepest boundary that can show the page. Deeper\n * ones than that belonged to the page being left — a section with its own\n * layout adds a boundary the page you are going back to never had — so they\n * are dropped, exactly as setSegment drops them. Requiring every boundary to\n * hold the key instead made any such page refuse to restore.\n *\n * Shallower boundaries need no key of their own: their trees contain the\n * deeper boundary, so they delegate to whatever it is showing. One that does\n * hold the key is switched to it, since that is a real change at its level.\n */\n/**\n * Reveal a page still being held, if it is worth revealing.\n *\n * `maxAge` is what a link passes and the back button does not. Going back is\n * unambiguous — it names a moment, and the page from that moment is the right\n * answer however old. A link says \"go here\", and answering it with a tree from\n * twenty minutes ago is stale data presented as fresh, which is the objection\n * this design started with. Recent enough, and it is the same page you were\n * just on, with the form you were filling in still filled in.\n */\nexport function restoreSegments(key: string, maxAge?: number): boolean {\n const holding = [...depths.keys()].filter((d) =>\n depths.get(d)!.entries.some((entry) => entry.key === key),\n )\n\n if (holding.length === 0) return false\n\n if (maxAge !== undefined) {\n const ages = holding.flatMap((d) =>\n depths.get(d)!.entries.filter((entry) => entry.key === key).map((entry) => entry.at),\n )\n\n // The oldest layer decides: revealing a fresh page under a stale layout\n // would be a chain nobody rendered together.\n // >= rather than >, so a window of 0 means never rather than \"only within\n // the same millisecond\".\n if (Date.now() - Math.min(...ages) >= maxAge) return false\n }\n\n const anchor = Math.max(...holding)\n\n for (const d of [...depths.keys()].filter((d) => d > anchor)) {\n depths.delete(d)\n notify(d)\n }\n\n for (const d of holding) {\n const state = depths.get(d)!\n\n depths.set(d, retain(state.entries, [...state.order.filter((k) => k !== key), key], key))\n notify(d)\n }\n\n return true\n}\n\n/**\n * Drop everything, so boundaries fall back to their server-given children.\n *\n * A deployment invalidates them all: a segment from the previous build has no\n * claim on being correct for this one.\n */\nexport function clearSegments(): void {\n const all = [...depths.keys()]\n depths.clear()\n for (const depth of all) notify(depth)\n}\n"]}
|
package/dist/js/slotStore.d.ts
CHANGED
package/dist/js/slotStore.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slotStore.js","sourceRoot":"","sources":["../../src/js/slotStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAA;AAC1C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAA;AAElD,kFAAkF;AAClF,MAAM,KAAK,GAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;AAE5C,SAAS,MAAM,CAAC,IAAY;IAC1B,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAU;IAC9C,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IACzB,MAAM,CAAC,IAAI,CAAC,CAAA;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,QAAkB;IAC9D,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAY,CAAA;IAE3D,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACtB,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAE7B,OAAO,GAAG,EAAE;QACV,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC3B,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAA;AACjC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IAE/B,KAAK,CAAC,KAAK,EAAE,CAAA;IACb,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AACvB,CAAC"}
|
|
1
|
+
{"version":3,"file":"slotStore.js","sourceRoot":"","sources":["../../src/js/slotStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAA;AAC1C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAA;AAElD,kFAAkF;AAClF,MAAM,KAAK,GAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;AAE5C,SAAS,MAAM,CAAC,IAAY;IAC1B,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAU;IAC9C,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IACzB,MAAM,CAAC,IAAI,CAAC,CAAA;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,QAAkB;IAC9D,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAY,CAAA;IAE3D,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACtB,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAE7B,OAAO,GAAG,EAAE;QACV,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC3B,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAA;AACjC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IAE/B,KAAK,CAAC,KAAK,EAAE,CAAA;IACb,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AACvB,CAAC","sourcesContent":["/**\n * What each parallel slot is showing, when the server has re-rendered one.\n *\n * A slot is the only region of a page smaller than the page that the server\n * can name, so it is the unit an action can invalidate on its own — two tables\n * refresh apart from each other only if they are two slots.\n *\n * Empty is meaningful, and is the ordinary case: a slot with nothing stored\n * renders what the server sent with the page. Nothing is retained here the way\n * segments retain pages; a slot has one current value and replacing it is the\n * whole point.\n */\n\ntype Tree = unknown\ntype Listener = () => void\n\n/**\n * Immutable, like the segment store and for the same reason:\n * useSyncExternalStore compares snapshots by identity, so returning a fresh\n * object per read reads as \"changed every render\" and loops until React gives\n * up. Every write replaces the entry; nothing is edited in place.\n */\ninterface SlotState {\n readonly tree: Tree\n}\n\nconst slots = new Map<string, SlotState>()\nconst listeners = new Map<string, Set<Listener>>()\n\n/** Shared by every slot that has never been written to, so identity is stable. */\nconst EMPTY: SlotState = { tree: undefined }\n\nfunction notify(name: string): void {\n listeners.get(name)?.forEach((listener) => listener())\n}\n\nexport function setSlot(name: string, tree: Tree): void {\n slots.set(name, { tree })\n notify(name)\n}\n\nexport function subscribeToSlot(name: string, listener: Listener): () => void {\n const existing = listeners.get(name) ?? new Set<Listener>()\n\n existing.add(listener)\n listeners.set(name, existing)\n\n return () => {\n existing.delete(listener)\n }\n}\n\nexport function getSlotState(name: string): SlotState {\n return slots.get(name) ?? EMPTY\n}\n\n/**\n * Forget every slot.\n *\n * A navigation replaces the page, and a slot rendered for the page you left\n * has no claim on the one you arrived at.\n */\nexport function clearSlots(): void {\n const names = [...slots.keys()]\n\n slots.clear()\n names.forEach(notify)\n}\n"]}
|
|
@@ -44,4 +44,3 @@ export declare function issuesToErrors(issues: NonNullable<StandardSchemaResult<
|
|
|
44
44
|
*/
|
|
45
45
|
export declare function validateWith(schema: StandardSchemaV1 | undefined, value: unknown): Promise<Record<string, string[]> | null>;
|
|
46
46
|
export {};
|
|
47
|
-
//# sourceMappingURL=standardSchema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standardSchema.js","sourceRoot":"","sources":["../../src/js/standardSchema.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAmCb;;;;;;;GAOG;AACH,SAAS,OAAO,CAAC,IAA8I;IAC7J,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAEzC,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;SAC3F,IAAI,CAAC,GAAG,CAAC,CAAA;AACd,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,cAAc,CAC5B,MAA4D;IAE5D,MAAM,MAAM,GAA6B,EAAE,CAAA;IAE3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAEhC;QAAA,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC7C,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAoC,EACpC,KAAc;IAEd,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAExD,OAAO,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACzF,CAAC"}
|
|
1
|
+
{"version":3,"file":"standardSchema.js","sourceRoot":"","sources":["../../src/js/standardSchema.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAmCb;;;;;;;GAOG;AACH,SAAS,OAAO,CAAC,IAA8I;IAC7J,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAEzC,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;SAC3F,IAAI,CAAC,GAAG,CAAC,CAAA;AACd,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,cAAc,CAC5B,MAA4D;IAE5D,MAAM,MAAM,GAA6B,EAAE,CAAA;IAE3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAEhC;QAAA,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC7C,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAoC,EACpC,KAAc;IAEd,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAExD,OAAO,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACzF,CAAC","sourcesContent":["\"use client\";\n\n/**\n * Validating a form before it is submitted, with whatever library you use.\n *\n * Not with an adapter per library: Standard Schema is the contract Zod,\n * Valibot, ArkType and others already implement, so this speaks to the schema\n * itself and never imports one. The types below are the spec, restated here\n * rather than depended on — it is an interface, and a package for it would be\n * a dependency that ships nothing.\n *\n * Validation on the client is a courtesy, never a control. The server still\n * has to check: a form is one way to reach an action, and the action is a\n * public endpoint reachable without it.\n */\n\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly '~standard': {\n readonly version: 1\n readonly vendor: string\n readonly validate: (\n value: unknown,\n ) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>\n readonly types?: { readonly input: Input; readonly output: Output }\n }\n}\n\ninterface StandardSchemaResult<Output> {\n readonly value?: Output\n readonly issues?: ReadonlyArray<{\n readonly message: string\n readonly path?: ReadonlyArray<PropertyKey | { readonly key: PropertyKey }>\n }>\n}\n\n/**\n * The field an issue belongs to.\n *\n * Segments are joined with dots so a nested field reads the way it was named —\n * `address.city`, not `address`. An issue with no path is about the form\n * rather than any field, and goes under the empty string, which is where a\n * component that wants to render it can look.\n */\nfunction fieldOf(path: StandardSchemaResult<unknown>['issues'] extends undefined ? never : NonNullable<StandardSchemaResult<unknown>['issues']>[number]['path']): string {\n if (!path || path.length === 0) return ''\n\n return path\n .map((segment) => (typeof segment === 'object' && segment !== null ? segment.key : segment))\n .join('.')\n}\n\n/** Issues grouped by field, in the shape the form already reports. */\nexport function issuesToErrors(\n issues: NonNullable<StandardSchemaResult<unknown>['issues']>,\n): Record<string, string[]> {\n const errors: Record<string, string[]> = {}\n\n for (const issue of issues) {\n const field = fieldOf(issue.path)\n\n ;(errors[field] ??= []).push(issue.message)\n }\n\n return errors\n}\n\n/**\n * Run a schema, or nothing if there is not one.\n *\n * Returns the errors to show, or null when the value is acceptable. The parsed\n * value is deliberately discarded: what gets submitted is the FormData the\n * browser built, and quietly sending something else — a coerced number, a\n * trimmed string — would mean the action receives what the schema decided\n * rather than what the field said.\n */\nexport async function validateWith(\n schema: StandardSchemaV1 | undefined,\n value: unknown,\n): Promise<Record<string, string[]> | null> {\n if (!schema) return null\n\n const result = await schema['~standard'].validate(value)\n\n return result.issues && result.issues.length > 0 ? issuesToErrors(result.issues) : null\n}\n"]}
|
package/dist/js/useForm.d.ts
CHANGED
package/dist/js/useForm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useForm.js","sourceRoot":"","sources":["../../src/js/useForm.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useForm.js","sourceRoot":"","sources":["../../src/js/useForm.ts"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport { useState, useRef, useCallback, useTransition } from \"react\";\nimport { ServerValidationError, ServerDumpError } from \"./errors\";\nimport { validateWith } from \"./standardSchema\";\nimport type { StandardSchemaV1 } from \"./standardSchema\";\n\ntype SetDataFn<T> = {\n <K extends keyof T>(field: K, value: T[K]): void;\n (values: Partial<T>): void;\n};\n\nexport interface UseFormReturn<T extends Record<string, unknown>> {\n data: T;\n setData: SetDataFn<T>;\n errors: Partial<Record<keyof T & string, string[]>>;\n error: (field: keyof T & string) => string | undefined;\n hasErrors: boolean;\n pending: boolean;\n processing: boolean;\n wasSuccessful: boolean;\n recentlySuccessful: boolean;\n clearErrors: (...fields: (keyof T & string)[]) => void;\n reset: (...fields: (keyof T)[]) => void;\n setDefaults: (values?: Partial<T>) => void;\n transform: (fn: (data: T) => Record<string, unknown>) => void;\n submit: (action: (formData: FormData) => Promise<unknown>, optimistic?: () => void) => Promise<void>;\n}\n\n/**\n * Serialize form state into FormData for a server action.\n *\n * Exported for testing: this is the contract between useForm and the action —\n * booleans become \"1\"/\"0\" so PHP sees something truthy, arrays repeat under\n * `key[]`, Files pass through untouched for native uploads, and null/undefined\n * are dropped rather than sent as the string \"null\".\n */\nexport function buildFormData(data: Record<string, unknown>): FormData {\n const formData = new FormData();\n\n for (const [key, val] of Object.entries(data)) {\n if (val === null || val === undefined) {\n continue;\n }\n if (val instanceof File) {\n formData.append(key, val);\n } else if (typeof val === \"boolean\") {\n formData.append(key, val ? \"1\" : \"0\");\n } else if (Array.isArray(val)) {\n for (const item of val) {\n formData.append(`${key}[]`, String(item));\n }\n } else {\n formData.append(key, String(val));\n }\n }\n\n return formData;\n}\n\nexport function useForm<T extends Record<string, unknown>>(\n initialValues: T,\n options: {\n /**\n * Check the fields before submitting, with any Standard Schema — Zod,\n * Valibot, ArkType.\n *\n * A failure fills `errors` and the action is never called. A courtesy, not\n * a control: the action is reachable without this form, so the server\n * still has to check.\n */\n schema?: StandardSchemaV1\n } = {},\n): UseFormReturn<T> {\n const [data, setDataState] = useState<T>(initialValues);\n const [errors, setErrors] = useState<Record<string, string[]>>({});\n const [wasSuccessful, setWasSuccessful] = useState(false);\n const [recentlySuccessful, setRecentlySuccessful] = useState(false);\n const [isPending, startTransition] = useTransition();\n\n const defaultsRef = useRef<T>({ ...initialValues });\n const transformRef = useRef<((data: T) => Record<string, unknown>) | null>(null);\n const recentTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n const setData: SetDataFn<T> = useCallback(\n (fieldOrValues: keyof T | Partial<T>, value?: unknown) => {\n if (typeof fieldOrValues === \"object\") {\n setDataState((prev) => ({ ...prev, ...fieldOrValues }));\n } else {\n setDataState((prev) => ({ ...prev, [fieldOrValues]: value as T[keyof T] }));\n }\n },\n []\n );\n\n const error = useCallback(\n (field: keyof T & string): string | undefined => errors[field]?.[0],\n [errors]\n );\n\n const clearErrors = useCallback(\n (...fields: (keyof T & string)[]) => {\n if (fields.length === 0) {\n setErrors({});\n } else {\n setErrors((prev) => {\n const next = { ...prev };\n for (const f of fields) {\n delete next[f];\n }\n return next;\n });\n }\n },\n []\n );\n\n const reset = useCallback(\n (...fields: (keyof T)[]) => {\n if (fields.length === 0) {\n setDataState({ ...defaultsRef.current });\n } else {\n setDataState((prev) => {\n const next = { ...prev };\n for (const f of fields) {\n next[f] = defaultsRef.current[f];\n }\n return next;\n });\n }\n clearErrors();\n },\n [clearErrors]\n );\n\n const setDefaults = useCallback(\n (values?: Partial<T>) => {\n if (values) {\n defaultsRef.current = { ...defaultsRef.current, ...values };\n } else {\n defaultsRef.current = { ...data };\n }\n },\n [data, options.schema]\n );\n\n const transformFn = useCallback(\n (fn: (data: T) => Record<string, unknown>) => {\n transformRef.current = fn;\n },\n []\n );\n\n const submit = useCallback(\n (action: (formData: FormData) => Promise<unknown>, optimistic?: () => void): Promise<void> => {\n return new Promise<void>((resolve, reject) => {\n startTransition(async () => {\n try {\n // Before the optimistic update, so a rejected form never shows the\n // row it was going to add.\n const invalid = await validateWith(options.schema, data);\n\n if (invalid) {\n setErrors(invalid);\n resolve();\n\n return;\n }\n\n // Call optimistic updater inside the transition so React's\n // useOptimistic picks it up and auto-reverts on settle.\n optimistic?.();\n\n const payload = transformRef.current ? transformRef.current(data) : data;\n const formData = buildFormData(payload);\n\n setErrors({});\n\n const result = await action(formData);\n const answer = result as\n | { validationErrors?: Record<string, string[]>; serverError?: string }\n | undefined\n\n // An action built with createActionClient returns its failures\n // rather than throwing them, because a rejected server action is\n // serialised opaquely and the fields it named would be lost.\n if (answer?.validationErrors) {\n setErrors(answer.validationErrors);\n resolve();\n\n return;\n }\n\n if (answer?.serverError) {\n reject(new Error(answer.serverError));\n\n return;\n }\n\n setWasSuccessful(true);\n setRecentlySuccessful(true);\n\n if (recentTimerRef.current) {\n clearTimeout(recentTimerRef.current);\n }\n recentTimerRef.current = setTimeout(() => setRecentlySuccessful(false), 2000);\n\n resolve();\n } catch (err) {\n if (err instanceof ServerValidationError) {\n setErrors(err.errors);\n }\n if (err instanceof ServerDumpError) {\n resolve();\n return;\n }\n reject(err);\n }\n });\n });\n },\n [data]\n );\n\n return {\n data,\n setData,\n errors: errors as Partial<Record<keyof T & string, string[]>>,\n error,\n hasErrors: Object.keys(errors).length > 0,\n pending: isPending,\n processing: isPending,\n wasSuccessful,\n recentlySuccessful,\n clearErrors,\n reset,\n setDefaults,\n transform: transformFn,\n submit,\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLinkStatus.js","sourceRoot":"","sources":["../../src/js/useLinkStatus.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC"}
|
|
1
|
+
{"version":3,"file":"useLinkStatus.js","sourceRoot":"","sources":["../../src/js/useLinkStatus.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC","sourcesContent":["\"use client\";\n\nexport { useLinkStatus } from \"./Link\";\n"]}
|
package/dist/js/useOnline.d.ts
CHANGED
package/dist/js/useOnline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useOnline.js","sourceRoot":"","sources":["../../src/js/useOnline.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEhE;4DAC4D;AAC5D,SAAS,iBAAiB;IACxB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO,oBAAoB,CAAC,kBAAkB,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,CAAC,SAAS,EAAE,CAAC;AACtB,CAAC"}
|
|
1
|
+
{"version":3,"file":"useOnline.js","sourceRoot":"","sources":["../../src/js/useOnline.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEhE;4DAC4D;AAC5D,SAAS,iBAAiB;IACxB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO,oBAAoB,CAAC,kBAAkB,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,CAAC,SAAS,EAAE,CAAC;AACtB,CAAC","sourcesContent":["\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\nimport { isReachable, subscribeReachable } from \"./onlineStore\";\n\n/** The server is reachable from itself; assuming otherwise would render an\n * offline state into the HTML and mismatch on hydration. */\nfunction getServerSnapshot(): boolean {\n return true;\n}\n\n/**\n * Whether the app can reach the server, as the router last observed it.\n *\n * See onlineStore.ts for why this is not `navigator.onLine`.\n */\nexport function useOnline(): boolean {\n return useSyncExternalStore(subscribeReachable, isReachable, getServerSnapshot);\n}\n\nexport function useOffline(): boolean {\n return !useOnline();\n}\n"]}
|
package/dist/js/usePathname.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePathname.js","sourceRoot":"","sources":["../../src/js/usePathname.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,IAAI,eAAe,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;AACrF,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;AAExC,SAAS,MAAM;IACb,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC3C,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAClC,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,SAAS,CAAC,QAAoB;IACrC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,QAAQ,GAAG,mBAAmB,EAAE,CAAC;IAEvC,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC;AACtE,CAAC"}
|
|
1
|
+
{"version":3,"file":"usePathname.js","sourceRoot":"","sources":["../../src/js/usePathname.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,IAAI,eAAe,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;AACrF,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;AAExC,SAAS,MAAM;IACb,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC3C,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAClC,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,SAAS,CAAC,QAAoB;IACrC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,QAAQ,GAAG,mBAAmB,EAAE,CAAC;IAEvC,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC;AACtE,CAAC","sourcesContent":["\"use client\";\n\nimport { useSyncExternalStore } from \"react\";\nimport { useRenderedPathname } from \"./PathnameProvider\";\n\nlet currentPathname = typeof window !== \"undefined\" ? window.location.pathname : \"/\";\nconst listeners = new Set<() => void>();\n\nfunction notify(): void {\n currentPathname = window.location.pathname;\n listeners.forEach((fn) => fn());\n}\n\nif (typeof window !== \"undefined\") {\n window.addEventListener(\"rsc-navigate\", notify);\n window.addEventListener(\"popstate\", notify);\n}\n\nfunction subscribe(callback: () => void): () => void {\n listeners.add(callback);\n return () => listeners.delete(callback);\n}\n\nfunction getSnapshot(): string {\n return currentPathname;\n}\n\n/**\n * The current pathname, live across navigations.\n *\n * The server snapshot comes from the url being rendered rather than a\n * constant. It used to be \"/\" — so a nav bar on any other page rendered with\n * the wrong link marked active, and only hydration put it right.\n */\nexport function usePathname(): string {\n const rendered = useRenderedPathname();\n\n return useSyncExternalStore(subscribe, getSnapshot, () => rendered);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSearchParams.js","sourceRoot":"","sources":["../../src/js/useSearchParams.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;AAExC;;;;;;GAMG;AACH,IAAI,MAAM,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,eAAe,EAAE,EAAE,CAAC;AAE3D,SAAS,aAAa;IACpB,OAAO,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;AACrE,CAAC;AAED,SAAS,MAAM;IACb,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAClC,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,SAAS,CAAC,QAAoB;IACrC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAExB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,IAAI,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QAC7B,MAAM,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;IAC3D,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,SAAS,iBAAiB;IACxB,MAAM,IAAI,KAAK,CACb,4FAA4F;QAC1F,4FAA4F;QAC5F,sDAAsD,CACzD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AACzE,CAAC"}
|
|
1
|
+
{"version":3,"file":"useSearchParams.js","sourceRoot":"","sources":["../../src/js/useSearchParams.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;AAExC;;;;;;GAMG;AACH,IAAI,MAAM,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,eAAe,EAAE,EAAE,CAAC;AAE3D,SAAS,aAAa;IACpB,OAAO,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;AACrE,CAAC;AAED,SAAS,MAAM;IACb,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAClC,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,SAAS,CAAC,QAAoB;IACrC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAExB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,IAAI,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QAC7B,MAAM,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;IAC3D,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,SAAS,iBAAiB;IACxB,MAAM,IAAI,KAAK,CACb,4FAA4F;QAC1F,4FAA4F;QAC5F,sDAAsD,CACzD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AACzE,CAAC","sourcesContent":["\"use client\";\n\n/**\n * The query string, live across navigations.\n *\n * Deliberately unlike usePathname, which answers with the url being rendered.\n * A pathname is fixed for a given stored page; a query string is not — the\n * same route is asked for with `?q=shoes` and `?q=hats`, and a page stored\n * holding one of them would serve it to everyone.\n *\n * So there is no server snapshot to give, and this throws rather than\n * inventing one. React treats an error thrown during SSR as recoverable at the\n * nearest Suspense boundary: the fallback is what gets stored, and the client\n * renders the real thing on hydration. Without a boundary the error reaches the\n * root, nothing paints, and the build refuses the route and says why — which\n * is the same answer it gives for reading the request too early on the server.\n *\n * Returning an empty URLSearchParams instead would be worse than either: the\n * page would be stored showing results for no query at all, and nothing would\n * say so.\n */\n\nimport { useSyncExternalStore } from \"react\";\n\nconst listeners = new Set<() => void>();\n\n/**\n * Cached by the string it was parsed from.\n *\n * useSyncExternalStore compares snapshots by identity, so handing back a fresh\n * URLSearchParams on every read reads as \"changed every time\" and loops until\n * React gives up.\n */\nlet cached = { search: \"\", params: new URLSearchParams() };\n\nfunction currentSearch(): string {\n return typeof window === \"undefined\" ? \"\" : window.location.search;\n}\n\nfunction notify(): void {\n listeners.forEach((fn) => fn());\n}\n\nif (typeof window !== \"undefined\") {\n window.addEventListener(\"rsc-navigate\", notify);\n window.addEventListener(\"popstate\", notify);\n}\n\nfunction subscribe(callback: () => void): () => void {\n listeners.add(callback);\n\n return () => listeners.delete(callback);\n}\n\nfunction getSnapshot(): URLSearchParams {\n const search = currentSearch();\n\n if (search !== cached.search) {\n cached = { search, params: new URLSearchParams(search) };\n }\n\n return cached.params;\n}\n\nfunction getServerSnapshot(): URLSearchParams {\n throw new Error(\n \"useSearchParams() was read while rendering on the server, where there is no query string. \" +\n \"Wrap the component in <Suspense> — or add a loading.tsx beside the page — so the fallback \" +\n \"is stored and the real value arrives in the browser.\",\n );\n}\n\nexport function useSearchParams(): URLSearchParams {\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n"]}
|
package/dist/manifest.d.ts
CHANGED
package/dist/manifest.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,gFAAgF;AAChF,uCAAuC;AACvC,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,oBAAoB"}
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,gFAAgF;AAChF,uCAAuC;AACvC,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,oBAAoB","sourcesContent":["// The shape of routes.json — what the build discovered, for a host to read.\n//\n// The build already walks app/ to generate its entries, and every host needs\n// the same facts: which url a component answers, what layouts wrap it, which\n// slots and sections belong to it. Laravel used to scan the tree a second time\n// to work that out; a JS host would have had to write a third walk. This is the\n// one answer, and these are its types.\n//\n// Urls are segments rather than a pattern string, because the pattern is the\n// host's dialect: Laravel writes {slug}, Hono writes :slug, and neither is the\n// build's business.\n\nexport interface RouteSegment {\n type: 'static' | 'param' | 'catchAll'\n value: string\n}\n\nexport interface ManifestRoute {\n component: string\n segments: RouteSegment[]\n layouts: string[]\n loadings: string[]\n /**\n * `middleware.ts` files above this route, outermost first.\n *\n * Run before anything at or below them renders, on every path. A check is\n * not UI, and making it a layout meant the client could decline it: layouts\n * are skipped on a partial navigation, and what gets skipped is named in a\n * header nothing can verify.\n */\n middleware: string[]\n slots: Record<string, string>\n sections: string[]\n /**\n * The host's route-config file beside this page, if it named one, and the\n * ancestor ones that also apply — outermost first, this page's excluded.\n *\n * Relative to the project root: an absolute path is true only on the machine\n * that produced it, and building in a container is ordinary.\n */\n config: string | null\n ancestorConfigs: string[]\n /**\n * Host middleware names for this route, outermost first.\n *\n * Declared in a route.ts beside or above the page. The engine does not know\n * what they mean — they are the host's own vocabulary — it only runs them\n * past the host before anything at or below this route renders.\n *\n * Empty on a route that named none, and on every route in an app that never\n * wrote a route.ts, which is why this needs no flag.\n *\n * Optional because registration boots from the PREVIOUS build's manifest: a\n * shape change takes two builds to settle, and a required field would make\n * the first of those a hard failure rather than a route with no guards.\n */\n hostMiddleware?: string[]\n /**\n * Whether the page exports generateStaticParams.\n *\n * Recorded here so a host can plan a build — which routes to ask for urls,\n * which to leave on demand — without loading the server bundle first. The\n * function itself is reached through the bundle's getStaticParams(), because\n * only the bundle can run it.\n */\n staticParams: boolean\n /**\n * Whether this route ships the client runtime.\n *\n * False renders to HTML and stops: no bootstrap, so no React, no Flight\n * client, no router. A client component on such a route is inert markup — a\n * button that does nothing — so the build refuses the combination rather\n * than shipping it.\n */\n clientJs: boolean\n}\n\nexport interface ManifestIntercept {\n component: string\n slot: string\n segments: RouteSegment[]\n /** (.) same level, (..) one up, (...) from the root. */\n marker: string\n}\n\nexport interface RouteManifest {\n version: number\n build: { output: string; exportPath: string; payloadName: string }\n routes: ManifestRoute[]\n intercepts: ManifestIntercept[]\n}\n"]}
|
package/dist/nondeterminism.d.ts
CHANGED
|
@@ -12,4 +12,3 @@ export declare function watchNondeterminism(): () => void;
|
|
|
12
12
|
/** Run a route's render, collecting what it reached for. */
|
|
13
13
|
export declare function whileRendering<T>(run: () => Promise<T>): Promise<[T, string[]]>;
|
|
14
14
|
export { WATCHED };
|
|
15
|
-
//# sourceMappingURL=nondeterminism.d.ts.map
|