@rsc-kit/core 0.30.37 → 0.30.39

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.
Files changed (56) hide show
  1. package/dist/flash.d.ts +18 -0
  2. package/dist/flash.js +29 -0
  3. package/dist/flash.js.map +1 -0
  4. package/dist/flashCookie.d.ts +17 -0
  5. package/dist/flashCookie.js +36 -0
  6. package/dist/flashCookie.js.map +1 -0
  7. package/dist/host.js +15 -2
  8. package/dist/host.js.map +1 -1
  9. package/dist/js/ActivityRouter.js +3 -3
  10. package/dist/js/ActivityRouter.js.map +1 -1
  11. package/dist/js/Changes.js +1 -1
  12. package/dist/js/Changes.js.map +1 -1
  13. package/dist/js/DefaultRouteError.d.ts +1 -1
  14. package/dist/js/DefaultRouteError.js.map +1 -1
  15. package/dist/js/Form.d.ts +2 -2
  16. package/dist/js/Form.js +5 -5
  17. package/dist/js/Form.js.map +1 -1
  18. package/dist/js/Link.js +2 -2
  19. package/dist/js/Link.js.map +1 -1
  20. package/dist/js/RedirectBoundary.js +2 -2
  21. package/dist/js/RedirectBoundary.js.map +1 -1
  22. package/dist/js/SegmentBoundary.js +5 -5
  23. package/dist/js/SegmentBoundary.js.map +1 -1
  24. package/dist/js/SlotBoundary.js +3 -3
  25. package/dist/js/SlotBoundary.js.map +1 -1
  26. package/dist/js/changesStore.js +1 -1
  27. package/dist/js/changesStore.js.map +1 -1
  28. package/dist/js/createViteRscApp.js +14 -14
  29. package/dist/js/createViteRscApp.js.map +1 -1
  30. package/dist/js/navigate.d.ts +1 -1
  31. package/dist/js/navigate.js +8 -8
  32. package/dist/js/navigate.js.map +1 -1
  33. package/dist/js/nuqs.js +2 -2
  34. package/dist/js/nuqs.js.map +1 -1
  35. package/dist/js/pagePayload.js +2 -2
  36. package/dist/js/pagePayload.js.map +1 -1
  37. package/dist/js/refreshOn.js +3 -3
  38. package/dist/js/refreshOn.js.map +1 -1
  39. package/dist/js/section.d.ts +4 -4
  40. package/dist/js/section.js +2 -2
  41. package/dist/js/section.js.map +1 -1
  42. package/dist/js/useAppUpdate.js +1 -1
  43. package/dist/js/useAppUpdate.js.map +1 -1
  44. package/dist/js/useFlash.d.ts +16 -0
  45. package/dist/js/useFlash.js +56 -0
  46. package/dist/js/useFlash.js.map +1 -0
  47. package/dist/js/useOnline.js +1 -1
  48. package/dist/js/useOnline.js.map +1 -1
  49. package/dist/js/usePathname.js +1 -1
  50. package/dist/js/usePathname.js.map +1 -1
  51. package/dist/testing.d.ts +9 -0
  52. package/dist/testing.js +14 -2
  53. package/dist/testing.js.map +1 -1
  54. package/dist/vite.js +1 -0
  55. package/dist/vite.js.map +1 -1
  56. package/package.json +9 -1
