@rsc-kit/core 0.20.6 → 0.20.8
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/dist/cache.d.ts +11 -0
- package/dist/cache.js +13 -0
- package/dist/cache.js.map +1 -1
- package/dist/clientEntries.js +4 -1
- package/dist/clientEntries.js.map +1 -1
- package/dist/files.d.ts +38 -0
- package/dist/files.js +109 -0
- package/dist/files.js.map +1 -1
- package/dist/host.d.ts +11 -2
- package/dist/host.js +37 -6
- package/dist/host.js.map +1 -1
- package/dist/js/ActivityRouter.d.ts +11 -0
- package/dist/js/ActivityRouter.js +24 -4
- package/dist/js/ActivityRouter.js.map +1 -1
- package/dist/js/Form.js +24 -2
- package/dist/js/Form.js.map +1 -1
- package/dist/js/Link.d.ts +1 -1
- package/dist/js/Link.js +25 -9
- package/dist/js/Link.js.map +1 -1
- package/dist/js/PageTransition.d.ts +27 -0
- package/dist/js/PageTransition.js +57 -0
- package/dist/js/PageTransition.js.map +1 -0
- package/dist/js/PathnameProvider.d.ts +15 -2
- package/dist/js/PathnameProvider.js +17 -2
- package/dist/js/PathnameProvider.js.map +1 -1
- package/dist/js/SegmentBoundary.js +27 -3
- package/dist/js/SegmentBoundary.js.map +1 -1
- package/dist/js/activityMarkers.d.ts +7 -0
- package/dist/js/activityMarkers.js +18 -0
- package/dist/js/activityMarkers.js.map +1 -0
- package/dist/js/createViteRscApp.js +122 -76
- package/dist/js/createViteRscApp.js.map +1 -1
- package/dist/js/earlyClicks.d.ts +3 -1
- package/dist/js/earlyClicks.js +28 -5
- package/dist/js/earlyClicks.js.map +1 -1
- package/dist/js/errors.d.ts +2 -0
- package/dist/js/errors.js +11 -0
- package/dist/js/errors.js.map +1 -1
- package/dist/js/imagePreload.d.ts +37 -0
- package/dist/js/imagePreload.js +116 -0
- package/dist/js/imagePreload.js.map +1 -0
- package/dist/js/navigate.d.ts +47 -1
- package/dist/js/navigate.js +249 -10
- package/dist/js/navigate.js.map +1 -1
- package/dist/js/perf.d.ts +25 -0
- package/dist/js/perf.js +57 -0
- package/dist/js/perf.js.map +1 -0
- package/dist/js/router.d.ts +10 -1
- package/dist/js/router.js +9 -2
- package/dist/js/router.js.map +1 -1
- package/dist/js/segmentStore.d.ts +52 -0
- package/dist/js/segmentStore.js +123 -2
- package/dist/js/segmentStore.js.map +1 -1
- package/dist/js/staleAssets.d.ts +10 -0
- package/dist/js/staleAssets.js +23 -2
- package/dist/js/staleAssets.js.map +1 -1
- package/dist/js/viewportPrefetch.d.ts +19 -10
- package/dist/js/viewportPrefetch.js +50 -15
- package/dist/js/viewportPrefetch.js.map +1 -1
- package/dist/shellHead.d.ts +22 -0
- package/dist/shellHead.js +43 -0
- package/dist/shellHead.js.map +1 -0
- package/dist/vite.js +250 -55
- package/dist/vite.js.map +1 -1
- package/package.json +5 -1
|
@@ -21,6 +21,17 @@ export interface RouterEntry {
|
|
|
21
21
|
export interface ActivityRouterHandle {
|
|
22
22
|
/** Show `tree` for `key`, retaining whatever was visible before. */
|
|
23
23
|
show(key: string, tree: ReactNode): void;
|
|
24
|
+
/**
|
|
25
|
+
* Give the visible page a new tree, in place: the same entry, the same
|
|
26
|
+
* Activity, re-rendered. For a re-render of the whole document after an
|
|
27
|
+
* action - revalidate("all") - which is the page the visitor is on, not a
|
|
28
|
+
* page they went to. show() under the current url made a NEW entry when
|
|
29
|
+
* the visible one was keyed by the url the document loaded with, and
|
|
30
|
+
* every partial navigation since had left that key alone: the whole app
|
|
31
|
+
* remounted under a second Activity, the old one hidden behind it, and
|
|
32
|
+
* the form's state - its "added to cart" - went with it.
|
|
33
|
+
*/
|
|
34
|
+
replace(tree: ReactNode): void;
|
|
24
35
|
/** Reveal a retained page without refetching. False when not retained. */
|
|
25
36
|
restore(key: string): boolean;
|
|
26
37
|
/** Drop every retained page but the visible one. */
|
|
@@ -13,8 +13,9 @@ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
|
13
13
|
* would grow the document for the length of the session; the least recently
|
|
14
14
|
* visited entry is dropped past the limit.
|
|
15
15
|
*/
|
|
16
|
-
import { Activity, useCallback, useEffect, useRef, useState } from 'react';
|
|
17
|
-
import { setNavigateHandler, setRestoreHandler } from './navigate';
|
|
16
|
+
import { Activity, useCallback, useEffect, useRef, useState, startTransition } from 'react';
|
|
17
|
+
import { setNavigateHandler, setReplaceRootHandler, setRestoreHandler } from './navigate';
|
|
18
|
+
import { navigationCommitted } from './perf';
|
|
18
19
|
import { clearSegments, setSegment } from './segmentStore';
|
|
19
20
|
/** Pages kept alive behind the visible one. Four covers ordinary back-and-forth. */
|
|
20
21
|
export const DEFAULT_RETENTION = 4;
|
|
@@ -44,6 +45,20 @@ export function useActivityRouter(initial, retention = DEFAULT_RETENTION) {
|
|
|
44
45
|
commit([...others, { key, tree }]);
|
|
45
46
|
setActiveKey(key);
|
|
46
47
|
}, [commit, touch]);
|
|
48
|
+
const replace = useCallback((tree) => {
|
|
49
|
+
const active = entriesRef.current.find((entry) => entry.key === activeKey);
|
|
50
|
+
if (!active)
|
|
51
|
+
return;
|
|
52
|
+
// A transition, so a boundary in the new tree still waiting on its row
|
|
53
|
+
// keeps what is on screen rather than showing its fallback - which
|
|
54
|
+
// would unmount the form under it and take its state along. The
|
|
55
|
+
// document's payload arrives with some rows still pending, and a
|
|
56
|
+
// synchronous render showed the product's skeleton for a frame and
|
|
57
|
+
// remounted the "add to cart" form beneath it.
|
|
58
|
+
startTransition(() => {
|
|
59
|
+
commit(entriesRef.current.map((entry) => (entry === active ? { key: entry.key, tree } : entry)));
|
|
60
|
+
});
|
|
61
|
+
}, [activeKey, commit]);
|
|
47
62
|
const restore = useCallback((key) => {
|
|
48
63
|
if (!entriesRef.current.some((entry) => entry.key === key))
|
|
49
64
|
return false;
|
|
@@ -56,8 +71,8 @@ export function useActivityRouter(initial, retention = DEFAULT_RETENTION) {
|
|
|
56
71
|
commit(kept);
|
|
57
72
|
orderRef.current = [activeKey];
|
|
58
73
|
}, [activeKey, commit]);
|
|
59
|
-
const handleRef = useRef({ show, restore, clear });
|
|
60
|
-
handleRef.current = { show, restore, clear };
|
|
74
|
+
const handleRef = useRef({ show, replace, restore, clear });
|
|
75
|
+
handleRef.current = { show, replace, restore, clear };
|
|
61
76
|
return { entries, activeKey, handle: handleRef.current };
|
|
62
77
|
}
|
|
63
78
|
/**
|
|
@@ -69,6 +84,10 @@ export function useActivityRouter(initial, retention = DEFAULT_RETENTION) {
|
|
|
69
84
|
*/
|
|
70
85
|
export function ActivityRoot({ initialKey, initialTree }) {
|
|
71
86
|
const { entries, activeKey, handle } = useActivityRouter({ key: initialKey, tree: initialTree });
|
|
87
|
+
// A whole document shown, or a held page revealed: the navigation's commit.
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
navigationCommitted();
|
|
90
|
+
}, [activeKey, entries]);
|
|
72
91
|
useEffect(() => {
|
|
73
92
|
setNavigateHandler((tree, key, segmentDepth) => {
|
|
74
93
|
// A partial payload replaces one segment inside the page that is already
|
|
@@ -81,6 +100,7 @@ export function ActivityRoot({ initialKey, initialTree }) {
|
|
|
81
100
|
handle.show(key, tree);
|
|
82
101
|
});
|
|
83
102
|
setRestoreHandler((key) => handle.restore(key));
|
|
103
|
+
setReplaceRootHandler((tree) => handle.replace(tree));
|
|
84
104
|
}, [handle]);
|
|
85
105
|
return (_jsx(_Fragment, { children: entries.map((entry) => (_jsx(Activity, { mode: entry.key === activeKey ? 'visible' : 'hidden', children: entry.tree }, entry.key))) }));
|
|
86
106
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActivityRouter.js","sourceRoot":"","sources":["../../src/js/ActivityRouter.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Keeps recently visited pages mounted but hidden, so going back restores them\n * rather than rebuilding them.\n *\n * Rendering each navigation with root.render() unmounts the previous tree, and\n * unmounting destroys client state: a half-typed form, an open disclosure, a\n * scrolled list. React's <Activity mode=\"hidden\"> hides a tree without\n * unmounting it — effects are torn down, but state survives — which is what\n * makes returning feel like the browser's own back button rather than a reload.\n *\n * Retention is bounded. Hidden trees keep their DOM, so an unbounded history\n * would grow the document for the length of the session; the least recently\n * visited entry is dropped past the limit.\n */\n\nimport { Activity, useCallback, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { setNavigateHandler, setRestoreHandler } from './navigate'\nimport { clearSegments, setSegment } from './segmentStore'\n\nexport interface RouterEntry {\n /** Identifies the page: its URL, or the intercept variant of one. */\n key: string\n tree: ReactNode\n}\n\nexport interface ActivityRouterHandle {\n /** Show `tree` for `key`, retaining whatever was visible before. */\n show(key: string, tree: ReactNode): void\n /** Reveal a retained page without refetching. False when not retained. */\n restore(key: string): boolean\n /** Drop every retained page but the visible one. */\n clear(): void\n}\n\n/** Pages kept alive behind the visible one. Four covers ordinary back-and-forth. */\nexport const DEFAULT_RETENTION = 4\n\nexport function useActivityRouter(\n initial: RouterEntry,\n retention: number = DEFAULT_RETENTION,\n): { entries: RouterEntry[]; activeKey: string; handle: ActivityRouterHandle } {\n const [entries, setEntries] = useState<RouterEntry[]>([initial])\n const [activeKey, setActiveKey] = useState(initial.key)\n\n // The router calls into this from outside React, where the values captured by\n // the last render may already be stale. Refs mirror state so those calls read\n // what is actually mounted.\n const entriesRef = useRef(entries)\n const orderRef = useRef<string[]>([initial.key])\n\n const commit = useCallback((next: RouterEntry[]) => {\n entriesRef.current = next\n setEntries(next)\n }, [])\n\n // Recency, most recent last. Retention is by visit order rather than\n // insertion, so bouncing between two pages evicts neither.\n const touch = useCallback(\n (key: string) => {\n const next = orderRef.current.filter((k) => k !== key)\n next.push(key)\n orderRef.current = next.slice(-Math.max(1, retention))\n\n return orderRef.current\n },\n [retention],\n )\n\n const show = useCallback(\n (key: string, tree: ReactNode) => {\n const kept = touch(key)\n const others = entriesRef.current.filter((entry) => entry.key !== key && kept.includes(entry.key))\n\n commit([...others, { key, tree }])\n setActiveKey(key)\n },\n [commit, touch],\n )\n\n const restore = useCallback(\n (key: string) => {\n if (!entriesRef.current.some((entry) => entry.key === key)) return false\n\n touch(key)\n setActiveKey(key)\n\n return true\n },\n [touch],\n )\n\n const clear = useCallback(() => {\n const kept = entriesRef.current.filter((entry) => entry.key === activeKey)\n\n commit(kept)\n orderRef.current = [activeKey]\n }, [activeKey, commit])\n\n const handleRef = useRef<ActivityRouterHandle>({ show, restore, clear })\n handleRef.current = { show, restore, clear }\n\n return { entries, activeKey, handle: handleRef.current }\n}\n\n/**\n * Root of the hydrated app.\n *\n * Registers itself with the navigation engine so a navigation becomes a state\n * update here rather than a fresh root.render(), which is what allows the\n * previous page to stay mounted.\n */\nexport function ActivityRoot({ initialKey, initialTree }: { initialKey: string; initialTree: ReactNode }) {\n const { entries, activeKey, handle } = useActivityRouter({ key: initialKey, tree: initialTree })\n\n useEffect(() => {\n setNavigateHandler((tree, key, segmentDepth) => {\n // A partial payload replaces one segment inside the page that is already\n // showing; only a whole document is a new page to retain.\n if (segmentDepth > 0) {\n setSegment(segmentDepth, key, tree as ReactNode)\n\n return\n }\n\n clearSegments()\n handle.show(key, tree as ReactNode)\n })\n\n setRestoreHandler((key) => handle.restore(key))\n }, [handle])\n\n return (\n <>\n {entries.map((entry) => (\n <Activity key={entry.key} mode={entry.key === activeKey ? 'visible' : 'hidden'}>\n {entry.tree}\n </Activity>\n ))}\n </>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ActivityRouter.js","sourceRoot":"","sources":["../../src/js/ActivityRouter.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Keeps recently visited pages mounted but hidden, so going back restores them\n * rather than rebuilding them.\n *\n * Rendering each navigation with root.render() unmounts the previous tree, and\n * unmounting destroys client state: a half-typed form, an open disclosure, a\n * scrolled list. React's <Activity mode=\"hidden\"> hides a tree without\n * unmounting it — effects are torn down, but state survives — which is what\n * makes returning feel like the browser's own back button rather than a reload.\n *\n * Retention is bounded. Hidden trees keep their DOM, so an unbounded history\n * would grow the document for the length of the session; the least recently\n * visited entry is dropped past the limit.\n */\n\nimport { Activity, useCallback, useEffect, useRef, useState, startTransition } from 'react'\nimport type { ReactNode } from 'react'\nimport { setNavigateHandler, setReplaceRootHandler, setRestoreHandler } from './navigate'\nimport { navigationCommitted } from './perf'\nimport { clearSegments, setSegment } from './segmentStore'\n\nexport interface RouterEntry {\n /** Identifies the page: its URL, or the intercept variant of one. */\n key: string\n tree: ReactNode\n}\n\nexport interface ActivityRouterHandle {\n /** Show `tree` for `key`, retaining whatever was visible before. */\n show(key: string, tree: ReactNode): void\n /**\n * Give the visible page a new tree, in place: the same entry, the same\n * Activity, re-rendered. For a re-render of the whole document after an\n * action - revalidate(\"all\") - which is the page the visitor is on, not a\n * page they went to. show() under the current url made a NEW entry when\n * the visible one was keyed by the url the document loaded with, and\n * every partial navigation since had left that key alone: the whole app\n * remounted under a second Activity, the old one hidden behind it, and\n * the form's state - its \"added to cart\" - went with it.\n */\n replace(tree: ReactNode): void\n /** Reveal a retained page without refetching. False when not retained. */\n restore(key: string): boolean\n /** Drop every retained page but the visible one. */\n clear(): void\n}\n\n/** Pages kept alive behind the visible one. Four covers ordinary back-and-forth. */\nexport const DEFAULT_RETENTION = 4\n\nexport function useActivityRouter(\n initial: RouterEntry,\n retention: number = DEFAULT_RETENTION,\n): { entries: RouterEntry[]; activeKey: string; handle: ActivityRouterHandle } {\n const [entries, setEntries] = useState<RouterEntry[]>([initial])\n const [activeKey, setActiveKey] = useState(initial.key)\n\n // The router calls into this from outside React, where the values captured by\n // the last render may already be stale. Refs mirror state so those calls read\n // what is actually mounted.\n const entriesRef = useRef(entries)\n const orderRef = useRef<string[]>([initial.key])\n\n const commit = useCallback((next: RouterEntry[]) => {\n entriesRef.current = next\n setEntries(next)\n }, [])\n\n // Recency, most recent last. Retention is by visit order rather than\n // insertion, so bouncing between two pages evicts neither.\n const touch = useCallback(\n (key: string) => {\n const next = orderRef.current.filter((k) => k !== key)\n next.push(key)\n orderRef.current = next.slice(-Math.max(1, retention))\n\n return orderRef.current\n },\n [retention],\n )\n\n const show = useCallback(\n (key: string, tree: ReactNode) => {\n const kept = touch(key)\n const others = entriesRef.current.filter((entry) => entry.key !== key && kept.includes(entry.key))\n\n commit([...others, { key, tree }])\n setActiveKey(key)\n },\n [commit, touch],\n )\n\n const replace = useCallback(\n (tree: ReactNode) => {\n const active = entriesRef.current.find((entry) => entry.key === activeKey)\n\n if (!active) return\n\n // A transition, so a boundary in the new tree still waiting on its row\n // keeps what is on screen rather than showing its fallback - which\n // would unmount the form under it and take its state along. The\n // document's payload arrives with some rows still pending, and a\n // synchronous render showed the product's skeleton for a frame and\n // remounted the \"add to cart\" form beneath it.\n startTransition(() => {\n commit(entriesRef.current.map((entry) => (entry === active ? { key: entry.key, tree } : entry)))\n })\n },\n [activeKey, commit],\n )\n\n const restore = useCallback(\n (key: string) => {\n if (!entriesRef.current.some((entry) => entry.key === key)) return false\n\n touch(key)\n setActiveKey(key)\n\n return true\n },\n [touch],\n )\n\n const clear = useCallback(() => {\n const kept = entriesRef.current.filter((entry) => entry.key === activeKey)\n\n commit(kept)\n orderRef.current = [activeKey]\n }, [activeKey, commit])\n\n const handleRef = useRef<ActivityRouterHandle>({ show, replace, restore, clear })\n handleRef.current = { show, replace, restore, clear }\n\n return { entries, activeKey, handle: handleRef.current }\n}\n\n/**\n * Root of the hydrated app.\n *\n * Registers itself with the navigation engine so a navigation becomes a state\n * update here rather than a fresh root.render(), which is what allows the\n * previous page to stay mounted.\n */\nexport function ActivityRoot({ initialKey, initialTree }: { initialKey: string; initialTree: ReactNode }) {\n const { entries, activeKey, handle } = useActivityRouter({ key: initialKey, tree: initialTree })\n\n // A whole document shown, or a held page revealed: the navigation's commit.\n useEffect(() => {\n navigationCommitted()\n }, [activeKey, entries])\n\n useEffect(() => {\n setNavigateHandler((tree, key, segmentDepth) => {\n // A partial payload replaces one segment inside the page that is already\n // showing; only a whole document is a new page to retain.\n if (segmentDepth > 0) {\n setSegment(segmentDepth, key, tree as ReactNode)\n\n return\n }\n\n clearSegments()\n handle.show(key, tree as ReactNode)\n })\n\n setRestoreHandler((key) => handle.restore(key))\n setReplaceRootHandler((tree) => handle.replace(tree as ReactNode))\n }, [handle])\n\n return (\n <>\n {entries.map((entry) => (\n <Activity key={entry.key} mode={entry.key === activeKey ? 'visible' : 'hidden'}>\n {entry.tree}\n </Activity>\n ))}\n </>\n )\n}\n"]}
|
package/dist/js/Form.js
CHANGED
|
@@ -271,8 +271,22 @@ export default function Form({ action, method: methodProp, defaultValues, store:
|
|
|
271
271
|
useEffect(snapshot, [snapshot]);
|
|
272
272
|
// A bound control with no native element behind it changes through the
|
|
273
273
|
// store, not through an input event - a Radix select - so the store is
|
|
274
|
-
// watched too. Its input, if it has one, is read back with the rest.
|
|
274
|
+
// watched too. Its input, if it has one, is read back with the rest. In
|
|
275
|
+
// a microtask: the fields subscribed before this effect ran, so React's
|
|
276
|
+
// own flush of their update is queued ahead of the measure, and the
|
|
277
|
+
// element is read after it was written.
|
|
275
278
|
useEffect(() => store.subscribe(() => queueMicrotask(measureDirty)), [store, measureDirty]);
|
|
279
|
+
// A reset writes every bound field at once, through a render of this
|
|
280
|
+
// component; the measure has to read the elements after that commit,
|
|
281
|
+
// and a microtask queued during the reset ran before it. Marked here,
|
|
282
|
+
// measured by the effect below once the render lands.
|
|
283
|
+
const remeasure = useRef(false);
|
|
284
|
+
useEffect(() => {
|
|
285
|
+
if (!remeasure.current)
|
|
286
|
+
return;
|
|
287
|
+
remeasure.current = false;
|
|
288
|
+
measureDirty();
|
|
289
|
+
});
|
|
276
290
|
// Both handles, one element. React 19 hands a function component its ref
|
|
277
291
|
// as a prop, and spread after the form's own it replaced it - every
|
|
278
292
|
// FormData read then found null. A caller's ref is filled beside ours.
|
|
@@ -298,12 +312,18 @@ export default function Form({ action, method: methodProp, defaultValues, store:
|
|
|
298
312
|
});
|
|
299
313
|
}
|
|
300
314
|
}, []);
|
|
315
|
+
// The store first, then the DOM. A bound field renders from the store, so
|
|
316
|
+
// a DOM reset alone put its default in the element for one frame and the
|
|
317
|
+
// typed value back on the next render: a port's Discard left the name as
|
|
318
|
+
// typed while the dirty flag went clean beside it.
|
|
301
319
|
const resetForm = useCallback(() => {
|
|
320
|
+
remeasure.current = true;
|
|
321
|
+
store.reset();
|
|
302
322
|
formRef.current?.reset();
|
|
303
323
|
setErrors({});
|
|
304
324
|
setCurrentData({});
|
|
305
325
|
queueMicrotask(measureDirty);
|
|
306
|
-
}, [measureDirty]);
|
|
326
|
+
}, [store, measureDirty]);
|
|
307
327
|
useEffect(() => {
|
|
308
328
|
if (isGetForm && prefetch === "mount") {
|
|
309
329
|
const fn = window.__rsc_prefetch;
|
|
@@ -398,6 +418,7 @@ export default function Form({ action, method: methodProp, defaultValues, store:
|
|
|
398
418
|
return;
|
|
399
419
|
}
|
|
400
420
|
if (resetOnSuccess) {
|
|
421
|
+
store.reset();
|
|
401
422
|
formRef.current?.reset();
|
|
402
423
|
setTouched({});
|
|
403
424
|
setCurrentData({});
|
|
@@ -445,6 +466,7 @@ export default function Form({ action, method: methodProp, defaultValues, store:
|
|
|
445
466
|
replace,
|
|
446
467
|
preserveScroll,
|
|
447
468
|
resetOnSuccess,
|
|
469
|
+
store,
|
|
448
470
|
schema,
|
|
449
471
|
transform,
|
|
450
472
|
optimistic,
|
package/dist/js/Form.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Form.js","sourceRoot":"","sources":["../../src/js/Form.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport type { Route } from \"../routes.js\";\nimport {\n type FormHTMLAttributes,\n type FormEvent,\n type ReactNode,\n type Ref,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useActionState,\n useState,\n useSyncExternalStore,\n useTransition,\n} from \"react\";\nimport { submitForm } from \"../formSubmit\";\nimport { ServerValidationError, ServerDumpError, ServerRedirectError, redirectedTo } from \"./errors\";\nimport { buildFormData, decodeFormData } from \"./formEncoding\";\nimport { createFormStore } from \"./formStore\";\nimport type { FormStore } from \"./formStore\";\nimport { validateWith } from \"./standardSchema\";\nimport type { StandardSchemaV1 } from \"./standardSchema\";\n\n/**\n * How long a blur that leaves the form waits before checking the field. A\n * dialog's exit animation is shorter than this; a form still on screen after\n * it is one somebody is looking at.\n */\nconst LEAVE_FORM_SETTLE_MS = 300;\n\ntype PrefetchStrategy = \"hover\" | \"mount\" | \"none\";\n\n/**\n * What `field(name)` hands a control, spread straight onto it.\n *\n * The same four things react-hook-form's `<Controller>` gives, because it is\n * the same job: a component with no native control behind it needs a value and\n * a way to report a new one.\n */\ninterface FieldBinding<V> {\n name: string;\n value: V;\n /**\n * Either shape: a DOM event, or the value itself.\n *\n * A native input passes the event; a Radix select or a rich editor passes\n * what was chosen. A binder understanding only one of them would work on\n * half the controls anyone actually uses.\n */\n onChange: (next: V | { target: { value: V } }) => void;\n onBlur: () => void;\n}\n\n/**\n * What is known about one field, separately from what is spread onto it.\n *\n * Two objects rather than one, which is react-hook-form's split and it is right\n * for a mechanical reason: `touched` and `invalid` are not DOM attributes, so a\n * single spreadable object would put them on the element and React would warn\n * about every one.\n */\ninterface FieldState {\n /** Whether it has been left at least once. */\n touched: boolean;\n /** Whether it currently has errors. */\n invalid: boolean;\n /** Its messages, ready for a `<FieldError>`. */\n errors: string[];\n}\n\ninterface FormRenderProps<\n T extends Record<string, unknown> = Record<string, unknown>,\n> {\n pending: boolean;\n data: T;\n /**\n * Keyed by field name, so a typo is a type error rather than undefined.\n *\n * Partial because most fields have none, and nested paths join with dots —\n * `errors['address.city']`, which is the key a Standard Schema issue for that\n * field produces.\n */\n errors: Partial<Record<keyof T & string, string[]>> &\n Record<string, string[] | undefined>;\n error: (field: keyof T & string) => string | undefined;\n clearErrors: (...fields: (keyof T & string)[]) => void;\n reset: () => void;\n /**\n * Whether anything differs from what the form started with.\n *\n * For the Save button that stays quiet until there is something to save,\n * and the Discard that appears beside it. Read from the form itself - a\n * snapshot of its FormData on mount, compared on every input - so an\n * uncontrolled field counts, which nothing outside the form can do. A\n * successful submit makes the current values the new baseline; `reset()`\n * goes back to the first one.\n */\n dirty: boolean;\n /** Whether the last submit was accepted. */\n succeeded: boolean;\n /**\n * The same thing, for two seconds.\n *\n * The \"Saved ✓\" that appears and fades. Worth having as state rather than a\n * timer in every form that wants one, because the timer has to be cleared\n * when the component goes away and that is the part people forget.\n */\n recentlySucceeded: boolean;\n /**\n * Bind one field so this component holds its value.\n *\n * Most fields need nothing: they are uncontrolled, the DOM holds the value,\n * and it is read back as FormData on submit. Reach for this when the DOM\n * cannot hold it for you — a control with no native element behind it, or a\n * value you want to read as it is typed:\n *\n * <Input {...field('title')} />\n * <span>{field('body').value.length}/100</span>\n *\n * A bound field is still an ordinary named input, so it arrives in FormData\n * with everything else. Nothing merges; there is one source of truth.\n */\n field: <K extends keyof T & string>(name: K) => FieldBinding<T[K]>;\n /**\n * What is known about a field, for deciding how to show it.\n *\n * const title = fieldState('title')\n *\n * <Field data-invalid={title.invalid}>\n * <Input {...field('title')} aria-invalid={title.invalid} />\n * <FieldError errors={title.errors.map((message) => ({ message }))} />\n * </Field>\n *\n * `touched` is what separates \"not filled in yet\" from \"filled in wrongly\" —\n * an error on a field nobody has visited is a form shouting before anyone\n * has done anything.\n */\n fieldState: (name: string) => FieldState;\n}\n\ninterface FormProps<\n T extends Record<string, unknown> = Record<string, unknown>,\n> extends Omit<\n FormHTMLAttributes<HTMLFormElement>,\n \"action\" | \"method\" | \"children\" | \"onSubmit\" | \"onError\"\n> {\n action: Route | ((formData: FormData) => Promise<unknown>);\n method?: \"get\" | \"post\";\n /**\n * The <form> element, for a caller that needs it - to focus, to scroll to,\n * to hand a library. The form keeps its own handle beside it; a caller's\n * never replaces it.\n */\n ref?: Ref<HTMLFormElement>;\n /**\n * Starting values for fields bound with `field()`.\n *\n * Uncontrolled fields do not need this — they take React's own\n * `defaultValue`, and the DOM keeps whatever is typed into them.\n */\n defaultValues?: Partial<T>;\n /**\n * A store created above this form, from `useFormStore()`.\n *\n * For the one case the context cannot reach: something that is not a\n * descendant — a top bar showing unsaved changes, a sidebar preview — needs\n * the values to exist above both of them. Create the store where they share\n * an ancestor and hand it down.\n *\n * Without it the form makes its own, which is what almost every form wants.\n */\n store?: FormStore;\n prefetch?: PrefetchStrategy;\n cacheFor?: number;\n replace?: boolean;\n preserveScroll?: boolean;\n resetOnSuccess?: boolean;\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, so a mistake\n * costs no round trip. It is a courtesy and not a control: the same action\n * is reachable without this form, so the server still has to check.\n */\n schema?: StandardSchemaV1;\n /** Transform form data before submitting to the server action. */\n transform?: (data: T) => Record<string, unknown>;\n /** Called inside the transition with typed form data. Use it to call your useOptimistic setter. */\n optimistic?: (data: T) => void;\n onSuccess?: (result: unknown) => void;\n /**\n * Called when a submit does not succeed.\n *\n * Validation failures arrive as field errors, with no second argument.\n * Anything else arrives as an empty error map and the thrown value — there\n * are no field errors to report, but the form still has to say so.\n */\n onError?: (errors: Record<string, string[]>, error?: unknown) => void;\n onSubmit?: (formData: FormData) => void | false;\n children: ReactNode | ((form: FormRenderProps<T>) => ReactNode);\n}\n\n/**\n * What an action built with createActionClient answers with.\n *\n * Recognised rather than thrown, because React serialises a rejected server\n * action opaquely: production strips the message and the fields it named are\n * gone. A returned object crosses intact, so a form reads it.\n */\nfunction resultOf(\n value: unknown,\n): { errors?: Record<string, string[]>; serverError?: string; redirected?: string } | null {\n if (typeof value !== \"object\" || value === null) return null;\n\n const result = value as {\n validationErrors?: Record<string, string[]>;\n serverError?: string;\n redirected?: string;\n };\n\n if (result.validationErrors) return { errors: result.validationErrors };\n if (result.serverError) return { serverError: result.serverError };\n if (typeof result.redirected === \"string\") return { redirected: result.redirected };\n\n return null;\n}\n\nconst FormStatusContext = createContext<FormRenderProps>({\n pending: false,\n data: {},\n dirty: false,\n errors: {},\n error: () => undefined,\n clearErrors: () => {},\n reset: () => {},\n succeeded: false,\n recentlySucceeded: false,\n // Outside a Form there is nothing holding a value, so a binding that reported\n // one would be lying. Name only, which is the part that is still true.\n field: ((name: string) => ({\n name,\n value: \"\",\n onChange: () => {},\n onBlur: () => {},\n })) as FormRenderProps[\"field\"],\n fieldState: () => ({ touched: false, invalid: false, errors: [] }),\n});\n\n/**\n * The store, on its own context.\n *\n * Separate from the status context because that one holds a fresh object every\n * render, so anything reading it re-renders with the form. The store is stable\n * for the life of the form, which is what lets a subscriber below it re-render\n * alone.\n */\nconst FormStoreContext = createContext<{\n store: FormStore;\n touch: (name: string) => void;\n} | null>(null);\n\n/**\n * A value store created above the form rather than by it.\n *\n * const store = useFormStore({ title: '' })\n *\n * <TopBar store={store} /> // not inside the form\n * <Form action={save} store={store}>…</Form>\n *\n * For the one case the context cannot reach. `<Form>` makes its own otherwise,\n * and almost every form should let it — this exists so that a component which\n * is not a descendant can still read the values, which is the flexibility\n * react-hook-form and TanStack Form get from `useForm()` being yours to call.\n *\n * Deliberately not reactive itself: creating the store does not subscribe to\n * it, so the component holding it does not re-render on every keystroke and\n * take the whole subtree with it. Read it with `useFormValues(store)`.\n */\nexport function useFormStore<T extends Record<string, unknown>>(\n initial: Partial<T> = {},\n): FormStore {\n const ref = useRef<FormStore | null>(null);\n\n ref.current ??= createFormStore({ ...initial });\n\n return ref.current;\n}\n\n/**\n * One field, subscribed on its own.\n *\n * The same thing `field()` gives, from a component that re-renders when this\n * field changes and at no other time. Reach for it when a form is large enough\n * that re-rendering all of it per keystroke is real:\n *\n * function Title() {\n * const { field, invalid, errors } = useField('title')\n *\n * return <Input {...field} aria-invalid={invalid} />\n * }\n *\n * Which is react-hook-form's `<Controller>` without the render prop: the\n * component you already had to write is the subscription boundary.\n */\nexport function useField(\n name: string,\n store?: FormStore,\n): FieldBinding<string> & FieldState {\n const ctx = useContext(FormStoreContext);\n const status = useContext(FormStatusContext);\n\n if (!ctx && !store) {\n throw new Error(\n \"useField() was called outside a <Form>. It reads that form's values, so there has to be one above it.\",\n );\n }\n\n const source = store ?? ctx!.store;\n const touch = ctx?.touch;\n\n const value = useSyncExternalStore(\n source.subscribe,\n () => (source.get(name) ?? \"\") as string,\n () => \"\",\n );\n\n const errors = status.errors[name] ?? [];\n\n return {\n name,\n value,\n onChange: (next) => {\n source.set(\n name,\n typeof next === \"object\" && next !== null && \"target\" in next\n ? (next as { target: { value: string } }).target.value\n : next,\n );\n },\n onBlur: () => void touch?.(name),\n touched: status.fieldState(name).touched,\n invalid: errors.length > 0,\n errors,\n };\n}\n\n/**\n * Every bound value, from anywhere inside the form.\n *\n * For a summary, a preview, a count of what has changed — something that reads\n * the form without being a field in it. Only values bound through `field()` or\n * `useField` are here: an uncontrolled input's value belongs to the DOM, and\n * this has no way to know it changed.\n */\nexport function useFormValues<T extends Record<string, unknown>>(\n store?: FormStore,\n): Partial<T> {\n const ctx = useContext(FormStoreContext);\n\n if (!ctx && !store) {\n throw new Error(\n \"useFormValues() was called outside a <Form>. It reads that form's values, so there has to be one above it.\",\n );\n }\n\n const source = store ?? ctx!.store;\n\n return useSyncExternalStore(\n source.subscribe,\n () => source.all() as Partial<T>,\n () => ({}) as Partial<T>,\n );\n}\n\nexport function useFormStatus<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(): FormRenderProps<T> {\n return useContext(FormStatusContext) as FormRenderProps<T>;\n}\n\n/**\n * submitForm bound to an action, once per action.\n *\n * Bound on every render, it was a fresh function with a fresh promise for\n * its bound arguments each time - and React, seating a posted form's state\n * during the server render, suspends on that promise until it settles, then\n * renders the component again, which bound again. A render that never\n * finished, on every page a form was posted to without javascript. One\n * bound function per action, and the promise settles once.\n */\nconst boundSubmits = new WeakMap<Function, (previous: unknown, formData: FormData) => Promise<unknown>>();\n\nfunction boundSubmit(action: (formData: FormData) => Promise<unknown>) {\n let bound = boundSubmits.get(action);\n\n if (!bound) {\n bound = submitForm.bind(null, action);\n boundSubmits.set(action, bound);\n }\n\n return bound;\n}\n\n/**\n * A form's values as one comparable string.\n *\n * FormData in document order, a file by its name and size: enough to say\n * whether anything changed, which is all `dirty` asks.\n */\nfunction serializeForm(form: HTMLFormElement): string {\n const parts: string[] = [];\n\n for (const [name, value] of new FormData(form)) {\n parts.push(\n name + \"=\" + (typeof value === \"string\" ? value : \"file:\" + value.name + \":\" + value.size),\n );\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Also exported by name, and re-exported below, because both spellings are in\n * use: `import Form from` and `import { Form } from`.\n */\nexport default function Form<\n T extends Record<string, unknown> = Record<string, unknown>,\n>({\n action,\n method: methodProp,\n defaultValues,\n store: providedStore,\n prefetch = \"hover\",\n cacheFor,\n replace = false,\n preserveScroll = false,\n resetOnSuccess = true,\n schema,\n transform,\n optimistic,\n onSuccess,\n onError,\n onSubmit,\n children,\n ref: callerRef,\n ...rest\n}: FormProps<T>) {\n const isGetForm = typeof action === \"string\";\n const method = methodProp ?? (isGetForm ? \"get\" : \"post\");\n\n // The answer to this form posted without a runtime, seated by React into\n // the form that posted it - which is the only way a visitor with no\n // javascript sees a refusal. Registered through submitForm, bound to the\n // action, because React hands a form-state action a (previous, formData)\n // pair and the action takes the FormData alone. On the server a plain\n // function - a closure, not a server reference - cannot be bound into a\n // form; the form falls back to the action itself, and posts nothing\n // seatable, which is all a closure could ever have done.\n const bindable =\n typeof action === \"function\" && (typeof window !== \"undefined\" || \"$$FORM_ACTION\" in action);\n const [posted, formAction] = useActionState(\n bindable ? boundSubmit(action as (formData: FormData) => Promise<unknown>) : async () => null,\n null as unknown,\n );\n const postedRefusal = resultOf(posted);\n const [errors, setErrors] = useState<Record<string, string[]>>(() => postedRefusal?.errors ?? {});\n const [touched, setTouched] = useState<Record<string, boolean>>({});\n\n /**\n * Mark a field visited, and check it.\n *\n * Checking on blur rather than on every keystroke, because an error that\n * appears while someone is halfway through typing an email address is a form\n * arguing with them. Leaving the field is the moment they have finished\n * saying what they meant.\n *\n * The whole object is validated and only this field's issues are kept: a\n * Standard Schema has no notion of one field, and filtering by path is the\n * honest way to ask it about one. Everything else's errors are left as they\n * were, so blurring an empty field does not light up the rest of the form.\n */\n const touch = useCallback(\n async (name: string) => {\n setTouched((prev) => (prev[name] ? prev : { ...prev, [name]: true }));\n\n if (!schema || !formRef.current) return;\n\n const invalid = await validateWith(\n schema,\n decodeFormData(new FormData(formRef.current), schema),\n );\n\n setErrors((prev) => {\n const next = { ...prev };\n\n if (invalid?.[name]) next[name] = invalid[name];\n else delete next[name];\n\n return next;\n });\n },\n [schema],\n );\n\n const [succeeded, setSucceeded] = useState(false);\n const [recentlySucceeded, setRecentlySucceeded] = useState(false);\n const recentTimer = useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined,\n );\n\n // Cleared on unmount: a timer that fires into a component that has gone is\n // the warning nobody reads and the leak nobody finds.\n useEffect(() => () => clearTimeout(recentTimer.current), []);\n\n // Only the fields someone bound. Everything else is the DOM's.\n //\n // A store rather than state, so a component using `useField` can re-render\n // for one field while this one does not. See formStore.ts.\n const storeRef = useRef<FormStore | null>(null);\n\n storeRef.current ??= createFormStore({\n ...(defaultValues as Record<string, unknown> | undefined),\n });\n\n const store = providedStore ?? storeRef.current;\n\n // Which names the render prop read through `field()`. A change to one of\n // those has to re-render this component, because that is where the value is\n // being displayed; a change to anything else does not, which is what lets a\n // `useField` child stand on its own.\n const readHere = useRef(new Set<string>());\n const [, bump] = useState(0);\n\n useEffect(\n () =>\n store.subscribe((name) => {\n if (name === \"\" || readHere.current.has(name)) bump((n) => n + 1);\n }),\n [store],\n );\n const [currentData, setCurrentData] = useState<T>({} as T);\n const [isPending, startTransition] = useTransition();\n const formRef = useRef<HTMLFormElement>(null);\n\n // What the form started with, as its FormData reads: the baseline `dirty`\n // is measured against. Taken on mount, retaken after a successful submit.\n const baseline = useRef<string | null>(null);\n const [dirty, setDirty] = useState(false);\n // Only a form that reads `dirty` pays for it: the flip from clean to dirty\n // is a render of the whole form, and a field that subscribes for itself\n // is promised that typing in it renders it alone.\n const dirtyRead = useRef(false);\n\n const snapshot = useCallback(() => {\n if (!formRef.current) return;\n\n baseline.current = serializeForm(formRef.current);\n setDirty(false);\n }, []);\n\n const measureDirty = useCallback(() => {\n if (!dirtyRead.current || !formRef.current || baseline.current === null) return;\n\n const now = serializeForm(formRef.current) !== baseline.current;\n\n setDirty((was) => (was === now ? was : now));\n }, []);\n\n useEffect(snapshot, [snapshot]);\n\n // A bound control with no native element behind it changes through the\n // store, not through an input event - a Radix select - so the store is\n // watched too. Its input, if it has one, is read back with the rest.\n useEffect(() => store.subscribe(() => queueMicrotask(measureDirty)), [store, measureDirty]);\n\n // Both handles, one element. React 19 hands a function component its ref\n // as a prop, and spread after the form's own it replaced it - every\n // FormData read then found null. A caller's ref is filled beside ours.\n const attachForm = useCallback(\n (element: HTMLFormElement | null) => {\n formRef.current = element;\n\n if (typeof callerRef === \"function\") callerRef(element);\n else if (callerRef) callerRef.current = element;\n },\n [callerRef],\n );\n\n const error = useCallback(\n (field: keyof T & string): string | undefined => errors[field]?.[0],\n [errors],\n );\n\n const clearErrors = useCallback((...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 const resetForm = useCallback(() => {\n formRef.current?.reset();\n setErrors({});\n setCurrentData({} as T);\n queueMicrotask(measureDirty);\n }, [measureDirty]);\n\n useEffect(() => {\n if (isGetForm && prefetch === \"mount\") {\n const fn = (window as any).__rsc_prefetch;\n fn?.(action, cacheFor);\n }\n }, [isGetForm, prefetch, action, cacheFor]);\n\n const doPrefetch = useCallback(() => {\n if (!isGetForm) return;\n const fn = (window as any).__rsc_prefetch;\n fn?.(action as string, cacheFor);\n }, [isGetForm, action, cacheFor]);\n\n const handleSubmit = useCallback(\n async (e: FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n const formData = new FormData(e.currentTarget);\n const data = decodeFormData<T>(formData, schema);\n setCurrentData(data);\n\n if (onSubmit?.(formData) === false) {\n return;\n }\n\n if (isGetForm && method === \"get\") {\n const url = new URL(action as string, window.location.origin);\n for (const [key, value] of formData.entries()) {\n if (typeof value === \"string\" && value !== \"\") {\n url.searchParams.set(key, value);\n }\n }\n\n const path = url.pathname + url.search;\n const shell = action as string;\n const nav = (window as any).__rsc_navigate;\n const prefetched = (window as any).__rsc_is_prefetched;\n\n // The route without its query is that route's shell: the layout, the\n // chrome, and whatever it renders with nothing to show yet. Prefetching\n // on hover put it in the cache, so going there first costs no request\n // and puts the page on screen while the real query is still running.\n //\n // The query itself is never prefetched. It is the expensive half, and\n // hovering a search button is not a reason to run someone's search.\n if (path !== shell && prefetched?.(shell)) {\n // Awaited rather than raced: navigate() aborts whatever is in flight,\n // so starting the real one first would cancel the shell before it\n // could render. It is a cache hit, so this is a render, not a wait.\n Promise.resolve(nav?.(shell, { replace, preserveScroll })).then(() =>\n // Replaces, so the shell does not become a back-button stop of its\n // own — the pair leaves exactly one entry behind.\n nav?.(path, { replace: true, preserveScroll }),\n );\n\n return;\n }\n\n nav?.(path, { replace, preserveScroll });\n return;\n }\n\n const serverAction = action as (formData: FormData) => Promise<unknown>;\n\n // Rebuilt rather than edited: transform returns the values to send, so\n // whatever was in the form and is not in the result should not go.\n if (transform) {\n for (const key of [...formData.keys()]) formData.delete(key);\n\n for (const [key, value] of buildFormData(transform(data))) {\n formData.append(key, value);\n }\n }\n\n setErrors({});\n\n // Before the transition, so a failure neither runs the optimistic\n // update nor leaves the form looking like it is submitting.\n const invalid = await validateWith(schema, data);\n\n if (invalid) {\n setErrors(invalid);\n onError?.(invalid);\n\n return;\n }\n\n startTransition(async () => {\n try {\n // Call optimistic updater inside the transition so React's\n // useOptimistic picks it up and auto-reverts on settle.\n optimistic?.(data);\n\n const result = await serverAction(formData);\n const refused = resultOf(result);\n\n // The action answered with somewhere to go, and callServer has\n // already started the navigation. Not an error for a form to\n // show, and not a save to announce either: signing in and going\n // to the dashboard is the whole success.\n if (refused?.redirected !== undefined || redirectedTo() !== null) return;\n\n if (refused) {\n if (refused.errors) {\n setErrors(refused.errors);\n onError?.(refused.errors);\n } else {\n onError?.({}, new Error(refused.serverError));\n }\n\n return;\n }\n\n if (resetOnSuccess) {\n formRef.current?.reset();\n setTouched({});\n setCurrentData({} as T);\n }\n\n // Saved: what is in the form now is what the server has.\n snapshot();\n\n setErrors({});\n setSucceeded(true);\n setRecentlySucceeded(true);\n\n clearTimeout(recentTimer.current);\n recentTimer.current = setTimeout(\n () => setRecentlySucceeded(false),\n 2_000,\n );\n\n onSuccess?.(result);\n } catch (err) {\n // A redirect no longer arrives here - callServer resolves after\n // performing it - but an action called through something older\n // than that still may.\n if (err instanceof ServerRedirectError) return;\n\n if (err instanceof ServerValidationError) {\n setErrors(err.errors);\n onError?.(err.errors);\n } else if (err instanceof ServerDumpError) {\n // Dump overlay is already shown — silently swallow\n } else if (onError) {\n // Not rethrown. A rejected action never settles, and until it\n // settles React keeps the optimistic update on screen — so an\n // unexpected failure left the row showing as though the write had\n // worked. Settling is what takes it back.\n onError({}, err);\n } else {\n // Nothing is handling it, and swallowing here would lose it\n // entirely — the reason this used to rethrow.\n console.error(\"[rsc-kit] form submit failed\", err);\n }\n }\n });\n },\n [\n action,\n isGetForm,\n method,\n replace,\n preserveScroll,\n resetOnSuccess,\n schema,\n transform,\n optimistic,\n onSubmit,\n onSuccess,\n onError,\n ],\n );\n\n const field = useCallback(\n <K extends keyof T & string>(name: K): FieldBinding<T[K]> => {\n // Recorded during render, deliberately: this is how the form knows which\n // values it is the one displaying. Idempotent, so a double render in\n // development records the same name twice and means the same thing.\n readHere.current.add(name);\n\n return {\n name,\n value: (store.get(name) ?? \"\") as T[K],\n onChange: (next: unknown) => {\n const value =\n typeof next === \"object\" && next !== null && \"target\" in next\n ? (next as { target: { value: string } }).target.value\n : next;\n\n store.set(name, value);\n },\n onBlur: () => void touch(name),\n };\n },\n [store, touch],\n );\n\n const fieldState = useCallback(\n (name: string): FieldState => ({\n touched: touched[name] === true,\n invalid: (errors[name]?.length ?? 0) > 0,\n errors: errors[name] ?? [],\n }),\n [touched, errors],\n );\n\n // Stable, so a subscriber below does not re-render because this one did.\n const storeContext = useMemo(() => ({ store, touch }), [store, touch]);\n\n const formStatus: FormRenderProps<T> = {\n pending: isPending,\n data: currentData,\n get dirty() {\n dirtyRead.current = true;\n\n return dirty;\n },\n succeeded,\n recentlySucceeded,\n field,\n fieldState,\n errors: errors as FormRenderProps<T>[\"errors\"],\n error,\n clearErrors,\n reset: resetForm,\n };\n\n return (\n <FormStoreContext.Provider value={storeContext}>\n <FormStatusContext.Provider value={formStatus as FormRenderProps}>\n <form\n ref={attachForm}\n // On the element as well as in the handler, which is what makes this\n // work before hydration. React emits a form a browser can submit on its\n // own for a server action, and an ordinary action/method pair for a\n // url — so a submit that happens before the javascript arrives still\n // reaches the server.\n //\n // The two do not fight: handleSubmit calls preventDefault() first, and\n // React does not run a form action when the submit event was cancelled.\n // So the enhanced path wins whenever there is one, and the native path\n // is what is left when there is not.\n action={(bindable ? formAction : action) as never}\n // Only for a url. React sets the method itself for a server action, and\n // passing one alongside is what it warns about.\n method={isGetForm ? method : undefined}\n onSubmit={handleSubmit}\n // Every keystroke and every toggle, from any control in the form,\n // uncontrolled ones included: React's onInput here is the bubbling\n // `input` event. A caller's own handler still runs.\n onInput={(event) => {\n rest.onInput?.(event);\n measureDirty();\n }}\n onChange={(event) => {\n rest.onChange?.(event);\n measureDirty();\n }}\n // On the form, not only on the bound fields. `focusout` bubbles where\n // `blur` does not, so React's onBlur here sees every control that was\n // left — including the uncontrolled ones, which are most of them and\n // would otherwise never be marked touched at all.\n onBlur={(event) => {\n const target = event.target as unknown as {\n name?: string;\n value?: string;\n };\n const name = target.name;\n\n if (!name) return;\n\n // Leaving a field is the moment to check it; leaving the form is\n // not the same moment. Focus going to nothing, or to something\n // outside the form, is a click on the page - most often a dialog\n // closing - and an error painted into a dialog as it animates out\n // reads as a submit that nobody made. An empty field left that way\n // is not checked at all. One with something in it is, but only\n // once the dust settles: if the form is gone or hidden by then, it\n // was a dialog closing, and nobody is there to read the error.\n const left = event.relatedTarget as Node | null;\n const stillInForm = !!left && !!formRef.current?.contains(left);\n\n if (stillInForm) {\n void touch(name);\n\n return;\n }\n\n if (!target.value) return;\n\n setTimeout(() => {\n const form = formRef.current;\n\n if (\n !form ||\n !form.isConnected ||\n form.getClientRects().length === 0\n )\n return;\n if (\n form.closest(\n \"[hidden], [aria-hidden='true'], [data-ending-style], [data-closed]\",\n )\n )\n return;\n\n void touch(name);\n }, LEAVE_FORM_SETTLE_MS);\n }}\n onMouseEnter={prefetch === \"hover\" ? doPrefetch : undefined}\n data-pending={isPending ? \"\" : undefined}\n {...rest}\n >\n {typeof children === \"function\" ? children(formStatus) : children}\n </form>\n </FormStatusContext.Provider>\n </FormStoreContext.Provider>\n );\n}\n\n// Also by name, because both spellings are in use: `import Form` and\n// `import { Form }`. This was a re-export of \"./Form\" — from inside Form.tsx,\n// so the module named itself. It survived on the bundler tolerating a circular\n// self-reference, left over from when a separate barrel re-exported a\n// FormComponent.tsx that a case-insensitive filesystem would not let be called\n// Form.ts. There is one file now, and it can just export what it declares.\nexport { Form };\n"]}
|
|
1
|
+
{"version":3,"file":"Form.js","sourceRoot":"","sources":["../../src/js/Form.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport type { Route } from \"../routes.js\";\nimport {\n type FormHTMLAttributes,\n type FormEvent,\n type ReactNode,\n type Ref,\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useActionState,\n useState,\n useSyncExternalStore,\n useTransition,\n} from \"react\";\nimport { submitForm } from \"../formSubmit\";\nimport { ServerValidationError, ServerDumpError, ServerRedirectError, redirectedTo } from \"./errors\";\nimport { buildFormData, decodeFormData } from \"./formEncoding\";\nimport { createFormStore } from \"./formStore\";\nimport type { FormStore } from \"./formStore\";\nimport { validateWith } from \"./standardSchema\";\nimport type { StandardSchemaV1 } from \"./standardSchema\";\n\n/**\n * How long a blur that leaves the form waits before checking the field. A\n * dialog's exit animation is shorter than this; a form still on screen after\n * it is one somebody is looking at.\n */\nconst LEAVE_FORM_SETTLE_MS = 300;\n\ntype PrefetchStrategy = \"hover\" | \"mount\" | \"none\";\n\n/**\n * What `field(name)` hands a control, spread straight onto it.\n *\n * The same four things react-hook-form's `<Controller>` gives, because it is\n * the same job: a component with no native control behind it needs a value and\n * a way to report a new one.\n */\ninterface FieldBinding<V> {\n name: string;\n value: V;\n /**\n * Either shape: a DOM event, or the value itself.\n *\n * A native input passes the event; a Radix select or a rich editor passes\n * what was chosen. A binder understanding only one of them would work on\n * half the controls anyone actually uses.\n */\n onChange: (next: V | { target: { value: V } }) => void;\n onBlur: () => void;\n}\n\n/**\n * What is known about one field, separately from what is spread onto it.\n *\n * Two objects rather than one, which is react-hook-form's split and it is right\n * for a mechanical reason: `touched` and `invalid` are not DOM attributes, so a\n * single spreadable object would put them on the element and React would warn\n * about every one.\n */\ninterface FieldState {\n /** Whether it has been left at least once. */\n touched: boolean;\n /** Whether it currently has errors. */\n invalid: boolean;\n /** Its messages, ready for a `<FieldError>`. */\n errors: string[];\n}\n\ninterface FormRenderProps<\n T extends Record<string, unknown> = Record<string, unknown>,\n> {\n pending: boolean;\n data: T;\n /**\n * Keyed by field name, so a typo is a type error rather than undefined.\n *\n * Partial because most fields have none, and nested paths join with dots —\n * `errors['address.city']`, which is the key a Standard Schema issue for that\n * field produces.\n */\n errors: Partial<Record<keyof T & string, string[]>> &\n Record<string, string[] | undefined>;\n error: (field: keyof T & string) => string | undefined;\n clearErrors: (...fields: (keyof T & string)[]) => void;\n reset: () => void;\n /**\n * Whether anything differs from what the form started with.\n *\n * For the Save button that stays quiet until there is something to save,\n * and the Discard that appears beside it. Read from the form itself - a\n * snapshot of its FormData on mount, compared on every input - so an\n * uncontrolled field counts, which nothing outside the form can do. A\n * successful submit makes the current values the new baseline; `reset()`\n * goes back to the first one.\n */\n dirty: boolean;\n /** Whether the last submit was accepted. */\n succeeded: boolean;\n /**\n * The same thing, for two seconds.\n *\n * The \"Saved ✓\" that appears and fades. Worth having as state rather than a\n * timer in every form that wants one, because the timer has to be cleared\n * when the component goes away and that is the part people forget.\n */\n recentlySucceeded: boolean;\n /**\n * Bind one field so this component holds its value.\n *\n * Most fields need nothing: they are uncontrolled, the DOM holds the value,\n * and it is read back as FormData on submit. Reach for this when the DOM\n * cannot hold it for you — a control with no native element behind it, or a\n * value you want to read as it is typed:\n *\n * <Input {...field('title')} />\n * <span>{field('body').value.length}/100</span>\n *\n * A bound field is still an ordinary named input, so it arrives in FormData\n * with everything else. Nothing merges; there is one source of truth.\n */\n field: <K extends keyof T & string>(name: K) => FieldBinding<T[K]>;\n /**\n * What is known about a field, for deciding how to show it.\n *\n * const title = fieldState('title')\n *\n * <Field data-invalid={title.invalid}>\n * <Input {...field('title')} aria-invalid={title.invalid} />\n * <FieldError errors={title.errors.map((message) => ({ message }))} />\n * </Field>\n *\n * `touched` is what separates \"not filled in yet\" from \"filled in wrongly\" —\n * an error on a field nobody has visited is a form shouting before anyone\n * has done anything.\n */\n fieldState: (name: string) => FieldState;\n}\n\ninterface FormProps<\n T extends Record<string, unknown> = Record<string, unknown>,\n> extends Omit<\n FormHTMLAttributes<HTMLFormElement>,\n \"action\" | \"method\" | \"children\" | \"onSubmit\" | \"onError\"\n> {\n action: Route | ((formData: FormData) => Promise<unknown>);\n method?: \"get\" | \"post\";\n /**\n * The <form> element, for a caller that needs it - to focus, to scroll to,\n * to hand a library. The form keeps its own handle beside it; a caller's\n * never replaces it.\n */\n ref?: Ref<HTMLFormElement>;\n /**\n * Starting values for fields bound with `field()`.\n *\n * Uncontrolled fields do not need this — they take React's own\n * `defaultValue`, and the DOM keeps whatever is typed into them.\n */\n defaultValues?: Partial<T>;\n /**\n * A store created above this form, from `useFormStore()`.\n *\n * For the one case the context cannot reach: something that is not a\n * descendant — a top bar showing unsaved changes, a sidebar preview — needs\n * the values to exist above both of them. Create the store where they share\n * an ancestor and hand it down.\n *\n * Without it the form makes its own, which is what almost every form wants.\n */\n store?: FormStore;\n prefetch?: PrefetchStrategy;\n cacheFor?: number;\n replace?: boolean;\n preserveScroll?: boolean;\n resetOnSuccess?: boolean;\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, so a mistake\n * costs no round trip. It is a courtesy and not a control: the same action\n * is reachable without this form, so the server still has to check.\n */\n schema?: StandardSchemaV1;\n /** Transform form data before submitting to the server action. */\n transform?: (data: T) => Record<string, unknown>;\n /** Called inside the transition with typed form data. Use it to call your useOptimistic setter. */\n optimistic?: (data: T) => void;\n onSuccess?: (result: unknown) => void;\n /**\n * Called when a submit does not succeed.\n *\n * Validation failures arrive as field errors, with no second argument.\n * Anything else arrives as an empty error map and the thrown value — there\n * are no field errors to report, but the form still has to say so.\n */\n onError?: (errors: Record<string, string[]>, error?: unknown) => void;\n onSubmit?: (formData: FormData) => void | false;\n children: ReactNode | ((form: FormRenderProps<T>) => ReactNode);\n}\n\n/**\n * What an action built with createActionClient answers with.\n *\n * Recognised rather than thrown, because React serialises a rejected server\n * action opaquely: production strips the message and the fields it named are\n * gone. A returned object crosses intact, so a form reads it.\n */\nfunction resultOf(\n value: unknown,\n): { errors?: Record<string, string[]>; serverError?: string; redirected?: string } | null {\n if (typeof value !== \"object\" || value === null) return null;\n\n const result = value as {\n validationErrors?: Record<string, string[]>;\n serverError?: string;\n redirected?: string;\n };\n\n if (result.validationErrors) return { errors: result.validationErrors };\n if (result.serverError) return { serverError: result.serverError };\n if (typeof result.redirected === \"string\") return { redirected: result.redirected };\n\n return null;\n}\n\nconst FormStatusContext = createContext<FormRenderProps>({\n pending: false,\n data: {},\n dirty: false,\n errors: {},\n error: () => undefined,\n clearErrors: () => {},\n reset: () => {},\n succeeded: false,\n recentlySucceeded: false,\n // Outside a Form there is nothing holding a value, so a binding that reported\n // one would be lying. Name only, which is the part that is still true.\n field: ((name: string) => ({\n name,\n value: \"\",\n onChange: () => {},\n onBlur: () => {},\n })) as FormRenderProps[\"field\"],\n fieldState: () => ({ touched: false, invalid: false, errors: [] }),\n});\n\n/**\n * The store, on its own context.\n *\n * Separate from the status context because that one holds a fresh object every\n * render, so anything reading it re-renders with the form. The store is stable\n * for the life of the form, which is what lets a subscriber below it re-render\n * alone.\n */\nconst FormStoreContext = createContext<{\n store: FormStore;\n touch: (name: string) => void;\n} | null>(null);\n\n/**\n * A value store created above the form rather than by it.\n *\n * const store = useFormStore({ title: '' })\n *\n * <TopBar store={store} /> // not inside the form\n * <Form action={save} store={store}>…</Form>\n *\n * For the one case the context cannot reach. `<Form>` makes its own otherwise,\n * and almost every form should let it — this exists so that a component which\n * is not a descendant can still read the values, which is the flexibility\n * react-hook-form and TanStack Form get from `useForm()` being yours to call.\n *\n * Deliberately not reactive itself: creating the store does not subscribe to\n * it, so the component holding it does not re-render on every keystroke and\n * take the whole subtree with it. Read it with `useFormValues(store)`.\n */\nexport function useFormStore<T extends Record<string, unknown>>(\n initial: Partial<T> = {},\n): FormStore {\n const ref = useRef<FormStore | null>(null);\n\n ref.current ??= createFormStore({ ...initial });\n\n return ref.current;\n}\n\n/**\n * One field, subscribed on its own.\n *\n * The same thing `field()` gives, from a component that re-renders when this\n * field changes and at no other time. Reach for it when a form is large enough\n * that re-rendering all of it per keystroke is real:\n *\n * function Title() {\n * const { field, invalid, errors } = useField('title')\n *\n * return <Input {...field} aria-invalid={invalid} />\n * }\n *\n * Which is react-hook-form's `<Controller>` without the render prop: the\n * component you already had to write is the subscription boundary.\n */\nexport function useField(\n name: string,\n store?: FormStore,\n): FieldBinding<string> & FieldState {\n const ctx = useContext(FormStoreContext);\n const status = useContext(FormStatusContext);\n\n if (!ctx && !store) {\n throw new Error(\n \"useField() was called outside a <Form>. It reads that form's values, so there has to be one above it.\",\n );\n }\n\n const source = store ?? ctx!.store;\n const touch = ctx?.touch;\n\n const value = useSyncExternalStore(\n source.subscribe,\n () => (source.get(name) ?? \"\") as string,\n () => \"\",\n );\n\n const errors = status.errors[name] ?? [];\n\n return {\n name,\n value,\n onChange: (next) => {\n source.set(\n name,\n typeof next === \"object\" && next !== null && \"target\" in next\n ? (next as { target: { value: string } }).target.value\n : next,\n );\n },\n onBlur: () => void touch?.(name),\n touched: status.fieldState(name).touched,\n invalid: errors.length > 0,\n errors,\n };\n}\n\n/**\n * Every bound value, from anywhere inside the form.\n *\n * For a summary, a preview, a count of what has changed — something that reads\n * the form without being a field in it. Only values bound through `field()` or\n * `useField` are here: an uncontrolled input's value belongs to the DOM, and\n * this has no way to know it changed.\n */\nexport function useFormValues<T extends Record<string, unknown>>(\n store?: FormStore,\n): Partial<T> {\n const ctx = useContext(FormStoreContext);\n\n if (!ctx && !store) {\n throw new Error(\n \"useFormValues() was called outside a <Form>. It reads that form's values, so there has to be one above it.\",\n );\n }\n\n const source = store ?? ctx!.store;\n\n return useSyncExternalStore(\n source.subscribe,\n () => source.all() as Partial<T>,\n () => ({}) as Partial<T>,\n );\n}\n\nexport function useFormStatus<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(): FormRenderProps<T> {\n return useContext(FormStatusContext) as FormRenderProps<T>;\n}\n\n/**\n * submitForm bound to an action, once per action.\n *\n * Bound on every render, it was a fresh function with a fresh promise for\n * its bound arguments each time - and React, seating a posted form's state\n * during the server render, suspends on that promise until it settles, then\n * renders the component again, which bound again. A render that never\n * finished, on every page a form was posted to without javascript. One\n * bound function per action, and the promise settles once.\n */\nconst boundSubmits = new WeakMap<Function, (previous: unknown, formData: FormData) => Promise<unknown>>();\n\nfunction boundSubmit(action: (formData: FormData) => Promise<unknown>) {\n let bound = boundSubmits.get(action);\n\n if (!bound) {\n bound = submitForm.bind(null, action);\n boundSubmits.set(action, bound);\n }\n\n return bound;\n}\n\n/**\n * A form's values as one comparable string.\n *\n * FormData in document order, a file by its name and size: enough to say\n * whether anything changed, which is all `dirty` asks.\n */\nfunction serializeForm(form: HTMLFormElement): string {\n const parts: string[] = [];\n\n for (const [name, value] of new FormData(form)) {\n parts.push(\n name + \"=\" + (typeof value === \"string\" ? value : \"file:\" + value.name + \":\" + value.size),\n );\n }\n\n return parts.join(\"\\n\");\n}\n\n/**\n * Also exported by name, and re-exported below, because both spellings are in\n * use: `import Form from` and `import { Form } from`.\n */\nexport default function Form<\n T extends Record<string, unknown> = Record<string, unknown>,\n>({\n action,\n method: methodProp,\n defaultValues,\n store: providedStore,\n prefetch = \"hover\",\n cacheFor,\n replace = false,\n preserveScroll = false,\n resetOnSuccess = true,\n schema,\n transform,\n optimistic,\n onSuccess,\n onError,\n onSubmit,\n children,\n ref: callerRef,\n ...rest\n}: FormProps<T>) {\n const isGetForm = typeof action === \"string\";\n const method = methodProp ?? (isGetForm ? \"get\" : \"post\");\n\n // The answer to this form posted without a runtime, seated by React into\n // the form that posted it - which is the only way a visitor with no\n // javascript sees a refusal. Registered through submitForm, bound to the\n // action, because React hands a form-state action a (previous, formData)\n // pair and the action takes the FormData alone. On the server a plain\n // function - a closure, not a server reference - cannot be bound into a\n // form; the form falls back to the action itself, and posts nothing\n // seatable, which is all a closure could ever have done.\n const bindable =\n typeof action === \"function\" && (typeof window !== \"undefined\" || \"$$FORM_ACTION\" in action);\n const [posted, formAction] = useActionState(\n bindable ? boundSubmit(action as (formData: FormData) => Promise<unknown>) : async () => null,\n null as unknown,\n );\n const postedRefusal = resultOf(posted);\n const [errors, setErrors] = useState<Record<string, string[]>>(() => postedRefusal?.errors ?? {});\n const [touched, setTouched] = useState<Record<string, boolean>>({});\n\n /**\n * Mark a field visited, and check it.\n *\n * Checking on blur rather than on every keystroke, because an error that\n * appears while someone is halfway through typing an email address is a form\n * arguing with them. Leaving the field is the moment they have finished\n * saying what they meant.\n *\n * The whole object is validated and only this field's issues are kept: a\n * Standard Schema has no notion of one field, and filtering by path is the\n * honest way to ask it about one. Everything else's errors are left as they\n * were, so blurring an empty field does not light up the rest of the form.\n */\n const touch = useCallback(\n async (name: string) => {\n setTouched((prev) => (prev[name] ? prev : { ...prev, [name]: true }));\n\n if (!schema || !formRef.current) return;\n\n const invalid = await validateWith(\n schema,\n decodeFormData(new FormData(formRef.current), schema),\n );\n\n setErrors((prev) => {\n const next = { ...prev };\n\n if (invalid?.[name]) next[name] = invalid[name];\n else delete next[name];\n\n return next;\n });\n },\n [schema],\n );\n\n const [succeeded, setSucceeded] = useState(false);\n const [recentlySucceeded, setRecentlySucceeded] = useState(false);\n const recentTimer = useRef<ReturnType<typeof setTimeout> | undefined>(\n undefined,\n );\n\n // Cleared on unmount: a timer that fires into a component that has gone is\n // the warning nobody reads and the leak nobody finds.\n useEffect(() => () => clearTimeout(recentTimer.current), []);\n\n // Only the fields someone bound. Everything else is the DOM's.\n //\n // A store rather than state, so a component using `useField` can re-render\n // for one field while this one does not. See formStore.ts.\n const storeRef = useRef<FormStore | null>(null);\n\n storeRef.current ??= createFormStore({\n ...(defaultValues as Record<string, unknown> | undefined),\n });\n\n const store = providedStore ?? storeRef.current;\n\n // Which names the render prop read through `field()`. A change to one of\n // those has to re-render this component, because that is where the value is\n // being displayed; a change to anything else does not, which is what lets a\n // `useField` child stand on its own.\n const readHere = useRef(new Set<string>());\n const [, bump] = useState(0);\n\n useEffect(\n () =>\n store.subscribe((name) => {\n if (name === \"\" || readHere.current.has(name)) bump((n) => n + 1);\n }),\n [store],\n );\n const [currentData, setCurrentData] = useState<T>({} as T);\n const [isPending, startTransition] = useTransition();\n const formRef = useRef<HTMLFormElement>(null);\n\n // What the form started with, as its FormData reads: the baseline `dirty`\n // is measured against. Taken on mount, retaken after a successful submit.\n const baseline = useRef<string | null>(null);\n const [dirty, setDirty] = useState(false);\n // Only a form that reads `dirty` pays for it: the flip from clean to dirty\n // is a render of the whole form, and a field that subscribes for itself\n // is promised that typing in it renders it alone.\n const dirtyRead = useRef(false);\n\n const snapshot = useCallback(() => {\n if (!formRef.current) return;\n\n baseline.current = serializeForm(formRef.current);\n setDirty(false);\n }, []);\n\n const measureDirty = useCallback(() => {\n if (!dirtyRead.current || !formRef.current || baseline.current === null) return;\n\n const now = serializeForm(formRef.current) !== baseline.current;\n\n setDirty((was) => (was === now ? was : now));\n }, []);\n\n useEffect(snapshot, [snapshot]);\n\n // A bound control with no native element behind it changes through the\n // store, not through an input event - a Radix select - so the store is\n // watched too. Its input, if it has one, is read back with the rest. In\n // a microtask: the fields subscribed before this effect ran, so React's\n // own flush of their update is queued ahead of the measure, and the\n // element is read after it was written.\n useEffect(() => store.subscribe(() => queueMicrotask(measureDirty)), [store, measureDirty]);\n\n // A reset writes every bound field at once, through a render of this\n // component; the measure has to read the elements after that commit,\n // and a microtask queued during the reset ran before it. Marked here,\n // measured by the effect below once the render lands.\n const remeasure = useRef(false);\n\n useEffect(() => {\n if (!remeasure.current) return;\n\n remeasure.current = false;\n measureDirty();\n });\n\n // Both handles, one element. React 19 hands a function component its ref\n // as a prop, and spread after the form's own it replaced it - every\n // FormData read then found null. A caller's ref is filled beside ours.\n const attachForm = useCallback(\n (element: HTMLFormElement | null) => {\n formRef.current = element;\n\n if (typeof callerRef === \"function\") callerRef(element);\n else if (callerRef) callerRef.current = element;\n },\n [callerRef],\n );\n\n const error = useCallback(\n (field: keyof T & string): string | undefined => errors[field]?.[0],\n [errors],\n );\n\n const clearErrors = useCallback((...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 // The store first, then the DOM. A bound field renders from the store, so\n // a DOM reset alone put its default in the element for one frame and the\n // typed value back on the next render: a port's Discard left the name as\n // typed while the dirty flag went clean beside it.\n const resetForm = useCallback(() => {\n remeasure.current = true;\n store.reset();\n formRef.current?.reset();\n setErrors({});\n setCurrentData({} as T);\n queueMicrotask(measureDirty);\n }, [store, measureDirty]);\n\n useEffect(() => {\n if (isGetForm && prefetch === \"mount\") {\n const fn = (window as any).__rsc_prefetch;\n fn?.(action, cacheFor);\n }\n }, [isGetForm, prefetch, action, cacheFor]);\n\n const doPrefetch = useCallback(() => {\n if (!isGetForm) return;\n const fn = (window as any).__rsc_prefetch;\n fn?.(action as string, cacheFor);\n }, [isGetForm, action, cacheFor]);\n\n const handleSubmit = useCallback(\n async (e: FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n const formData = new FormData(e.currentTarget);\n const data = decodeFormData<T>(formData, schema);\n setCurrentData(data);\n\n if (onSubmit?.(formData) === false) {\n return;\n }\n\n if (isGetForm && method === \"get\") {\n const url = new URL(action as string, window.location.origin);\n for (const [key, value] of formData.entries()) {\n if (typeof value === \"string\" && value !== \"\") {\n url.searchParams.set(key, value);\n }\n }\n\n const path = url.pathname + url.search;\n const shell = action as string;\n const nav = (window as any).__rsc_navigate;\n const prefetched = (window as any).__rsc_is_prefetched;\n\n // The route without its query is that route's shell: the layout, the\n // chrome, and whatever it renders with nothing to show yet. Prefetching\n // on hover put it in the cache, so going there first costs no request\n // and puts the page on screen while the real query is still running.\n //\n // The query itself is never prefetched. It is the expensive half, and\n // hovering a search button is not a reason to run someone's search.\n if (path !== shell && prefetched?.(shell)) {\n // Awaited rather than raced: navigate() aborts whatever is in flight,\n // so starting the real one first would cancel the shell before it\n // could render. It is a cache hit, so this is a render, not a wait.\n Promise.resolve(nav?.(shell, { replace, preserveScroll })).then(() =>\n // Replaces, so the shell does not become a back-button stop of its\n // own — the pair leaves exactly one entry behind.\n nav?.(path, { replace: true, preserveScroll }),\n );\n\n return;\n }\n\n nav?.(path, { replace, preserveScroll });\n return;\n }\n\n const serverAction = action as (formData: FormData) => Promise<unknown>;\n\n // Rebuilt rather than edited: transform returns the values to send, so\n // whatever was in the form and is not in the result should not go.\n if (transform) {\n for (const key of [...formData.keys()]) formData.delete(key);\n\n for (const [key, value] of buildFormData(transform(data))) {\n formData.append(key, value);\n }\n }\n\n setErrors({});\n\n // Before the transition, so a failure neither runs the optimistic\n // update nor leaves the form looking like it is submitting.\n const invalid = await validateWith(schema, data);\n\n if (invalid) {\n setErrors(invalid);\n onError?.(invalid);\n\n return;\n }\n\n startTransition(async () => {\n try {\n // Call optimistic updater inside the transition so React's\n // useOptimistic picks it up and auto-reverts on settle.\n optimistic?.(data);\n\n const result = await serverAction(formData);\n const refused = resultOf(result);\n\n // The action answered with somewhere to go, and callServer has\n // already started the navigation. Not an error for a form to\n // show, and not a save to announce either: signing in and going\n // to the dashboard is the whole success.\n if (refused?.redirected !== undefined || redirectedTo() !== null) return;\n\n if (refused) {\n if (refused.errors) {\n setErrors(refused.errors);\n onError?.(refused.errors);\n } else {\n onError?.({}, new Error(refused.serverError));\n }\n\n return;\n }\n\n if (resetOnSuccess) {\n store.reset();\n formRef.current?.reset();\n setTouched({});\n setCurrentData({} as T);\n }\n\n // Saved: what is in the form now is what the server has.\n snapshot();\n\n setErrors({});\n setSucceeded(true);\n setRecentlySucceeded(true);\n\n clearTimeout(recentTimer.current);\n recentTimer.current = setTimeout(\n () => setRecentlySucceeded(false),\n 2_000,\n );\n\n onSuccess?.(result);\n } catch (err) {\n // A redirect no longer arrives here - callServer resolves after\n // performing it - but an action called through something older\n // than that still may.\n if (err instanceof ServerRedirectError) return;\n\n if (err instanceof ServerValidationError) {\n setErrors(err.errors);\n onError?.(err.errors);\n } else if (err instanceof ServerDumpError) {\n // Dump overlay is already shown — silently swallow\n } else if (onError) {\n // Not rethrown. A rejected action never settles, and until it\n // settles React keeps the optimistic update on screen — so an\n // unexpected failure left the row showing as though the write had\n // worked. Settling is what takes it back.\n onError({}, err);\n } else {\n // Nothing is handling it, and swallowing here would lose it\n // entirely — the reason this used to rethrow.\n console.error(\"[rsc-kit] form submit failed\", err);\n }\n }\n });\n },\n [\n action,\n isGetForm,\n method,\n replace,\n preserveScroll,\n resetOnSuccess,\n store,\n schema,\n transform,\n optimistic,\n onSubmit,\n onSuccess,\n onError,\n ],\n );\n\n const field = useCallback(\n <K extends keyof T & string>(name: K): FieldBinding<T[K]> => {\n // Recorded during render, deliberately: this is how the form knows which\n // values it is the one displaying. Idempotent, so a double render in\n // development records the same name twice and means the same thing.\n readHere.current.add(name);\n\n return {\n name,\n value: (store.get(name) ?? \"\") as T[K],\n onChange: (next: unknown) => {\n const value =\n typeof next === \"object\" && next !== null && \"target\" in next\n ? (next as { target: { value: string } }).target.value\n : next;\n\n store.set(name, value);\n },\n onBlur: () => void touch(name),\n };\n },\n [store, touch],\n );\n\n const fieldState = useCallback(\n (name: string): FieldState => ({\n touched: touched[name] === true,\n invalid: (errors[name]?.length ?? 0) > 0,\n errors: errors[name] ?? [],\n }),\n [touched, errors],\n );\n\n // Stable, so a subscriber below does not re-render because this one did.\n const storeContext = useMemo(() => ({ store, touch }), [store, touch]);\n\n const formStatus: FormRenderProps<T> = {\n pending: isPending,\n data: currentData,\n get dirty() {\n dirtyRead.current = true;\n\n return dirty;\n },\n succeeded,\n recentlySucceeded,\n field,\n fieldState,\n errors: errors as FormRenderProps<T>[\"errors\"],\n error,\n clearErrors,\n reset: resetForm,\n };\n\n return (\n <FormStoreContext.Provider value={storeContext}>\n <FormStatusContext.Provider value={formStatus as FormRenderProps}>\n <form\n ref={attachForm}\n // On the element as well as in the handler, which is what makes this\n // work before hydration. React emits a form a browser can submit on its\n // own for a server action, and an ordinary action/method pair for a\n // url — so a submit that happens before the javascript arrives still\n // reaches the server.\n //\n // The two do not fight: handleSubmit calls preventDefault() first, and\n // React does not run a form action when the submit event was cancelled.\n // So the enhanced path wins whenever there is one, and the native path\n // is what is left when there is not.\n action={(bindable ? formAction : action) as never}\n // Only for a url. React sets the method itself for a server action, and\n // passing one alongside is what it warns about.\n method={isGetForm ? method : undefined}\n onSubmit={handleSubmit}\n // Every keystroke and every toggle, from any control in the form,\n // uncontrolled ones included: React's onInput here is the bubbling\n // `input` event. A caller's own handler still runs.\n onInput={(event) => {\n rest.onInput?.(event);\n measureDirty();\n }}\n onChange={(event) => {\n rest.onChange?.(event);\n measureDirty();\n }}\n // On the form, not only on the bound fields. `focusout` bubbles where\n // `blur` does not, so React's onBlur here sees every control that was\n // left — including the uncontrolled ones, which are most of them and\n // would otherwise never be marked touched at all.\n onBlur={(event) => {\n const target = event.target as unknown as {\n name?: string;\n value?: string;\n };\n const name = target.name;\n\n if (!name) return;\n\n // Leaving a field is the moment to check it; leaving the form is\n // not the same moment. Focus going to nothing, or to something\n // outside the form, is a click on the page - most often a dialog\n // closing - and an error painted into a dialog as it animates out\n // reads as a submit that nobody made. An empty field left that way\n // is not checked at all. One with something in it is, but only\n // once the dust settles: if the form is gone or hidden by then, it\n // was a dialog closing, and nobody is there to read the error.\n const left = event.relatedTarget as Node | null;\n const stillInForm = !!left && !!formRef.current?.contains(left);\n\n if (stillInForm) {\n void touch(name);\n\n return;\n }\n\n if (!target.value) return;\n\n setTimeout(() => {\n const form = formRef.current;\n\n if (\n !form ||\n !form.isConnected ||\n form.getClientRects().length === 0\n )\n return;\n if (\n form.closest(\n \"[hidden], [aria-hidden='true'], [data-ending-style], [data-closed]\",\n )\n )\n return;\n\n void touch(name);\n }, LEAVE_FORM_SETTLE_MS);\n }}\n onMouseEnter={prefetch === \"hover\" ? doPrefetch : undefined}\n data-pending={isPending ? \"\" : undefined}\n {...rest}\n >\n {typeof children === \"function\" ? children(formStatus) : children}\n </form>\n </FormStatusContext.Provider>\n </FormStoreContext.Provider>\n );\n}\n\n// Also by name, because both spellings are in use: `import Form` and\n// `import { Form }`. This was a re-export of \"./Form\" — from inside Form.tsx,\n// so the module named itself. It survived on the bundler tolerating a circular\n// self-reference, left over from when a separate barrel re-exported a\n// FormComponent.tsx that a case-insensitive filesystem would not let be called\n// Form.ts. There is one file now, and it can just export what it declares.\nexport { Form };\n"]}
|
package/dist/js/Link.d.ts
CHANGED
|
@@ -28,5 +28,5 @@ interface LinkBaseProps<H extends Route> extends Omit<AnchorHTMLAttributes<HTMLA
|
|
|
28
28
|
* here. With no schema, any scalars. See SearchFor in routes.ts.
|
|
29
29
|
*/
|
|
30
30
|
type LinkProps<H extends Route> = LinkBaseProps<H> & SearchProp<H>;
|
|
31
|
-
export default function Link<H extends Route>({ href: path, search, prefetch: prefetchProp, cacheFor, replace, preserveScroll, children, onClick, onMouseEnter, onMouseLeave, ref: callerRef, ...rest }: LinkProps<H>): import("react").JSX.Element;
|
|
31
|
+
export default function Link<H extends Route>({ href: path, search, prefetch: prefetchProp, cacheFor, replace, preserveScroll, children, onClick, onMouseEnter, onMouseLeave, onMouseDown, ref: callerRef, ...rest }: LinkProps<H>): import("react").JSX.Element;
|
|
32
32
|
export {};
|
package/dist/js/Link.js
CHANGED
|
@@ -30,7 +30,7 @@ function shouldInterceptClick(e) {
|
|
|
30
30
|
* hover this short would not have had its payload back anyway.
|
|
31
31
|
*/
|
|
32
32
|
const HOVER_PREFETCH_DELAY_MS = 100;
|
|
33
|
-
export default function Link({ href: path, search, prefetch: prefetchProp = "hover", cacheFor, replace = false, preserveScroll = false, children, onClick, onMouseEnter, onMouseLeave, ref: callerRef, ...rest }) {
|
|
33
|
+
export default function Link({ href: path, search, prefetch: prefetchProp = "hover", cacheFor, replace = false, preserveScroll = false, children, onClick, onMouseEnter, onMouseLeave, onMouseDown, ref: callerRef, ...rest }) {
|
|
34
34
|
// The string the anchor and the router both use: the path, with the typed
|
|
35
35
|
// search params serialised onto it.
|
|
36
36
|
const href = (search ? withSearch(path, search) : path);
|
|
@@ -41,11 +41,14 @@ export default function Link({ href: path, search, prefetch: prefetchProp = "hov
|
|
|
41
41
|
: prefetchProp === false
|
|
42
42
|
? "none"
|
|
43
43
|
: prefetchProp;
|
|
44
|
-
|
|
44
|
+
// `intent` is a hover that settled or a touch: the payload is decoded as
|
|
45
|
+
// it lands, so the chunks the page names are loading before the click. A
|
|
46
|
+
// link that only came into view fetches the bytes and stops there.
|
|
47
|
+
const doPrefetch = useCallback((intent = false) => {
|
|
45
48
|
if (isExternalUrl(href))
|
|
46
49
|
return;
|
|
47
50
|
const fn = window.__rsc_prefetch;
|
|
48
|
-
fn?.(href, cacheFor);
|
|
51
|
+
fn?.(href, cacheFor, intent);
|
|
49
52
|
}, [href, cacheFor]);
|
|
50
53
|
// Only useEffect needed: prefetch on mount strategy
|
|
51
54
|
useEffect(() => {
|
|
@@ -53,13 +56,13 @@ export default function Link({ href: path, search, prefetch: prefetchProp = "hov
|
|
|
53
56
|
doPrefetch();
|
|
54
57
|
}
|
|
55
58
|
}, [prefetchStrategy, doPrefetch]);
|
|
56
|
-
//
|
|
57
|
-
//
|
|
59
|
+
// The link prefetches as it comes into view - the bytes, so a click finds
|
|
60
|
+
// them however quick it is. See viewportPrefetch.
|
|
58
61
|
const anchor = useRef(null);
|
|
59
62
|
useEffect(() => {
|
|
60
63
|
if (prefetchStrategy !== "hover")
|
|
61
64
|
return;
|
|
62
|
-
return prefetchWhenVisible(anchor.current, doPrefetch);
|
|
65
|
+
return prefetchWhenVisible(anchor.current, () => doPrefetch());
|
|
63
66
|
}, [prefetchStrategy, doPrefetch]);
|
|
64
67
|
const handleClick = useCallback((e) => {
|
|
65
68
|
onClick?.(e);
|
|
@@ -96,7 +99,7 @@ export default function Link({ href: path, search, prefetch: prefetchProp = "hov
|
|
|
96
99
|
clearTimeout(hoverTimer.current);
|
|
97
100
|
hoverTimer.current = setTimeout(() => {
|
|
98
101
|
hoverTimer.current = null;
|
|
99
|
-
doPrefetch();
|
|
102
|
+
doPrefetch(true);
|
|
100
103
|
}, HOVER_PREFETCH_DELAY_MS);
|
|
101
104
|
}, [prefetchStrategy, doPrefetch, onMouseEnter]);
|
|
102
105
|
const handleMouseLeave = useCallback((e) => {
|
|
@@ -115,11 +118,24 @@ export default function Link({ href: path, search, prefetch: prefetchProp = "hov
|
|
|
115
118
|
// Touch gets no delay. There is no hovering to disambiguate — a touch is
|
|
116
119
|
// already the start of a tap — and touchstart leads the click by little
|
|
117
120
|
// enough that spending any of it waiting would waste the head start.
|
|
121
|
+
//
|
|
122
|
+
// And it decodes: the viewport prefetch that came before it, if one did,
|
|
123
|
+
// fetched the bytes and left the chunks for the tap. This is the tap.
|
|
118
124
|
const handleTouchStart = useCallback(() => {
|
|
119
125
|
if (prefetchStrategy === "hover" || prefetchStrategy === "click") {
|
|
120
|
-
doPrefetch();
|
|
126
|
+
doPrefetch(true);
|
|
121
127
|
}
|
|
122
128
|
}, [prefetchStrategy, doPrefetch]);
|
|
129
|
+
// A press is a click 60-100 ms from landing, and a quick one comes before
|
|
130
|
+
// the hover has settled. Whatever the hover did not, this does; whatever
|
|
131
|
+
// it did, this finds done.
|
|
132
|
+
const handleMouseDown = useCallback((e) => {
|
|
133
|
+
onMouseDown?.(e);
|
|
134
|
+
if (e.button !== 0)
|
|
135
|
+
return;
|
|
136
|
+
if (prefetchStrategy === "hover" || prefetchStrategy === "click")
|
|
137
|
+
doPrefetch(true);
|
|
138
|
+
}, [prefetchStrategy, doPrefetch, onMouseDown]);
|
|
123
139
|
// A link unmounted mid-hover (navigating away) must not prefetch afterwards.
|
|
124
140
|
useEffect(() => () => {
|
|
125
141
|
if (hoverTimer.current !== null)
|
|
@@ -134,6 +150,6 @@ export default function Link({ href: path, search, prefetch: prefetchProp = "hov
|
|
|
134
150
|
callerRef(node);
|
|
135
151
|
else if (callerRef)
|
|
136
152
|
callerRef.current = node;
|
|
137
|
-
}, href: href, "data-rsc": "", onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onTouchStart: handleTouchStart, "data-pending": pending ? "" : undefined, ...rest, children: children }) }));
|
|
153
|
+
}, href: href, "data-rsc": "", onClick: handleClick, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, onTouchStart: handleTouchStart, onMouseDown: handleMouseDown, "data-pending": pending ? "" : undefined, ...rest, children: children }) }));
|
|
138
154
|
}
|
|
139
155
|
//# sourceMappingURL=Link.js.map
|
package/dist/js/Link.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/js/Link.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport { LinkStatusContext } from \"./useLinkStatus\";\nimport { prefetchWhenVisible } from \"./viewportPrefetch\";\nimport { type Route, type SearchProp, withSearch } from \"../routes.js\";\nimport {\n type AnchorHTMLAttributes,\n type MouseEvent,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\n\ntype PrefetchStrategy = \"hover\" | \"mount\" | \"click\" | \"none\" | boolean;\n\ninterface LinkBaseProps<H extends Route> extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\"\n> {\n /**\n * Where this goes. Typed to the routes the build found, so a link to a page\n * that does not exist stops compiling; `path as Route` when it is computed.\n */\n href: H;\n /**\n * The underlying anchor.\n *\n * Spread onto it like any other prop — React 19 passes ref through a\n * function component without forwardRef — but declared here because it is\n * not part of AnchorHTMLAttributes. A link that cannot be focused is a link\n * a dialog cannot move focus to when it opens.\n */\n ref?: Ref<HTMLAnchorElement>;\n prefetch?: PrefetchStrategy;\n cacheFor?: number;\n replace?: boolean;\n preserveScroll?: boolean;\n}\n\n/**\n * `search` is typed to the page's own `searchParams` schema when it exports\n * one — the same schema the page parses with, so a key it never reads or a\n * number written as text does not compile, and a key it requires is required\n * here. With no schema, any scalars. See SearchFor in routes.ts.\n */\ntype LinkProps<H extends Route> = LinkBaseProps<H> & SearchProp<H>;\n\nfunction isExternalUrl(url: string): boolean {\n try {\n return (\n new URL(url, window.location.origin).origin !== window.location.origin\n );\n } catch {\n return false;\n }\n}\n\nfunction shouldInterceptClick(e: MouseEvent<HTMLAnchorElement>): boolean {\n return (\n !e.defaultPrevented &&\n e.button === 0 &&\n !e.metaKey &&\n !e.ctrlKey &&\n !e.shiftKey &&\n !e.altKey\n );\n}\n\n/**\n * How long a pointer has to settle on a link before it prefetches.\n *\n * A pointer crossing a nav bar enters every link on the way, and each one used\n * to fire a request immediately — enough to fill the browser's per-origin\n * connection limit with pages the user never meant to visit. Waiting is close\n * to free: the prefetch only has to beat the click, and a click that follows a\n * hover this short would not have had its payload back anyway.\n */\nconst HOVER_PREFETCH_DELAY_MS = 100;\n\nexport default function Link<H extends Route>({\n href: path,\n search,\n prefetch: prefetchProp = \"hover\",\n cacheFor,\n replace = false,\n preserveScroll = false,\n children,\n onClick,\n onMouseEnter,\n onMouseLeave,\n ref: callerRef,\n ...rest\n}: LinkProps<H>) {\n // The string the anchor and the router both use: the path, with the typed\n // search params serialised onto it.\n const href = (search ? withSearch(path, search as object) : path) as Route;\n const [pending, setPending] = useState(false);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const prefetchStrategy =\n prefetchProp === true\n ? \"hover\"\n : prefetchProp === false\n ? \"none\"\n : prefetchProp;\n\n const doPrefetch = useCallback(() => {\n if (isExternalUrl(href)) return;\n const fn = (window as any).__rsc_prefetch;\n fn?.(href, cacheFor);\n }, [href, cacheFor]);\n\n // Only useEffect needed: prefetch on mount strategy\n useEffect(() => {\n if (prefetchStrategy === \"mount\") {\n doPrefetch();\n }\n }, [prefetchStrategy, doPrefetch]);\n\n // Where nothing can hover, the link prefetches as it comes into view -\n // the head start a touch gives is a round trip short. See viewportPrefetch.\n const anchor = useRef<HTMLAnchorElement | null>(null);\n\n useEffect(() => {\n if (prefetchStrategy !== \"hover\") return;\n\n return prefetchWhenVisible(anchor.current, doPrefetch);\n }, [prefetchStrategy, doPrefetch]);\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onClick?.(e);\n\n if (e.defaultPrevented) return;\n\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== \"_self\") return;\n if (!shouldInterceptClick(e) || isExternalUrl(href)) return;\n\n // Hash-only links (#section) — let the browser scroll natively\n if (href.startsWith(\"#\")) return;\n\n e.preventDefault();\n setPending(true);\n\n // A hover that had not yet prefetched: the click is the navigation, and\n // a prefetch of the page being left for, firing after it, is a request\n // for nothing - seen as a stray 204 for a guarded link after landing\n // on the login page.\n if (hoverTimer.current !== null) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = null;\n }\n\n // navigate() returns a Promise — clear pending when it resolves or rejects\n const nav = (window as any).__rsc_navigate;\n const promise = nav?.(href, { replace, preserveScroll });\n promise?.then(\n () => setPending(false),\n () => setPending(false),\n );\n },\n [href, replace, preserveScroll, onClick],\n );\n\n const handleMouseEnter = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onMouseEnter?.(e);\n\n if (prefetchStrategy !== \"hover\" && prefetchStrategy !== \"click\") return;\n\n if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);\n\n hoverTimer.current = setTimeout(() => {\n hoverTimer.current = null;\n doPrefetch();\n }, HOVER_PREFETCH_DELAY_MS);\n },\n [prefetchStrategy, doPrefetch, onMouseEnter],\n );\n\n const handleMouseLeave = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onMouseLeave?.(e);\n\n // Never fired: the pointer passed over on its way somewhere else.\n if (hoverTimer.current !== null) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = null;\n }\n\n // Already in flight: give the connection back to whatever the pointer is\n // heading for. A prefetch that has landed is kept — see cancelPrefetch.\n if (isExternalUrl(href)) return;\n (window as any).__rsc_cancel_prefetch?.(href);\n },\n [href, onMouseLeave],\n );\n\n // Touch gets no delay. There is no hovering to disambiguate — a touch is\n // already the start of a tap — and touchstart leads the click by little\n // enough that spending any of it waiting would waste the head start.\n const handleTouchStart = useCallback(() => {\n if (prefetchStrategy === \"hover\" || prefetchStrategy === \"click\") {\n doPrefetch();\n }\n }, [prefetchStrategy, doPrefetch]);\n\n // A link unmounted mid-hover (navigating away) must not prefetch afterwards.\n useEffect(\n () => () => {\n if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);\n },\n [],\n );\n\n return (\n <LinkStatusContext.Provider value={{ pending }}>\n <a\n // The caller's ref still fills: a dialog moving focus to its link\n // needs the element as much as the observer above does.\n ref={(node) => {\n anchor.current = node;\n\n if (typeof callerRef === \"function\") callerRef(node);\n else if (callerRef) (callerRef as { current: HTMLAnchorElement | null }).current = node;\n }}\n href={href}\n // The mark the bootstrap script's click listener looks for: a tap\n // on this before the router exists is held for it. See earlyClicks.\n data-rsc=\"\"\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onTouchStart={handleTouchStart}\n data-pending={pending ? \"\" : undefined}\n {...rest}\n >\n {children}\n </a>\n </LinkStatusContext.Provider>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"Link.js","sourceRoot":"","sources":["../../src/js/Link.tsx"],"names":[],"mappings":"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","sourcesContent":["\"use client\";\n\nimport { LinkStatusContext } from \"./useLinkStatus\";\nimport { prefetchWhenVisible } from \"./viewportPrefetch\";\nimport { type Route, type SearchProp, withSearch } from \"../routes.js\";\nimport {\n type AnchorHTMLAttributes,\n type MouseEvent,\n type Ref,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\n\ntype PrefetchStrategy = \"hover\" | \"mount\" | \"click\" | \"none\" | boolean;\n\ninterface LinkBaseProps<H extends Route> extends Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\"\n> {\n /**\n * Where this goes. Typed to the routes the build found, so a link to a page\n * that does not exist stops compiling; `path as Route` when it is computed.\n */\n href: H;\n /**\n * The underlying anchor.\n *\n * Spread onto it like any other prop — React 19 passes ref through a\n * function component without forwardRef — but declared here because it is\n * not part of AnchorHTMLAttributes. A link that cannot be focused is a link\n * a dialog cannot move focus to when it opens.\n */\n ref?: Ref<HTMLAnchorElement>;\n prefetch?: PrefetchStrategy;\n cacheFor?: number;\n replace?: boolean;\n preserveScroll?: boolean;\n}\n\n/**\n * `search` is typed to the page's own `searchParams` schema when it exports\n * one — the same schema the page parses with, so a key it never reads or a\n * number written as text does not compile, and a key it requires is required\n * here. With no schema, any scalars. See SearchFor in routes.ts.\n */\ntype LinkProps<H extends Route> = LinkBaseProps<H> & SearchProp<H>;\n\nfunction isExternalUrl(url: string): boolean {\n try {\n return (\n new URL(url, window.location.origin).origin !== window.location.origin\n );\n } catch {\n return false;\n }\n}\n\nfunction shouldInterceptClick(e: MouseEvent<HTMLAnchorElement>): boolean {\n return (\n !e.defaultPrevented &&\n e.button === 0 &&\n !e.metaKey &&\n !e.ctrlKey &&\n !e.shiftKey &&\n !e.altKey\n );\n}\n\n/**\n * How long a pointer has to settle on a link before it prefetches.\n *\n * A pointer crossing a nav bar enters every link on the way, and each one used\n * to fire a request immediately — enough to fill the browser's per-origin\n * connection limit with pages the user never meant to visit. Waiting is close\n * to free: the prefetch only has to beat the click, and a click that follows a\n * hover this short would not have had its payload back anyway.\n */\nconst HOVER_PREFETCH_DELAY_MS = 100;\n\nexport default function Link<H extends Route>({\n href: path,\n search,\n prefetch: prefetchProp = \"hover\",\n cacheFor,\n replace = false,\n preserveScroll = false,\n children,\n onClick,\n onMouseEnter,\n onMouseLeave,\n onMouseDown,\n ref: callerRef,\n ...rest\n}: LinkProps<H>) {\n // The string the anchor and the router both use: the path, with the typed\n // search params serialised onto it.\n const href = (search ? withSearch(path, search as object) : path) as Route;\n const [pending, setPending] = useState(false);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const prefetchStrategy =\n prefetchProp === true\n ? \"hover\"\n : prefetchProp === false\n ? \"none\"\n : prefetchProp;\n\n // `intent` is a hover that settled or a touch: the payload is decoded as\n // it lands, so the chunks the page names are loading before the click. A\n // link that only came into view fetches the bytes and stops there.\n const doPrefetch = useCallback(\n (intent = false) => {\n if (isExternalUrl(href)) return;\n const fn = (window as any).__rsc_prefetch;\n fn?.(href, cacheFor, intent);\n },\n [href, cacheFor],\n );\n\n // Only useEffect needed: prefetch on mount strategy\n useEffect(() => {\n if (prefetchStrategy === \"mount\") {\n doPrefetch();\n }\n }, [prefetchStrategy, doPrefetch]);\n\n // The link prefetches as it comes into view - the bytes, so a click finds\n // them however quick it is. See viewportPrefetch.\n const anchor = useRef<HTMLAnchorElement | null>(null);\n\n useEffect(() => {\n if (prefetchStrategy !== \"hover\") return;\n\n return prefetchWhenVisible(anchor.current, () => doPrefetch());\n }, [prefetchStrategy, doPrefetch]);\n\n const handleClick = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onClick?.(e);\n\n if (e.defaultPrevented) return;\n\n const target = (e.currentTarget as HTMLAnchorElement).target;\n if (target && target !== \"_self\") return;\n if (!shouldInterceptClick(e) || isExternalUrl(href)) return;\n\n // Hash-only links (#section) — let the browser scroll natively\n if (href.startsWith(\"#\")) return;\n\n e.preventDefault();\n setPending(true);\n\n // A hover that had not yet prefetched: the click is the navigation, and\n // a prefetch of the page being left for, firing after it, is a request\n // for nothing - seen as a stray 204 for a guarded link after landing\n // on the login page.\n if (hoverTimer.current !== null) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = null;\n }\n\n // navigate() returns a Promise — clear pending when it resolves or rejects\n const nav = (window as any).__rsc_navigate;\n const promise = nav?.(href, { replace, preserveScroll });\n promise?.then(\n () => setPending(false),\n () => setPending(false),\n );\n },\n [href, replace, preserveScroll, onClick],\n );\n\n const handleMouseEnter = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onMouseEnter?.(e);\n\n if (prefetchStrategy !== \"hover\" && prefetchStrategy !== \"click\") return;\n\n if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);\n\n hoverTimer.current = setTimeout(() => {\n hoverTimer.current = null;\n doPrefetch(true);\n }, HOVER_PREFETCH_DELAY_MS);\n },\n [prefetchStrategy, doPrefetch, onMouseEnter],\n );\n\n const handleMouseLeave = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onMouseLeave?.(e);\n\n // Never fired: the pointer passed over on its way somewhere else.\n if (hoverTimer.current !== null) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = null;\n }\n\n // Already in flight: give the connection back to whatever the pointer is\n // heading for. A prefetch that has landed is kept — see cancelPrefetch.\n if (isExternalUrl(href)) return;\n (window as any).__rsc_cancel_prefetch?.(href);\n },\n [href, onMouseLeave],\n );\n\n // Touch gets no delay. There is no hovering to disambiguate — a touch is\n // already the start of a tap — and touchstart leads the click by little\n // enough that spending any of it waiting would waste the head start.\n //\n // And it decodes: the viewport prefetch that came before it, if one did,\n // fetched the bytes and left the chunks for the tap. This is the tap.\n const handleTouchStart = useCallback(() => {\n if (prefetchStrategy === \"hover\" || prefetchStrategy === \"click\") {\n doPrefetch(true);\n }\n }, [prefetchStrategy, doPrefetch]);\n\n // A press is a click 60-100 ms from landing, and a quick one comes before\n // the hover has settled. Whatever the hover did not, this does; whatever\n // it did, this finds done.\n const handleMouseDown = useCallback(\n (e: MouseEvent<HTMLAnchorElement>) => {\n onMouseDown?.(e);\n\n if (e.button !== 0) return;\n if (prefetchStrategy === \"hover\" || prefetchStrategy === \"click\") doPrefetch(true);\n },\n [prefetchStrategy, doPrefetch, onMouseDown],\n );\n\n // A link unmounted mid-hover (navigating away) must not prefetch afterwards.\n useEffect(\n () => () => {\n if (hoverTimer.current !== null) clearTimeout(hoverTimer.current);\n },\n [],\n );\n\n return (\n <LinkStatusContext.Provider value={{ pending }}>\n <a\n // The caller's ref still fills: a dialog moving focus to its link\n // needs the element as much as the observer above does.\n ref={(node) => {\n anchor.current = node;\n\n if (typeof callerRef === \"function\") callerRef(node);\n else if (callerRef) (callerRef as { current: HTMLAnchorElement | null }).current = node;\n }}\n href={href}\n // The mark the bootstrap script's click listener looks for: a tap\n // on this before the router exists is held for it. See earlyClicks.\n data-rsc=\"\"\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onTouchStart={handleTouchStart}\n onMouseDown={handleMouseDown}\n data-pending={pending ? \"\" : undefined}\n {...rest}\n >\n {children}\n </a>\n </LinkStatusContext.Provider>\n );\n}\n"]}
|