@rsc-kit/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/SECURITY.md +60 -0
- package/dist/action.d.ts +90 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +137 -0
- package/dist/action.js.map +1 -0
- package/dist/build-rsc-vite.d.ts +39 -0
- package/dist/build-rsc-vite.d.ts.map +1 -0
- package/dist/build-rsc-vite.js +172 -0
- package/dist/build-rsc-vite.js.map +1 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +108 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +85 -0
- package/dist/cli.js.map +1 -0
- package/dist/compile.d.ts +55 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +141 -0
- package/dist/compile.js.map +1 -0
- package/dist/devServer.d.ts +18 -0
- package/dist/devServer.d.ts.map +1 -0
- package/dist/devServer.js +70 -0
- package/dist/devServer.js.map +1 -0
- package/dist/devUrls.d.ts +11 -0
- package/dist/devUrls.d.ts.map +1 -0
- package/dist/devUrls.js +68 -0
- package/dist/devUrls.js.map +1 -0
- package/dist/embed.d.ts +21 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +119 -0
- package/dist/embed.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +38 -0
- package/dist/errors.js.map +1 -0
- package/dist/export.d.ts +41 -0
- package/dist/export.d.ts.map +1 -0
- package/dist/export.js +100 -0
- package/dist/export.js.map +1 -0
- package/dist/files.d.ts +39 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +146 -0
- package/dist/files.js.map +1 -0
- package/dist/headers.d.ts +81 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +96 -0
- package/dist/headers.js.map +1 -0
- package/dist/host.d.ts +86 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +590 -0
- package/dist/host.js.map +1 -0
- package/dist/js/ActivityRouter.d.ts +47 -0
- package/dist/js/ActivityRouter.d.ts.map +1 -0
- package/dist/js/ActivityRouter.js +87 -0
- package/dist/js/ActivityRouter.js.map +1 -0
- package/dist/js/DocumentTitle.d.ts +22 -0
- package/dist/js/DocumentTitle.d.ts.map +1 -0
- package/dist/js/DocumentTitle.js +28 -0
- package/dist/js/DocumentTitle.js.map +1 -0
- package/dist/js/Form.d.ts +54 -0
- package/dist/js/Form.d.ts.map +1 -0
- package/dist/js/Form.js +225 -0
- package/dist/js/Form.js.map +1 -0
- package/dist/js/Link.d.ts +29 -0
- package/dist/js/Link.d.ts.map +1 -0
- package/dist/js/Link.js +112 -0
- package/dist/js/Link.js.map +1 -0
- package/dist/js/PathnameProvider.d.ts +8 -0
- package/dist/js/PathnameProvider.d.ts.map +1 -0
- package/dist/js/PathnameProvider.js +24 -0
- package/dist/js/PathnameProvider.js.map +1 -0
- package/dist/js/RedirectBoundary.d.ts +18 -0
- package/dist/js/RedirectBoundary.d.ts.map +1 -0
- package/dist/js/RedirectBoundary.js +34 -0
- package/dist/js/RedirectBoundary.js.map +1 -0
- package/dist/js/SegmentBoundary.d.ts +8 -0
- package/dist/js/SegmentBoundary.d.ts.map +1 -0
- package/dist/js/SegmentBoundary.js +38 -0
- package/dist/js/SegmentBoundary.js.map +1 -0
- package/dist/js/SlotBoundary.d.ts +15 -0
- package/dist/js/SlotBoundary.d.ts.map +1 -0
- package/dist/js/SlotBoundary.js +17 -0
- package/dist/js/SlotBoundary.js.map +1 -0
- package/dist/js/createViteRscApp.d.ts +8 -0
- package/dist/js/createViteRscApp.d.ts.map +1 -0
- package/dist/js/createViteRscApp.js +185 -0
- package/dist/js/createViteRscApp.js.map +1 -0
- package/dist/js/errors.d.ts +51 -0
- package/dist/js/errors.d.ts.map +1 -0
- package/dist/js/errors.js +93 -0
- package/dist/js/errors.js.map +1 -0
- package/dist/js/navigate.d.ts +118 -0
- package/dist/js/navigate.d.ts.map +1 -0
- package/dist/js/navigate.js +780 -0
- package/dist/js/navigate.js.map +1 -0
- package/dist/js/onlineStore.d.ts +23 -0
- package/dist/js/onlineStore.d.ts.map +1 -0
- package/dist/js/onlineStore.js +42 -0
- package/dist/js/onlineStore.js.map +1 -0
- package/dist/js/pagePayload.d.ts +15 -0
- package/dist/js/pagePayload.d.ts.map +1 -0
- package/dist/js/pagePayload.js +39 -0
- package/dist/js/pagePayload.js.map +1 -0
- package/dist/js/router.d.ts +29 -0
- package/dist/js/router.d.ts.map +1 -0
- package/dist/js/router.js +40 -0
- package/dist/js/router.js.map +1 -0
- package/dist/js/section.d.ts +16 -0
- package/dist/js/section.d.ts.map +1 -0
- package/dist/js/section.js +55 -0
- package/dist/js/section.js.map +1 -0
- package/dist/js/segmentStore.d.ts +69 -0
- package/dist/js/segmentStore.d.ts.map +1 -0
- package/dist/js/segmentStore.js +127 -0
- package/dist/js/segmentStore.js.map +1 -0
- package/dist/js/slotStore.d.ts +35 -0
- package/dist/js/slotStore.d.ts.map +1 -0
- package/dist/js/slotStore.js +46 -0
- package/dist/js/slotStore.js.map +1 -0
- package/dist/js/standardSchema.d.ts +47 -0
- package/dist/js/standardSchema.d.ts.map +1 -0
- package/dist/js/standardSchema.js +41 -0
- package/dist/js/standardSchema.js.map +1 -0
- package/dist/js/useForm.d.ts +43 -0
- package/dist/js/useForm.d.ts.map +1 -0
- package/dist/js/useForm.js +164 -0
- package/dist/js/useForm.js.map +1 -0
- package/dist/js/useLinkStatus.d.ts +2 -0
- package/dist/js/useLinkStatus.d.ts.map +1 -0
- package/dist/js/useLinkStatus.js +3 -0
- package/dist/js/useLinkStatus.js.map +1 -0
- package/dist/js/useOnline.d.ts +8 -0
- package/dist/js/useOnline.d.ts.map +1 -0
- package/dist/js/useOnline.js +20 -0
- package/dist/js/useOnline.js.map +1 -0
- package/dist/js/usePathname.d.ts +9 -0
- package/dist/js/usePathname.d.ts.map +1 -0
- package/dist/js/usePathname.js +32 -0
- package/dist/js/usePathname.js.map +1 -0
- package/dist/js/useSearchParams.d.ts +2 -0
- package/dist/js/useSearchParams.d.ts.map +1 -0
- package/dist/js/useSearchParams.js +60 -0
- package/dist/js/useSearchParams.js.map +1 -0
- package/dist/manifest.d.ts +66 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +13 -0
- package/dist/manifest.js.map +1 -0
- package/dist/prerender.d.ts +138 -0
- package/dist/prerender.d.ts.map +1 -0
- package/dist/prerender.js +387 -0
- package/dist/prerender.js.map +1 -0
- package/dist/redirect.d.ts +34 -0
- package/dist/redirect.d.ts.map +1 -0
- package/dist/redirect.js +108 -0
- package/dist/redirect.js.map +1 -0
- package/dist/redirectDigest.d.ts +20 -0
- package/dist/redirectDigest.d.ts.map +1 -0
- package/dist/redirectDigest.js +66 -0
- package/dist/redirectDigest.js.map +1 -0
- package/dist/request.d.ts +93 -0
- package/dist/request.d.ts.map +1 -0
- package/dist/request.js +308 -0
- package/dist/request.js.map +1 -0
- package/dist/revalidate.d.ts +43 -0
- package/dist/revalidate.d.ts.map +1 -0
- package/dist/revalidate.js +99 -0
- package/dist/revalidate.js.map +1 -0
- package/dist/routes.d.ts +37 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +28 -0
- package/dist/routes.js.map +1 -0
- package/dist/routing.d.ts +52 -0
- package/dist/routing.d.ts.map +1 -0
- package/dist/routing.js +146 -0
- package/dist/routing.js.map +1 -0
- package/dist/runtime.d.ts +36 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +86 -0
- package/dist/runtime.js.map +1 -0
- package/dist/safeUrl.d.ts +10 -0
- package/dist/safeUrl.d.ts.map +1 -0
- package/dist/safeUrl.js +40 -0
- package/dist/safeUrl.js.map +1 -0
- package/dist/streaming.d.ts +62 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/streaming.js +108 -0
- package/dist/streaming.js.map +1 -0
- package/dist/types.d.ts +82 -0
- package/dist/vite.d.ts +109 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +1909 -0
- package/dist/vite.js.map +1 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +863 -0
- package/dist/worker.js.map +1 -0
- package/package.json +223 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slotStore.js","sourceRoot":"","sources":["../../src/js/slotStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAeH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAqB,CAAA;AAC1C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAA;AAElD,kFAAkF;AAClF,MAAM,KAAK,GAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAA;AAE5C,SAAS,MAAM,CAAC,IAAY;IAC1B,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAA;AACxD,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,IAAU;IAC9C,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IACzB,MAAM,CAAC,IAAI,CAAC,CAAA;AACd,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,QAAkB;IAC9D,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,EAAY,CAAA;IAE3D,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACtB,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAE7B,OAAO,GAAG,EAAE;QACV,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC3B,CAAC,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAA;AACjC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,KAAK,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;IAE/B,KAAK,CAAC,KAAK,EAAE,CAAA;IACb,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;AACvB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validating a form before it is submitted, with whatever library you use.
|
|
3
|
+
*
|
|
4
|
+
* Not with an adapter per library: Standard Schema is the contract Zod,
|
|
5
|
+
* Valibot, ArkType and others already implement, so this speaks to the schema
|
|
6
|
+
* itself and never imports one. The types below are the spec, restated here
|
|
7
|
+
* rather than depended on — it is an interface, and a package for it would be
|
|
8
|
+
* a dependency that ships nothing.
|
|
9
|
+
*
|
|
10
|
+
* Validation on the client is a courtesy, never a control. The server still
|
|
11
|
+
* has to check: a form is one way to reach an action, and the action is a
|
|
12
|
+
* public endpoint reachable without it.
|
|
13
|
+
*/
|
|
14
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
15
|
+
readonly '~standard': {
|
|
16
|
+
readonly version: 1;
|
|
17
|
+
readonly vendor: string;
|
|
18
|
+
readonly validate: (value: unknown) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
|
|
19
|
+
readonly types?: {
|
|
20
|
+
readonly input: Input;
|
|
21
|
+
readonly output: Output;
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
interface StandardSchemaResult<Output> {
|
|
26
|
+
readonly value?: Output;
|
|
27
|
+
readonly issues?: ReadonlyArray<{
|
|
28
|
+
readonly message: string;
|
|
29
|
+
readonly path?: ReadonlyArray<PropertyKey | {
|
|
30
|
+
readonly key: PropertyKey;
|
|
31
|
+
}>;
|
|
32
|
+
}>;
|
|
33
|
+
}
|
|
34
|
+
/** Issues grouped by field, in the shape the form already reports. */
|
|
35
|
+
export declare function issuesToErrors(issues: NonNullable<StandardSchemaResult<unknown>['issues']>): Record<string, string[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Run a schema, or nothing if there is not one.
|
|
38
|
+
*
|
|
39
|
+
* Returns the errors to show, or null when the value is acceptable. The parsed
|
|
40
|
+
* value is deliberately discarded: what gets submitted is the FormData the
|
|
41
|
+
* browser built, and quietly sending something else — a coerced number, a
|
|
42
|
+
* trimmed string — would mean the action receives what the schema decided
|
|
43
|
+
* rather than what the field said.
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateWith(schema: StandardSchemaV1 | undefined, value: unknown): Promise<Record<string, string[]> | null>;
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=standardSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standardSchema.d.ts","sourceRoot":"","sources":["../../src/js/standardSchema.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,GAAG,KAAK;IAC/D,QAAQ,CAAC,WAAW,EAAE;QACpB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAA;QACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,QAAQ,EAAE,CACjB,KAAK,EAAE,OAAO,KACX,oBAAoB,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,CAAA;QACzE,QAAQ,CAAC,KAAK,CAAC,EAAE;YAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;YAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KACpE,CAAA;CACF;AAED,UAAU,oBAAoB,CAAC,MAAM;IACnC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;QAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;QACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC,WAAW,GAAG;YAAE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAA;SAAE,CAAC,CAAA;KAC3E,CAAC,CAAA;CACH;AAkBD,sEAAsE;AACtE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,GAC3D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAU1B;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,gBAAgB,GAAG,SAAS,EACpC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAM1C"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* The field an issue belongs to.
|
|
4
|
+
*
|
|
5
|
+
* Segments are joined with dots so a nested field reads the way it was named —
|
|
6
|
+
* `address.city`, not `address`. An issue with no path is about the form
|
|
7
|
+
* rather than any field, and goes under the empty string, which is where a
|
|
8
|
+
* component that wants to render it can look.
|
|
9
|
+
*/
|
|
10
|
+
function fieldOf(path) {
|
|
11
|
+
if (!path || path.length === 0)
|
|
12
|
+
return '';
|
|
13
|
+
return path
|
|
14
|
+
.map((segment) => (typeof segment === 'object' && segment !== null ? segment.key : segment))
|
|
15
|
+
.join('.');
|
|
16
|
+
}
|
|
17
|
+
/** Issues grouped by field, in the shape the form already reports. */
|
|
18
|
+
export function issuesToErrors(issues) {
|
|
19
|
+
const errors = {};
|
|
20
|
+
for (const issue of issues) {
|
|
21
|
+
const field = fieldOf(issue.path);
|
|
22
|
+
(errors[field] ??= []).push(issue.message);
|
|
23
|
+
}
|
|
24
|
+
return errors;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Run a schema, or nothing if there is not one.
|
|
28
|
+
*
|
|
29
|
+
* Returns the errors to show, or null when the value is acceptable. The parsed
|
|
30
|
+
* value is deliberately discarded: what gets submitted is the FormData the
|
|
31
|
+
* browser built, and quietly sending something else — a coerced number, a
|
|
32
|
+
* trimmed string — would mean the action receives what the schema decided
|
|
33
|
+
* rather than what the field said.
|
|
34
|
+
*/
|
|
35
|
+
export async function validateWith(schema, value) {
|
|
36
|
+
if (!schema)
|
|
37
|
+
return null;
|
|
38
|
+
const result = await schema['~standard'].validate(value);
|
|
39
|
+
return result.issues && result.issues.length > 0 ? issuesToErrors(result.issues) : null;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=standardSchema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"standardSchema.js","sourceRoot":"","sources":["../../src/js/standardSchema.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAmCb;;;;;;;GAOG;AACH,SAAS,OAAO,CAAC,IAA8I;IAC7J,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAEzC,OAAO,IAAI;SACR,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;SAC3F,IAAI,CAAC,GAAG,CAAC,CAAA;AACd,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,cAAc,CAC5B,MAA4D;IAE5D,MAAM,MAAM,GAA6B,EAAE,CAAA;IAE3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAEhC;QAAA,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAC7C,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAoC,EACpC,KAAc;IAEd,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;IAExD,OAAO,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACzF,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "./standardSchema";
|
|
2
|
+
type SetDataFn<T> = {
|
|
3
|
+
<K extends keyof T>(field: K, value: T[K]): void;
|
|
4
|
+
(values: Partial<T>): void;
|
|
5
|
+
};
|
|
6
|
+
export interface UseFormReturn<T extends Record<string, unknown>> {
|
|
7
|
+
data: T;
|
|
8
|
+
setData: SetDataFn<T>;
|
|
9
|
+
errors: Partial<Record<keyof T & string, string[]>>;
|
|
10
|
+
error: (field: keyof T & string) => string | undefined;
|
|
11
|
+
hasErrors: boolean;
|
|
12
|
+
pending: boolean;
|
|
13
|
+
processing: boolean;
|
|
14
|
+
wasSuccessful: boolean;
|
|
15
|
+
recentlySuccessful: boolean;
|
|
16
|
+
clearErrors: (...fields: (keyof T & string)[]) => void;
|
|
17
|
+
reset: (...fields: (keyof T)[]) => void;
|
|
18
|
+
setDefaults: (values?: Partial<T>) => void;
|
|
19
|
+
transform: (fn: (data: T) => Record<string, unknown>) => void;
|
|
20
|
+
submit: (action: (formData: FormData) => Promise<unknown>, optimistic?: () => void) => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Serialize form state into FormData for a server action.
|
|
24
|
+
*
|
|
25
|
+
* Exported for testing: this is the contract between useForm and the action —
|
|
26
|
+
* booleans become "1"/"0" so PHP sees something truthy, arrays repeat under
|
|
27
|
+
* `key[]`, Files pass through untouched for native uploads, and null/undefined
|
|
28
|
+
* are dropped rather than sent as the string "null".
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildFormData(data: Record<string, unknown>): FormData;
|
|
31
|
+
export declare function useForm<T extends Record<string, unknown>>(initialValues: T, options?: {
|
|
32
|
+
/**
|
|
33
|
+
* Check the fields before submitting, with any Standard Schema — Zod,
|
|
34
|
+
* Valibot, ArkType.
|
|
35
|
+
*
|
|
36
|
+
* A failure fills `errors` and the action is never called. A courtesy, not
|
|
37
|
+
* a control: the action is reachable without this form, so the server
|
|
38
|
+
* still has to check.
|
|
39
|
+
*/
|
|
40
|
+
schema?: StandardSchemaV1;
|
|
41
|
+
}): UseFormReturn<T>;
|
|
42
|
+
export {};
|
|
43
|
+
//# sourceMappingURL=useForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useForm.d.ts","sourceRoot":"","sources":["../../src/js/useForm.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,KAAK,SAAS,CAAC,CAAC,IAAI;IAClB,CAAC,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACjD,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9D,IAAI,EAAE,CAAC,CAAC;IACR,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IACpD,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACvD,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC;IACvD,KAAK,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;IACxC,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAC3C,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAC9D,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,EAAE,MAAM,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtG;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,QAAQ,CAqBrE;AAED,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvD,aAAa,EAAE,CAAC,EAChB,OAAO,GAAE;IACP;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAA;CACrB,GACL,aAAa,CAAC,CAAC,CAAC,CAuKlB"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useRef, useCallback, useTransition } from "react";
|
|
3
|
+
import { ServerValidationError, ServerDumpError } from "./errors";
|
|
4
|
+
import { validateWith } from "./standardSchema";
|
|
5
|
+
/**
|
|
6
|
+
* Serialize form state into FormData for a server action.
|
|
7
|
+
*
|
|
8
|
+
* Exported for testing: this is the contract between useForm and the action —
|
|
9
|
+
* booleans become "1"/"0" so PHP sees something truthy, arrays repeat under
|
|
10
|
+
* `key[]`, Files pass through untouched for native uploads, and null/undefined
|
|
11
|
+
* are dropped rather than sent as the string "null".
|
|
12
|
+
*/
|
|
13
|
+
export function buildFormData(data) {
|
|
14
|
+
const formData = new FormData();
|
|
15
|
+
for (const [key, val] of Object.entries(data)) {
|
|
16
|
+
if (val === null || val === undefined) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (val instanceof File) {
|
|
20
|
+
formData.append(key, val);
|
|
21
|
+
}
|
|
22
|
+
else if (typeof val === "boolean") {
|
|
23
|
+
formData.append(key, val ? "1" : "0");
|
|
24
|
+
}
|
|
25
|
+
else if (Array.isArray(val)) {
|
|
26
|
+
for (const item of val) {
|
|
27
|
+
formData.append(`${key}[]`, String(item));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
formData.append(key, String(val));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return formData;
|
|
35
|
+
}
|
|
36
|
+
export function useForm(initialValues, options = {}) {
|
|
37
|
+
const [data, setDataState] = useState(initialValues);
|
|
38
|
+
const [errors, setErrors] = useState({});
|
|
39
|
+
const [wasSuccessful, setWasSuccessful] = useState(false);
|
|
40
|
+
const [recentlySuccessful, setRecentlySuccessful] = useState(false);
|
|
41
|
+
const [isPending, startTransition] = useTransition();
|
|
42
|
+
const defaultsRef = useRef({ ...initialValues });
|
|
43
|
+
const transformRef = useRef(null);
|
|
44
|
+
const recentTimerRef = useRef(undefined);
|
|
45
|
+
const setData = useCallback((fieldOrValues, value) => {
|
|
46
|
+
if (typeof fieldOrValues === "object") {
|
|
47
|
+
setDataState((prev) => ({ ...prev, ...fieldOrValues }));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
setDataState((prev) => ({ ...prev, [fieldOrValues]: value }));
|
|
51
|
+
}
|
|
52
|
+
}, []);
|
|
53
|
+
const error = useCallback((field) => errors[field]?.[0], [errors]);
|
|
54
|
+
const clearErrors = useCallback((...fields) => {
|
|
55
|
+
if (fields.length === 0) {
|
|
56
|
+
setErrors({});
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
setErrors((prev) => {
|
|
60
|
+
const next = { ...prev };
|
|
61
|
+
for (const f of fields) {
|
|
62
|
+
delete next[f];
|
|
63
|
+
}
|
|
64
|
+
return next;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}, []);
|
|
68
|
+
const reset = useCallback((...fields) => {
|
|
69
|
+
if (fields.length === 0) {
|
|
70
|
+
setDataState({ ...defaultsRef.current });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
setDataState((prev) => {
|
|
74
|
+
const next = { ...prev };
|
|
75
|
+
for (const f of fields) {
|
|
76
|
+
next[f] = defaultsRef.current[f];
|
|
77
|
+
}
|
|
78
|
+
return next;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
clearErrors();
|
|
82
|
+
}, [clearErrors]);
|
|
83
|
+
const setDefaults = useCallback((values) => {
|
|
84
|
+
if (values) {
|
|
85
|
+
defaultsRef.current = { ...defaultsRef.current, ...values };
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
defaultsRef.current = { ...data };
|
|
89
|
+
}
|
|
90
|
+
}, [data, options.schema]);
|
|
91
|
+
const transformFn = useCallback((fn) => {
|
|
92
|
+
transformRef.current = fn;
|
|
93
|
+
}, []);
|
|
94
|
+
const submit = useCallback((action, optimistic) => {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
startTransition(async () => {
|
|
97
|
+
try {
|
|
98
|
+
// Before the optimistic update, so a rejected form never shows the
|
|
99
|
+
// row it was going to add.
|
|
100
|
+
const invalid = await validateWith(options.schema, data);
|
|
101
|
+
if (invalid) {
|
|
102
|
+
setErrors(invalid);
|
|
103
|
+
resolve();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Call optimistic updater inside the transition so React's
|
|
107
|
+
// useOptimistic picks it up and auto-reverts on settle.
|
|
108
|
+
optimistic?.();
|
|
109
|
+
const payload = transformRef.current ? transformRef.current(data) : data;
|
|
110
|
+
const formData = buildFormData(payload);
|
|
111
|
+
setErrors({});
|
|
112
|
+
const result = await action(formData);
|
|
113
|
+
const answer = result;
|
|
114
|
+
// An action built with createActionClient returns its failures
|
|
115
|
+
// rather than throwing them, because a rejected server action is
|
|
116
|
+
// serialised opaquely and the fields it named would be lost.
|
|
117
|
+
if (answer?.validationErrors) {
|
|
118
|
+
setErrors(answer.validationErrors);
|
|
119
|
+
resolve();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (answer?.serverError) {
|
|
123
|
+
reject(new Error(answer.serverError));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
setWasSuccessful(true);
|
|
127
|
+
setRecentlySuccessful(true);
|
|
128
|
+
if (recentTimerRef.current) {
|
|
129
|
+
clearTimeout(recentTimerRef.current);
|
|
130
|
+
}
|
|
131
|
+
recentTimerRef.current = setTimeout(() => setRecentlySuccessful(false), 2000);
|
|
132
|
+
resolve();
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
if (err instanceof ServerValidationError) {
|
|
136
|
+
setErrors(err.errors);
|
|
137
|
+
}
|
|
138
|
+
if (err instanceof ServerDumpError) {
|
|
139
|
+
resolve();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
reject(err);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}, [data]);
|
|
147
|
+
return {
|
|
148
|
+
data,
|
|
149
|
+
setData,
|
|
150
|
+
errors: errors,
|
|
151
|
+
error,
|
|
152
|
+
hasErrors: Object.keys(errors).length > 0,
|
|
153
|
+
pending: isPending,
|
|
154
|
+
processing: isPending,
|
|
155
|
+
wasSuccessful,
|
|
156
|
+
recentlySuccessful,
|
|
157
|
+
clearErrors,
|
|
158
|
+
reset,
|
|
159
|
+
setDefaults,
|
|
160
|
+
transform: transformFn,
|
|
161
|
+
submit,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=useForm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useForm.js","sourceRoot":"","sources":["../../src/js/useForm.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLinkStatus.d.ts","sourceRoot":"","sources":["../../src/js/useLinkStatus.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useLinkStatus.js","sourceRoot":"","sources":["../../src/js/useLinkStatus.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the app can reach the server, as the router last observed it.
|
|
3
|
+
*
|
|
4
|
+
* See onlineStore.ts for why this is not `navigator.onLine`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useOnline(): boolean;
|
|
7
|
+
export declare function useOffline(): boolean;
|
|
8
|
+
//# sourceMappingURL=useOnline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOnline.d.ts","sourceRoot":"","sources":["../../src/js/useOnline.ts"],"names":[],"mappings":"AAWA;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAEnC;AAED,wBAAgB,UAAU,IAAI,OAAO,CAEpC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSyncExternalStore } from "react";
|
|
3
|
+
import { isReachable, subscribeReachable } from "./onlineStore";
|
|
4
|
+
/** The server is reachable from itself; assuming otherwise would render an
|
|
5
|
+
* offline state into the HTML and mismatch on hydration. */
|
|
6
|
+
function getServerSnapshot() {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Whether the app can reach the server, as the router last observed it.
|
|
11
|
+
*
|
|
12
|
+
* See onlineStore.ts for why this is not `navigator.onLine`.
|
|
13
|
+
*/
|
|
14
|
+
export function useOnline() {
|
|
15
|
+
return useSyncExternalStore(subscribeReachable, isReachable, getServerSnapshot);
|
|
16
|
+
}
|
|
17
|
+
export function useOffline() {
|
|
18
|
+
return !useOnline();
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=useOnline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOnline.js","sourceRoot":"","sources":["../../src/js/useOnline.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEhE;4DAC4D;AAC5D,SAAS,iBAAiB;IACxB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS;IACvB,OAAO,oBAAoB,CAAC,kBAAkB,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,CAAC,SAAS,EAAE,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The current pathname, live across navigations.
|
|
3
|
+
*
|
|
4
|
+
* The server snapshot comes from the url being rendered rather than a
|
|
5
|
+
* constant. It used to be "/" — so a nav bar on any other page rendered with
|
|
6
|
+
* the wrong link marked active, and only hydration put it right.
|
|
7
|
+
*/
|
|
8
|
+
export declare function usePathname(): string;
|
|
9
|
+
//# sourceMappingURL=usePathname.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePathname.d.ts","sourceRoot":"","sources":["../../src/js/usePathname.ts"],"names":[],"mappings":"AA2BA;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAIpC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useSyncExternalStore } from "react";
|
|
3
|
+
import { useRenderedPathname } from "./PathnameProvider";
|
|
4
|
+
let currentPathname = typeof window !== "undefined" ? window.location.pathname : "/";
|
|
5
|
+
const listeners = new Set();
|
|
6
|
+
function notify() {
|
|
7
|
+
currentPathname = window.location.pathname;
|
|
8
|
+
listeners.forEach((fn) => fn());
|
|
9
|
+
}
|
|
10
|
+
if (typeof window !== "undefined") {
|
|
11
|
+
window.addEventListener("rsc-navigate", notify);
|
|
12
|
+
window.addEventListener("popstate", notify);
|
|
13
|
+
}
|
|
14
|
+
function subscribe(callback) {
|
|
15
|
+
listeners.add(callback);
|
|
16
|
+
return () => listeners.delete(callback);
|
|
17
|
+
}
|
|
18
|
+
function getSnapshot() {
|
|
19
|
+
return currentPathname;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The current pathname, live across navigations.
|
|
23
|
+
*
|
|
24
|
+
* The server snapshot comes from the url being rendered rather than a
|
|
25
|
+
* constant. It used to be "/" — so a nav bar on any other page rendered with
|
|
26
|
+
* the wrong link marked active, and only hydration put it right.
|
|
27
|
+
*/
|
|
28
|
+
export function usePathname() {
|
|
29
|
+
const rendered = useRenderedPathname();
|
|
30
|
+
return useSyncExternalStore(subscribe, getSnapshot, () => rendered);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=usePathname.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePathname.js","sourceRoot":"","sources":["../../src/js/usePathname.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,IAAI,eAAe,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC;AACrF,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;AAExC,SAAS,MAAM;IACb,eAAe,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC3C,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAClC,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,SAAS,CAAC,QAAoB;IACrC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACxB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW;IAClB,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,QAAQ,GAAG,mBAAmB,EAAE,CAAC;IAEvC,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSearchParams.d.ts","sourceRoot":"","sources":["../../src/js/useSearchParams.ts"],"names":[],"mappings":"AAwEA,wBAAgB,eAAe,IAAI,eAAe,CAEjD"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* The query string, live across navigations.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately unlike usePathname, which answers with the url being rendered.
|
|
6
|
+
* A pathname is fixed for a given stored page; a query string is not — the
|
|
7
|
+
* same route is asked for with `?q=shoes` and `?q=hats`, and a page stored
|
|
8
|
+
* holding one of them would serve it to everyone.
|
|
9
|
+
*
|
|
10
|
+
* So there is no server snapshot to give, and this throws rather than
|
|
11
|
+
* inventing one. React treats an error thrown during SSR as recoverable at the
|
|
12
|
+
* nearest Suspense boundary: the fallback is what gets stored, and the client
|
|
13
|
+
* renders the real thing on hydration. Without a boundary the error reaches the
|
|
14
|
+
* root, nothing paints, and the build refuses the route and says why — which
|
|
15
|
+
* is the same answer it gives for reading the request too early on the server.
|
|
16
|
+
*
|
|
17
|
+
* Returning an empty URLSearchParams instead would be worse than either: the
|
|
18
|
+
* page would be stored showing results for no query at all, and nothing would
|
|
19
|
+
* say so.
|
|
20
|
+
*/
|
|
21
|
+
import { useSyncExternalStore } from "react";
|
|
22
|
+
const listeners = new Set();
|
|
23
|
+
/**
|
|
24
|
+
* Cached by the string it was parsed from.
|
|
25
|
+
*
|
|
26
|
+
* useSyncExternalStore compares snapshots by identity, so handing back a fresh
|
|
27
|
+
* URLSearchParams on every read reads as "changed every time" and loops until
|
|
28
|
+
* React gives up.
|
|
29
|
+
*/
|
|
30
|
+
let cached = { search: "", params: new URLSearchParams() };
|
|
31
|
+
function currentSearch() {
|
|
32
|
+
return typeof window === "undefined" ? "" : window.location.search;
|
|
33
|
+
}
|
|
34
|
+
function notify() {
|
|
35
|
+
listeners.forEach((fn) => fn());
|
|
36
|
+
}
|
|
37
|
+
if (typeof window !== "undefined") {
|
|
38
|
+
window.addEventListener("rsc-navigate", notify);
|
|
39
|
+
window.addEventListener("popstate", notify);
|
|
40
|
+
}
|
|
41
|
+
function subscribe(callback) {
|
|
42
|
+
listeners.add(callback);
|
|
43
|
+
return () => listeners.delete(callback);
|
|
44
|
+
}
|
|
45
|
+
function getSnapshot() {
|
|
46
|
+
const search = currentSearch();
|
|
47
|
+
if (search !== cached.search) {
|
|
48
|
+
cached = { search, params: new URLSearchParams(search) };
|
|
49
|
+
}
|
|
50
|
+
return cached.params;
|
|
51
|
+
}
|
|
52
|
+
function getServerSnapshot() {
|
|
53
|
+
throw new Error("useSearchParams() was read while rendering on the server, where there is no query string. " +
|
|
54
|
+
"Wrap the component in <Suspense> — or add a loading.tsx beside the page — so the fallback " +
|
|
55
|
+
"is stored and the real value arrives in the browser.");
|
|
56
|
+
}
|
|
57
|
+
export function useSearchParams() {
|
|
58
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=useSearchParams.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSearchParams.js","sourceRoot":"","sources":["../../src/js/useSearchParams.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;AAExC;;;;;;GAMG;AACH,IAAI,MAAM,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,eAAe,EAAE,EAAE,CAAC;AAE3D,SAAS,aAAa;IACpB,OAAO,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;AACrE,CAAC;AAED,SAAS,MAAM;IACb,SAAS,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAClC,MAAM,CAAC,gBAAgB,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,SAAS,CAAC,QAAoB;IACrC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAExB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAE/B,IAAI,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QAC7B,MAAM,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;IAC3D,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,SAAS,iBAAiB;IACxB,MAAM,IAAI,KAAK,CACb,4FAA4F;QAC1F,4FAA4F;QAC5F,sDAAsD,CACzD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,OAAO,oBAAoB,CAAC,SAAS,EAAE,WAAW,EAAE,iBAAiB,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export interface RouteSegment {
|
|
2
|
+
type: 'static' | 'param' | 'catchAll';
|
|
3
|
+
value: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ManifestRoute {
|
|
6
|
+
component: string;
|
|
7
|
+
segments: RouteSegment[];
|
|
8
|
+
layouts: string[];
|
|
9
|
+
loadings: string[];
|
|
10
|
+
/**
|
|
11
|
+
* `middleware.ts` files above this route, outermost first.
|
|
12
|
+
*
|
|
13
|
+
* Run before anything at or below them renders, on every path. A check is
|
|
14
|
+
* not UI, and making it a layout meant the client could decline it: layouts
|
|
15
|
+
* are skipped on a partial navigation, and what gets skipped is named in a
|
|
16
|
+
* header nothing can verify.
|
|
17
|
+
*/
|
|
18
|
+
middleware: string[];
|
|
19
|
+
slots: Record<string, string>;
|
|
20
|
+
sections: string[];
|
|
21
|
+
/**
|
|
22
|
+
* The host's route-config file beside this page, if it named one, and the
|
|
23
|
+
* ancestor ones that also apply — outermost first, this page's excluded.
|
|
24
|
+
*
|
|
25
|
+
* Relative to the project root: an absolute path is true only on the machine
|
|
26
|
+
* that produced it, and building in a container is ordinary.
|
|
27
|
+
*/
|
|
28
|
+
config: string | null;
|
|
29
|
+
ancestorConfigs: string[];
|
|
30
|
+
/**
|
|
31
|
+
* Whether the page exports generateStaticParams.
|
|
32
|
+
*
|
|
33
|
+
* Recorded here so a host can plan a build — which routes to ask for urls,
|
|
34
|
+
* which to leave on demand — without loading the server bundle first. The
|
|
35
|
+
* function itself is reached through the bundle's getStaticParams(), because
|
|
36
|
+
* only the bundle can run it.
|
|
37
|
+
*/
|
|
38
|
+
staticParams: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Whether this route ships the client runtime.
|
|
41
|
+
*
|
|
42
|
+
* False renders to HTML and stops: no bootstrap, so no React, no Flight
|
|
43
|
+
* client, no router. A client component on such a route is inert markup — a
|
|
44
|
+
* button that does nothing — so the build refuses the combination rather
|
|
45
|
+
* than shipping it.
|
|
46
|
+
*/
|
|
47
|
+
clientJs: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface ManifestIntercept {
|
|
50
|
+
component: string;
|
|
51
|
+
slot: string;
|
|
52
|
+
segments: RouteSegment[];
|
|
53
|
+
/** (.) same level, (..) one up, (...) from the root. */
|
|
54
|
+
marker: string;
|
|
55
|
+
}
|
|
56
|
+
export interface RouteManifest {
|
|
57
|
+
version: number;
|
|
58
|
+
build: {
|
|
59
|
+
output: string;
|
|
60
|
+
exportPath: string;
|
|
61
|
+
payloadName: string;
|
|
62
|
+
};
|
|
63
|
+
routes: ManifestRoute[];
|
|
64
|
+
intercepts: ManifestIntercept[];
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAA;IACrC,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB;;;;;;;OAOG;IACH,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB;;;;;;;OAOG;IACH,YAAY,EAAE,OAAO,CAAA;IACrB;;;;;;;OAOG;IACH,QAAQ,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;IAClE,MAAM,EAAE,aAAa,EAAE,CAAA;IACvB,UAAU,EAAE,iBAAiB,EAAE,CAAA;CAChC"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// The shape of routes.json — what the build discovered, for a host to read.
|
|
2
|
+
//
|
|
3
|
+
// The build already walks app/ to generate its entries, and every host needs
|
|
4
|
+
// the same facts: which url a component answers, what layouts wrap it, which
|
|
5
|
+
// slots and sections belong to it. Laravel used to scan the tree a second time
|
|
6
|
+
// to work that out; a JS host would have had to write a third walk. This is the
|
|
7
|
+
// one answer, and these are its types.
|
|
8
|
+
//
|
|
9
|
+
// Urls are segments rather than a pattern string, because the pattern is the
|
|
10
|
+
// host's dialect: Laravel writes {slug}, Hono writes :slug, and neither is the
|
|
11
|
+
// build's business.
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,+EAA+E;AAC/E,gFAAgF;AAChF,uCAAuC;AACvC,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,oBAAoB"}
|