@@ -0,0 +1,18 @@
1
+ import { type FlashKind } from "./flashCookie.js";
2
+ export type { FlashKind, FlashMessage } from "./flashCookie.js";
3
+ /**
4
+ * A message for the next page the visitor sees: "Saved." after a save that
5
+ * redirects to the list. Call it in a server action, middleware or a
6
+ * route.ts - anywhere a cookie can still be set - and show it with
7
+ * useFlash() from @rsc-kit/core/useFlash.
8
+ *
9
+ * A cookie the browser takes, not one the server reads. A page that read it
10
+ * would be a page that can never be stored or cached at the edge, and a
11
+ * render cannot clear it anyway: its headers leave before it starts. So the
12
+ * page is the same for everyone, and the browser picks the message up after
13
+ * the navigation and clears it, once.
14
+ *
15
+ * Two calls in one request are two messages. Unread, it lapses after a
16
+ * minute, so a message never turns up on some later, unrelated page.
17
+ */
18
+ export declare function flash(message: string, kind?: FlashKind): Promise<void>;
package/dist/flash.js ADDED
@@ -0,0 +1,29 @@
1
+ import { cookies } from "./request.js";
2
+ import { FLASH_COOKIE, readFlash } from "./flashCookie.js";
3
+ /**
4
+ * A message for the next page the visitor sees: "Saved." after a save that
5
+ * redirects to the list. Call it in a server action, middleware or a
6
+ * route.ts - anywhere a cookie can still be set - and show it with
7
+ * useFlash() from @rsc-kit/core/useFlash.
8
+ *
9
+ * A cookie the browser takes, not one the server reads. A page that read it
10
+ * would be a page that can never be stored or cached at the edge, and a
11
+ * render cannot clear it anyway: its headers leave before it starts. So the
12
+ * page is the same for everyone, and the browser picks the message up after
13
+ * the navigation and clears it, once.
14
+ *
15
+ * Two calls in one request are two messages. Unread, it lapses after a
16
+ * minute, so a message never turns up on some later, unrelated page.
17
+ */
18
+ export async function flash(message, kind = "success") {
19
+ const jar = await cookies();
20
+ const queued = readFlash(jar.get(FLASH_COOKIE)?.value);
21
+ // Readable by script on purpose (no HttpOnly): the browser is what takes it.
22
+ // It holds a message the app chose, never data.
23
+ jar.set(FLASH_COOKIE, JSON.stringify([...queued, { message, kind }]), {
24
+ path: "/",
25
+ maxAge: 60,
26
+ sameSite: "lax",
27
+ });
28
+ }
29
+ //# sourceMappingURL=flash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flash.js","sourceRoot":"","sources":["../src/flash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAkB,MAAM,kBAAkB,CAAC;AAI3E;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAAe,EAAE,IAAI,GAAc,SAAS;IACtE,MAAM,GAAG,GAAG,MAAM,OAAO,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;IAEvD,6EAA6E;IAC7E,gDAAgD;IAChD,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QACpE,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,EAAE;QACV,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { cookies } from \"./request.js\";\nimport { FLASH_COOKIE, readFlash, type FlashKind } from \"./flashCookie.js\";\n\nexport type { FlashKind, FlashMessage } from \"./flashCookie.js\";\n\n/**\n * A message for the next page the visitor sees: \"Saved.\" after a save that\n * redirects to the list. Call it in a server action, middleware or a\n * route.ts - anywhere a cookie can still be set - and show it with\n * useFlash() from @rsc-kit/core/useFlash.\n *\n * A cookie the browser takes, not one the server reads. A page that read it\n * would be a page that can never be stored or cached at the edge, and a\n * render cannot clear it anyway: its headers leave before it starts. So the\n * page is the same for everyone, and the browser picks the message up after\n * the navigation and clears it, once.\n *\n * Two calls in one request are two messages. Unread, it lapses after a\n * minute, so a message never turns up on some later, unrelated page.\n */\nexport async function flash(message: string, kind: FlashKind = \"success\"): Promise<void> {\n const jar = await cookies();\n const queued = readFlash(jar.get(FLASH_COOKIE)?.value);\n\n // Readable by script on purpose (no HttpOnly): the browser is what takes it.\n // It holds a message the app chose, never data.\n jar.set(FLASH_COOKIE, JSON.stringify([...queued, { message, kind }]), {\n path: \"/\",\n maxAge: 60,\n sameSite: \"lax\",\n });\n}\n"]}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The one-time message's cookie, shared by the server that writes it and the
3
+ * browser that takes it. No imports: the hook that reads it is in the
4
+ * browser bundle, and request.ts must never be.
5
+ */
6
+ export declare const FLASH_COOKIE = "rsc-flash";
7
+ export type FlashKind = "success" | "error" | "info" | "warning";
8
+ export interface FlashMessage {
9
+ message: string;
10
+ kind: FlashKind;
11
+ }
12
+ /**
13
+ * What a cookie value holds, keeping only well-formed messages. The value
14
+ * crossed the network and could have been written by anything that shares
15
+ * the domain, so nothing here trusts it beyond being text.
16
+ */
17
+ export declare function readFlash(value: string | undefined | null): FlashMessage[];
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The one-time message's cookie, shared by the server that writes it and the
3
+ * browser that takes it. No imports: the hook that reads it is in the
4
+ * browser bundle, and request.ts must never be.
5
+ */
6
+ export const FLASH_COOKIE = "rsc-flash";
7
+ const KINDS = new Set(["success", "error", "info", "warning"]);
8
+ /**
9
+ * What a cookie value holds, keeping only well-formed messages. The value
10
+ * crossed the network and could have been written by anything that shares
11
+ * the domain, so nothing here trusts it beyond being text.
12
+ */
13
+ export function readFlash(value) {
14
+ if (!value)
15
+ return [];
16
+ let parsed;
17
+ try {
18
+ parsed = JSON.parse(value);
19
+ }
20
+ catch {
21
+ return [];
22
+ }
23
+ if (!Array.isArray(parsed))
24
+ return [];
25
+ return parsed.flatMap((entry) => {
26
+ if (typeof entry !== "object" || entry === null)
27
+ return [];
28
+ const { message, kind } = entry;
29
+ if (typeof message !== "string" || message === "")
30
+ return [];
31
+ return [
32
+ { message, kind: typeof kind === "string" && KINDS.has(kind) ? kind : "info" },
33
+ ];
34
+ });
35
+ }
36
+ //# sourceMappingURL=flashCookie.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flashCookie.js","sourceRoot":"","sources":["../src/flashCookie.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AASxC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAS,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;AAEvE;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,KAAgC;IACxD,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IAEtB,IAAI,MAAe,CAAC;IAEpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAkB,EAAE;QAC9C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,EAAE,CAAC;QAE3D,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,KAAgC,CAAC;QAE3D,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,EAAE;YAAE,OAAO,EAAE,CAAC;QAE7D,OAAO;YACL,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAAkB,CAAC,CAAC,CAAC,MAAM,EAAE;SAC9F,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * The one-time message's cookie, shared by the server that writes it and the\n * browser that takes it. No imports: the hook that reads it is in the\n * browser bundle, and request.ts must never be.\n */\nexport const FLASH_COOKIE = \"rsc-flash\";\n\nexport type FlashKind = \"success\" | \"error\" | \"info\" | \"warning\";\n\nexport interface FlashMessage {\n message: string;\n kind: FlashKind;\n}\n\nconst KINDS = new Set<string>([\"success\", \"error\", \"info\", \"warning\"]);\n\n/**\n * What a cookie value holds, keeping only well-formed messages. The value\n * crossed the network and could have been written by anything that shares\n * the domain, so nothing here trusts it beyond being text.\n */\nexport function readFlash(value: string | undefined | null): FlashMessage[] {\n if (!value) return [];\n\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(value);\n } catch {\n return [];\n }\n\n if (!Array.isArray(parsed)) return [];\n\n return parsed.flatMap((entry): FlashMessage[] => {\n if (typeof entry !== \"object\" || entry === null) return [];\n\n const { message, kind } = entry as Record<string, unknown>;\n\n if (typeof message !== \"string\" || message === \"\") return [];\n\n return [\n { message, kind: typeof kind === \"string\" && KINDS.has(kind) ? (kind as FlashKind) : \"info\" },\n ];\n });\n}\n"]}
package/dist/host.js CHANGED
@@ -128,6 +128,19 @@ function isNavigation(request) {
128
128
  return mode === "navigate";
129
129
  return (request.headers.get("accept") ?? "").includes("text/html");
130
130
  }
131
+ /**
132
+ * A redirect out of a form a browser posted to a page, as See Other.
133
+ *
134
+ * Whatever status redirect() was given. A 307 or 308 tells the browser to
135
+ * repeat the request - the same POST, the same body - at the destination, and
136
+ * that body still names the action, which runs at any page's url. So a
137
+ * save that redirected to its list ran again there, redirected again, and
138
+ * looped until the browser gave up, writing a record on every pass. 303 is
139
+ * the answer a form post is meant to get: go there, with a GET.
140
+ */
141
+ function afterFormPost(to) {
142
+ return { ...to, status: 303 };
143
+ }
131
144
  function redirectResponse(to, isPayloadRequest) {
132
145
  if (isPayloadRequest) {
133
146
  return new Response(null, {
@@ -955,7 +968,7 @@ export function createRscHandler(options) {
955
968
  // destination sitting in a scope nobody read.
956
969
  const refused = taken();
957
970
  if (refused)
958
- return redirectResponse(refused, false);
971
+ return redirectResponse(formPost ? afterFormPost(refused) : refused, false);
959
972
  // The page said this url names nothing. Null rather than a rendered
960
973
  // 404: null is already how this host says "not mine", and the caller
961
974
  // in front answers it with not-found.tsx and the right status. One
@@ -977,7 +990,7 @@ export function createRscHandler(options) {
977
990
  const early = taken();
978
991
  if (early) {
979
992
  letGo();
980
- return redirectResponse(early, false);
993
+ return redirectResponse(formPost ? afterFormPost(early) : early, false);
981
994
  }
982
995
  // Above every boundary, so the shell resolving means the page did not
983
996
  // refuse itself. Deeper than that and the shell is already on the wire