@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.
Files changed (200) hide show
  1. package/README.md +95 -0
  2. package/SECURITY.md +60 -0
  3. package/dist/action.d.ts +90 -0
  4. package/dist/action.d.ts.map +1 -0
  5. package/dist/action.js +137 -0
  6. package/dist/action.js.map +1 -0
  7. package/dist/build-rsc-vite.d.ts +39 -0
  8. package/dist/build-rsc-vite.d.ts.map +1 -0
  9. package/dist/build-rsc-vite.js +172 -0
  10. package/dist/build-rsc-vite.js.map +1 -0
  11. package/dist/cache.d.ts +25 -0
  12. package/dist/cache.d.ts.map +1 -0
  13. package/dist/cache.js +108 -0
  14. package/dist/cache.js.map +1 -0
  15. package/dist/cli.d.ts +2 -0
  16. package/dist/cli.d.ts.map +1 -0
  17. package/dist/cli.js +85 -0
  18. package/dist/cli.js.map +1 -0
  19. package/dist/compile.d.ts +55 -0
  20. package/dist/compile.d.ts.map +1 -0
  21. package/dist/compile.js +141 -0
  22. package/dist/compile.js.map +1 -0
  23. package/dist/devServer.d.ts +18 -0
  24. package/dist/devServer.d.ts.map +1 -0
  25. package/dist/devServer.js +70 -0
  26. package/dist/devServer.js.map +1 -0
  27. package/dist/devUrls.d.ts +11 -0
  28. package/dist/devUrls.d.ts.map +1 -0
  29. package/dist/devUrls.js +68 -0
  30. package/dist/devUrls.js.map +1 -0
  31. package/dist/embed.d.ts +21 -0
  32. package/dist/embed.d.ts.map +1 -0
  33. package/dist/embed.js +119 -0
  34. package/dist/embed.js.map +1 -0
  35. package/dist/errors.d.ts +24 -0
  36. package/dist/errors.d.ts.map +1 -0
  37. package/dist/errors.js +38 -0
  38. package/dist/errors.js.map +1 -0
  39. package/dist/export.d.ts +41 -0
  40. package/dist/export.d.ts.map +1 -0
  41. package/dist/export.js +100 -0
  42. package/dist/export.js.map +1 -0
  43. package/dist/files.d.ts +39 -0
  44. package/dist/files.d.ts.map +1 -0
  45. package/dist/files.js +146 -0
  46. package/dist/files.js.map +1 -0
  47. package/dist/headers.d.ts +81 -0
  48. package/dist/headers.d.ts.map +1 -0
  49. package/dist/headers.js +96 -0
  50. package/dist/headers.js.map +1 -0
  51. package/dist/host.d.ts +86 -0
  52. package/dist/host.d.ts.map +1 -0
  53. package/dist/host.js +590 -0
  54. package/dist/host.js.map +1 -0
  55. package/dist/js/ActivityRouter.d.ts +47 -0
  56. package/dist/js/ActivityRouter.d.ts.map +1 -0
  57. package/dist/js/ActivityRouter.js +87 -0
  58. package/dist/js/ActivityRouter.js.map +1 -0
  59. package/dist/js/DocumentTitle.d.ts +22 -0
  60. package/dist/js/DocumentTitle.d.ts.map +1 -0
  61. package/dist/js/DocumentTitle.js +28 -0
  62. package/dist/js/DocumentTitle.js.map +1 -0
  63. package/dist/js/Form.d.ts +54 -0
  64. package/dist/js/Form.d.ts.map +1 -0
  65. package/dist/js/Form.js +225 -0
  66. package/dist/js/Form.js.map +1 -0
  67. package/dist/js/Link.d.ts +29 -0
  68. package/dist/js/Link.d.ts.map +1 -0
  69. package/dist/js/Link.js +112 -0
  70. package/dist/js/Link.js.map +1 -0
  71. package/dist/js/PathnameProvider.d.ts +8 -0
  72. package/dist/js/PathnameProvider.d.ts.map +1 -0
  73. package/dist/js/PathnameProvider.js +24 -0
  74. package/dist/js/PathnameProvider.js.map +1 -0
  75. package/dist/js/RedirectBoundary.d.ts +18 -0
  76. package/dist/js/RedirectBoundary.d.ts.map +1 -0
  77. package/dist/js/RedirectBoundary.js +34 -0
  78. package/dist/js/RedirectBoundary.js.map +1 -0
  79. package/dist/js/SegmentBoundary.d.ts +8 -0
  80. package/dist/js/SegmentBoundary.d.ts.map +1 -0
  81. package/dist/js/SegmentBoundary.js +38 -0
  82. package/dist/js/SegmentBoundary.js.map +1 -0
  83. package/dist/js/SlotBoundary.d.ts +15 -0
  84. package/dist/js/SlotBoundary.d.ts.map +1 -0
  85. package/dist/js/SlotBoundary.js +17 -0
  86. package/dist/js/SlotBoundary.js.map +1 -0
  87. package/dist/js/createViteRscApp.d.ts +8 -0
  88. package/dist/js/createViteRscApp.d.ts.map +1 -0
  89. package/dist/js/createViteRscApp.js +185 -0
  90. package/dist/js/createViteRscApp.js.map +1 -0
  91. package/dist/js/errors.d.ts +51 -0
  92. package/dist/js/errors.d.ts.map +1 -0
  93. package/dist/js/errors.js +93 -0
  94. package/dist/js/errors.js.map +1 -0
  95. package/dist/js/navigate.d.ts +118 -0
  96. package/dist/js/navigate.d.ts.map +1 -0
  97. package/dist/js/navigate.js +780 -0
  98. package/dist/js/navigate.js.map +1 -0
  99. package/dist/js/onlineStore.d.ts +23 -0
  100. package/dist/js/onlineStore.d.ts.map +1 -0
  101. package/dist/js/onlineStore.js +42 -0
  102. package/dist/js/onlineStore.js.map +1 -0
  103. package/dist/js/pagePayload.d.ts +15 -0
  104. package/dist/js/pagePayload.d.ts.map +1 -0
  105. package/dist/js/pagePayload.js +39 -0
  106. package/dist/js/pagePayload.js.map +1 -0
  107. package/dist/js/router.d.ts +29 -0
  108. package/dist/js/router.d.ts.map +1 -0
  109. package/dist/js/router.js +40 -0
  110. package/dist/js/router.js.map +1 -0
  111. package/dist/js/section.d.ts +16 -0
  112. package/dist/js/section.d.ts.map +1 -0
  113. package/dist/js/section.js +55 -0
  114. package/dist/js/section.js.map +1 -0
  115. package/dist/js/segmentStore.d.ts +69 -0
  116. package/dist/js/segmentStore.d.ts.map +1 -0
  117. package/dist/js/segmentStore.js +127 -0
  118. package/dist/js/segmentStore.js.map +1 -0
  119. package/dist/js/slotStore.d.ts +35 -0
  120. package/dist/js/slotStore.d.ts.map +1 -0
  121. package/dist/js/slotStore.js +46 -0
  122. package/dist/js/slotStore.js.map +1 -0
  123. package/dist/js/standardSchema.d.ts +47 -0
  124. package/dist/js/standardSchema.d.ts.map +1 -0
  125. package/dist/js/standardSchema.js +41 -0
  126. package/dist/js/standardSchema.js.map +1 -0
  127. package/dist/js/useForm.d.ts +43 -0
  128. package/dist/js/useForm.d.ts.map +1 -0
  129. package/dist/js/useForm.js +164 -0
  130. package/dist/js/useForm.js.map +1 -0
  131. package/dist/js/useLinkStatus.d.ts +2 -0
  132. package/dist/js/useLinkStatus.d.ts.map +1 -0
  133. package/dist/js/useLinkStatus.js +3 -0
  134. package/dist/js/useLinkStatus.js.map +1 -0
  135. package/dist/js/useOnline.d.ts +8 -0
  136. package/dist/js/useOnline.d.ts.map +1 -0
  137. package/dist/js/useOnline.js +20 -0
  138. package/dist/js/useOnline.js.map +1 -0
  139. package/dist/js/usePathname.d.ts +9 -0
  140. package/dist/js/usePathname.d.ts.map +1 -0
  141. package/dist/js/usePathname.js +32 -0
  142. package/dist/js/usePathname.js.map +1 -0
  143. package/dist/js/useSearchParams.d.ts +2 -0
  144. package/dist/js/useSearchParams.d.ts.map +1 -0
  145. package/dist/js/useSearchParams.js +60 -0
  146. package/dist/js/useSearchParams.js.map +1 -0
  147. package/dist/manifest.d.ts +66 -0
  148. package/dist/manifest.d.ts.map +1 -0
  149. package/dist/manifest.js +13 -0
  150. package/dist/manifest.js.map +1 -0
  151. package/dist/prerender.d.ts +138 -0
  152. package/dist/prerender.d.ts.map +1 -0
  153. package/dist/prerender.js +387 -0
  154. package/dist/prerender.js.map +1 -0
  155. package/dist/redirect.d.ts +34 -0
  156. package/dist/redirect.d.ts.map +1 -0
  157. package/dist/redirect.js +108 -0
  158. package/dist/redirect.js.map +1 -0
  159. package/dist/redirectDigest.d.ts +20 -0
  160. package/dist/redirectDigest.d.ts.map +1 -0
  161. package/dist/redirectDigest.js +66 -0
  162. package/dist/redirectDigest.js.map +1 -0
  163. package/dist/request.d.ts +93 -0
  164. package/dist/request.d.ts.map +1 -0
  165. package/dist/request.js +308 -0
  166. package/dist/request.js.map +1 -0
  167. package/dist/revalidate.d.ts +43 -0
  168. package/dist/revalidate.d.ts.map +1 -0
  169. package/dist/revalidate.js +99 -0
  170. package/dist/revalidate.js.map +1 -0
  171. package/dist/routes.d.ts +37 -0
  172. package/dist/routes.d.ts.map +1 -0
  173. package/dist/routes.js +28 -0
  174. package/dist/routes.js.map +1 -0
  175. package/dist/routing.d.ts +52 -0
  176. package/dist/routing.d.ts.map +1 -0
  177. package/dist/routing.js +146 -0
  178. package/dist/routing.js.map +1 -0
  179. package/dist/runtime.d.ts +36 -0
  180. package/dist/runtime.d.ts.map +1 -0
  181. package/dist/runtime.js +86 -0
  182. package/dist/runtime.js.map +1 -0
  183. package/dist/safeUrl.d.ts +10 -0
  184. package/dist/safeUrl.d.ts.map +1 -0
  185. package/dist/safeUrl.js +40 -0
  186. package/dist/safeUrl.js.map +1 -0
  187. package/dist/streaming.d.ts +62 -0
  188. package/dist/streaming.d.ts.map +1 -0
  189. package/dist/streaming.js +108 -0
  190. package/dist/streaming.js.map +1 -0
  191. package/dist/types.d.ts +82 -0
  192. package/dist/vite.d.ts +109 -0
  193. package/dist/vite.d.ts.map +1 -0
  194. package/dist/vite.js +1909 -0
  195. package/dist/vite.js.map +1 -0
  196. package/dist/worker.d.ts +2 -0
  197. package/dist/worker.d.ts.map +1 -0
  198. package/dist/worker.js +863 -0
  199. package/dist/worker.js.map +1 -0
  200. package/package.json +223 -0
@@ -0,0 +1,43 @@
1
+ /** What this needs from a runtime: somewhere to keep per-action state. */
2
+ interface Scope {
3
+ getStore(): Set<string> | undefined;
4
+ run<T>(store: Set<string>, fn: () => T): T;
5
+ }
6
+ /**
7
+ * AsyncLocalStorage, wherever this happens to be running.
8
+ *
9
+ * The global first, because that is where a Worker has it — and where the
10
+ * engine puts it, since @vitejs/plugin-rsc assigns
11
+ * `globalThis.AsyncLocalStorage` for React's edge build. Then the module,
12
+ * which is where Node, Bun and Deno have it.
13
+ *
14
+ * There is no third branch on purpose. The engine bundle imports
15
+ * `node:async_hooks` statically, so async context is a requirement of the
16
+ * whole system rather than of this file: a runtime without it cannot load the
17
+ * engine at all, and a fallback here would be code that can never run.
18
+ */
19
+ export declare function resolveScope(from?: Record<string, unknown>): Promise<Scope>;
20
+ /**
21
+ * Mark a region of the current page for re-rendering.
22
+ *
23
+ * `'page'` and `'all'` re-render the page or the whole document; any other
24
+ * name is a section or a parallel slot. Unknown names are refused by the
25
+ * renderer with the names it does know, rather than quietly refreshing
26
+ * nothing.
27
+ *
28
+ * Outside an action this does nothing rather than throwing: it is reasonable
29
+ * for shared code to mark, and unreasonable for that to fail when the same
30
+ * function is called during an ordinary render.
31
+ */
32
+ export declare function revalidate(target: string): void;
33
+ /** Whether anything is listening, for a host that wants to warn about the rest. */
34
+ export declare function isRevalidating(): boolean;
35
+ /**
36
+ * Run an action with somewhere for its marks to go, and collect them.
37
+ *
38
+ * The marks are read *after* the action has run, because what it invalidated
39
+ * is only known once its work is done.
40
+ */
41
+ export declare function withRevalidation<T>(run: (taken: () => string[]) => Promise<T>): Promise<T>;
42
+ export {};
43
+ //# sourceMappingURL=revalidate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revalidate.d.ts","sourceRoot":"","sources":["../src/revalidate.ts"],"names":[],"mappings":"AAmBA,0EAA0E;AAC1E,UAAU,KAAK;IACb,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAA;IACnC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAA;CAC3C;AAqBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,YAAY,CAGhC,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAW,GACtC,OAAO,CAAC,KAAK,CAAC,CAQhB;AAOD;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,mFAAmF;AACnF,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,MAAM,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,GACzC,OAAO,CAAC,CAAC,CAAC,CAcZ"}
@@ -0,0 +1,99 @@
1
+ // Marking part of a page stale from inside a server action.
2
+ //
3
+ // The point is one round trip. An action that changes an order could tell the
4
+ // browser to go and re-fetch something, but then the answer the user is
5
+ // waiting on arrives before the screen is right, and the fix costs another
6
+ // request. Marking instead lets the action's own answer carry the re-rendered
7
+ // region back with it — the client applies it and never knows it asked.
8
+ //
9
+ // 'use server'
10
+ // export async function addOrder(order) {
11
+ // await orders.insert(order)
12
+ // revalidate('orders') // the section or slot by that name
13
+ // return { ok: true }
14
+ // }
15
+ //
16
+ // Scoped to the action that is running, not global, because two requests can
17
+ // be in flight at once and marking is per-request state. In Laravel this is a
18
+ // `scoped()` binding on the container.
19
+ /**
20
+ * One store, however many copies of this module exist.
21
+ *
22
+ * The app's actions are bundled into the server bundle; the host that runs
23
+ * them is not. So this module is loaded twice, and two scopes mean the action
24
+ * marks in one and the host reads the other — everything appears to work, the
25
+ * action's answer simply never carries anything back, and no error is raised
26
+ * on either side.
27
+ *
28
+ * The engine installs its host callable on globalThis for exactly this
29
+ * reason. This is the same crossing.
30
+ */
31
+ const SCOPE = Symbol.for('@rsc-kit/core.revalidation-scope');
32
+ const globals = globalThis;
33
+ let ready = null;
34
+ let warned = false;
35
+ /**
36
+ * AsyncLocalStorage, wherever this happens to be running.
37
+ *
38
+ * The global first, because that is where a Worker has it — and where the
39
+ * engine puts it, since @vitejs/plugin-rsc assigns
40
+ * `globalThis.AsyncLocalStorage` for React's edge build. Then the module,
41
+ * which is where Node, Bun and Deno have it.
42
+ *
43
+ * There is no third branch on purpose. The engine bundle imports
44
+ * `node:async_hooks` statically, so async context is a requirement of the
45
+ * whole system rather than of this file: a runtime without it cannot load the
46
+ * engine at all, and a fallback here would be code that can never run.
47
+ */
48
+ export async function resolveScope(
49
+ // Injectable so the branch a Worker takes can be tested from a runtime that
50
+ // would otherwise never reach it.
51
+ from = globals) {
52
+ const Ambient = from.AsyncLocalStorage;
53
+ if (Ambient)
54
+ return new Ambient();
55
+ const { AsyncLocalStorage } = await import('node:async_hooks');
56
+ return new AsyncLocalStorage();
57
+ }
58
+ /** The scope, once resolved. Null before the first action runs. */
59
+ function scope() {
60
+ return globals[SCOPE] ?? null;
61
+ }
62
+ /**
63
+ * Mark a region of the current page for re-rendering.
64
+ *
65
+ * `'page'` and `'all'` re-render the page or the whole document; any other
66
+ * name is a section or a parallel slot. Unknown names are refused by the
67
+ * renderer with the names it does know, rather than quietly refreshing
68
+ * nothing.
69
+ *
70
+ * Outside an action this does nothing rather than throwing: it is reasonable
71
+ * for shared code to mark, and unreasonable for that to fail when the same
72
+ * function is called during an ordinary render.
73
+ */
74
+ export function revalidate(target) {
75
+ scope()?.getStore()?.add(target);
76
+ }
77
+ /** Whether anything is listening, for a host that wants to warn about the rest. */
78
+ export function isRevalidating() {
79
+ return scope()?.getStore() !== undefined;
80
+ }
81
+ /**
82
+ * Run an action with somewhere for its marks to go, and collect them.
83
+ *
84
+ * The marks are read *after* the action has run, because what it invalidated
85
+ * is only known once its work is done.
86
+ */
87
+ export async function withRevalidation(run) {
88
+ if (!globals[SCOPE]) {
89
+ // Resolved once and shared, for the same reason the scope itself is: a
90
+ // second copy of this module must not end up with a second scope.
91
+ ready ??= resolveScope().then((resolved) => {
92
+ globals[SCOPE] ??= resolved;
93
+ });
94
+ await ready;
95
+ }
96
+ const marked = new Set();
97
+ return await scope().run(marked, () => run(() => [...marked]));
98
+ }
99
+ //# sourceMappingURL=revalidate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revalidate.js","sourceRoot":"","sources":["../src/revalidate.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,EAAE;AACF,8EAA8E;AAC9E,wEAAwE;AACxE,2EAA2E;AAC3E,8EAA8E;AAC9E,wEAAwE;AACxE,EAAE;AACF,iBAAiB;AACjB,4CAA4C;AAC5C,iCAAiC;AACjC,qEAAqE;AACrE,0BAA0B;AAC1B,MAAM;AACN,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,uCAAuC;AAQvC;;;;;;;;;;;GAWG;AACH,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAA;AAE5D,MAAM,OAAO,GAAG,UAA8C,CAAA;AAE9D,IAAI,KAAK,GAAyB,IAAI,CAAA;AACtC,IAAI,MAAM,GAAG,KAAK,CAAA;AAElB;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY;AAChC,4EAA4E;AAC5E,kCAAkC;AAClC,IAAI,GAA4B,OAAO;IAEvC,MAAM,OAAO,GAAG,IAAI,CAAC,iBAAkD,CAAA;IAEvE,IAAI,OAAO;QAAE,OAAO,IAAI,OAAO,EAAE,CAAA;IAEjC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAA;IAE9D,OAAO,IAAI,iBAAiB,EAAwB,CAAA;AACtD,CAAC;AAED,mEAAmE;AACnE,SAAS,KAAK;IACZ,OAAQ,OAAO,CAAC,KAAK,CAAuB,IAAI,IAAI,CAAA;AACtD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;AAClC,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,cAAc;IAC5B,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,SAAS,CAAA;AAC1C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAA0C;IAE1C,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,uEAAuE;QACvE,kEAAkE;QAClE,KAAK,KAAK,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;YACzC,OAAO,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAA;QAC7B,CAAC,CAAC,CAAA;QAEF,MAAM,KAAK,CAAA;IACb,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAA;IAEhC,OAAO,MAAM,KAAK,EAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;AACjE,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Augmented by the generated `rsc-routes.d.ts`. Empty here on purpose.
3
+ *
4
+ * Declaration merging rather than a generic parameter, because the routes are
5
+ * a property of the project, not of each call site — threading them through
6
+ * every component that renders a Link is not a thing anyone would do twice.
7
+ */
8
+ export interface Register {
9
+ }
10
+ /** The route patterns this app declared: `'/posts/[slug]'`. */
11
+ export type RoutePattern = Register extends {
12
+ routes: infer R extends string;
13
+ } ? R : string;
14
+ /** Whether anything was registered. `string` means the generator never ran. */
15
+ type Unregistered = string extends RoutePattern ? true : false;
16
+ /**
17
+ * A pattern with its dynamic segments opened up: `/posts/[slug]` accepts
18
+ * `/posts/anything`.
19
+ *
20
+ * Catch-all and single params both become `${string}`, which for a catch-all
21
+ * also swallows the slashes — `/docs/[...path]` accepts `/docs/a/b/c`.
22
+ */
23
+ type Filled<P extends string> = P extends `${infer A}[...${string}]${infer B}` ? `${A}${string}${Filled<B>}` : P extends `${infer A}[${string}]${infer B}` ? `${A}${string}${Filled<B>}` : P;
24
+ /**
25
+ * Not a route, but a legitimate href: another site, a mail client, a phone
26
+ * number, an anchor on this page, a bare query string.
27
+ */
28
+ type OffRoute = `${string}://${string}` | `mailto:${string}` | `tel:${string}` | `#${string}` | `?${string}`;
29
+ /**
30
+ * A url this app can answer, or one that deliberately leaves it.
31
+ *
32
+ * Cast when the destination is computed rather than written:
33
+ * `href={path as Href}`.
34
+ */
35
+ export type Href = Unregistered extends true ? string : Filled<RoutePattern> | `${Filled<RoutePattern>}?${string}` | `${Filled<RoutePattern>}#${string}` | OffRoute;
36
+ export {};
37
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AA2BA;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;CAAG;AAE5B,+DAA+D;AAC/D,MAAM,MAAM,YAAY,GAAG,QAAQ,SAAS;IAAE,MAAM,EAAE,MAAM,CAAC,SAAS,MAAM,CAAA;CAAE,GAAG,CAAC,GAAG,MAAM,CAAA;AAE3F,+EAA+E;AAC/E,KAAK,YAAY,GAAG,MAAM,SAAS,YAAY,GAAG,IAAI,GAAG,KAAK,CAAA;AAE9D;;;;;;GAMG;AACH,KAAK,MAAM,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,OAAO,MAAM,IAAI,MAAM,CAAC,EAAE,GAC1E,GAAG,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,GAC3B,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,MAAM,IAAI,MAAM,CAAC,EAAE,GACzC,GAAG,CAAC,GAAG,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,GAC3B,CAAC,CAAA;AAEP;;;GAGG;AACH,KAAK,QAAQ,GAAG,GAAG,MAAM,MAAM,MAAM,EAAE,GAAG,UAAU,MAAM,EAAE,GAAG,OAAO,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,GAAG,IAAI,MAAM,EAAE,CAAA;AAE5G;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG,YAAY,SAAS,IAAI,GACxC,MAAM,GACN,MAAM,CAAC,YAAY,CAAC,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,MAAM,EAAE,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,MAAM,EAAE,GAAG,QAAQ,CAAA"}
package/dist/routes.js ADDED
@@ -0,0 +1,28 @@
1
+ // Typed routes: the urls this app can actually answer, as a type.
2
+ //
3
+ // Laravel needs route() because the url lives in PHP and can move
4
+ // independently of the name it is called by. Here the url *is* the file path,
5
+ // so a name would be indirection that buys nothing. What is worth having is
6
+ // the other half — a link to a page that does not exist should fail at build
7
+ // time rather than in the browser.
8
+ //
9
+ // There is no route() builder to go with this, deliberately. A template
10
+ // literal is checked the same way — `/posts/${slug}` compiles and
11
+ // `/postz/${slug}` does not — so a builder would only wrap what the language
12
+ // already does. Encoding a value that is not url-safe is `encodeURIComponent`
13
+ // in the template, the same as anywhere else.
14
+ //
15
+ // The build already walks app/ and knows every route's segments, so it writes
16
+ // one line into the app's source dir:
17
+ //
18
+ // declare module '@rsc-kit/core/routes' {
19
+ // interface Register { routes: '/' | '/posts/[slug]' }
20
+ // }
21
+ //
22
+ // Everything below is derived from that union. An app that never runs the
23
+ // generator — a generic host, a Laravel app that has not rebuilt — registers
24
+ // nothing, `RoutePattern` stays `string`, and every url-taking API is exactly
25
+ // as permissive as it was before. That fallback is the reason this can ship
26
+ // without a flag.
27
+ export {};
28
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,EAAE;AACF,kEAAkE;AAClE,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,mCAAmC;AACnC,EAAE;AACF,wEAAwE;AACxE,kEAAkE;AAClE,6EAA6E;AAC7E,8EAA8E;AAC9E,8CAA8C;AAC9C,EAAE;AACF,8EAA8E;AAC9E,sCAAsC;AACtC,EAAE;AACF,4CAA4C;AAC5C,2DAA2D;AAC3D,MAAM;AACN,EAAE;AACF,0EAA0E;AAC1E,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,kBAAkB"}
@@ -0,0 +1,52 @@
1
+ import type { ManifestRoute, RouteManifest } from './manifest.js';
2
+ /** A matched route and the params its url segments bound. */
3
+ export interface MatchedRoute {
4
+ route: ManifestRoute;
5
+ params: Record<string, string>;
6
+ }
7
+ /**
8
+ * Match a pathname against the manifest's segments.
9
+ *
10
+ * The manifest stores segments rather than a pattern string, because the
11
+ * pattern is the host's dialect — Laravel writes `{slug}`, Hono writes
12
+ * `:slug`, and neither is the build's business. Matching them directly means
13
+ * no dialect at all.
14
+ *
15
+ * Static segments beat dynamic ones at the same position: /docs/new is the
16
+ * page called new, not the page called [slug] with slug=new.
17
+ */
18
+ export declare function matchRoute(manifest: RouteManifest, pathname: string): MatchedRoute | null;
19
+ /**
20
+ * The interceptor registered for a slot, if this url has one.
21
+ *
22
+ * Matched against the url being navigated *to*: `(.)posts/[slug]` intercepts
23
+ * /posts/anything, and the params it binds are the target's, not the page the
24
+ * modal opens over.
25
+ */
26
+ export declare function matchIntercept(manifest: RouteManifest, pathname: string, slot: string): {
27
+ component: string;
28
+ params: Record<string, string>;
29
+ } | null;
30
+ /**
31
+ * How much of the layout chain the client already holds.
32
+ *
33
+ * It sends the chain outermost-first; the shared prefix is what it can keep.
34
+ * Answering with a whole document instead is not merely wasteful — it replaces
35
+ * the root, and replacing the root unmounts every page retained behind the
36
+ * current one, so a half-typed form does not survive going back.
37
+ */
38
+ export declare function sharedDepth(held: string | null, chain: string[]): number;
39
+ /**
40
+ * What a page is remembered as, for retention and for the prefetch cache.
41
+ *
42
+ * The same url intercepted and not intercepted are two different things to go
43
+ * back to — a modal over the feed, and the post on its own page — so they
44
+ * cannot share a key or restoring one returns the other.
45
+ *
46
+ * Shared because both halves compute it: the host stores under this key and
47
+ * the client looks under it. Written out at each of the seven places that
48
+ * needed it, they only had to disagree once, and the symptom is a navigation
49
+ * that silently rebuilds a page it was holding.
50
+ */
51
+ export declare function retentionKey(path: string, interceptSlot?: string | null): string;
52
+ //# sourceMappingURL=routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../src/routing.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAgB,MAAM,eAAe,CAAA;AAE/E,6DAA6D;AAC7D,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAA;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAuBzF;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,IAAI,CA8B9D;AA4BD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAWxE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAIhF"}
@@ -0,0 +1,146 @@
1
+ // Matching a url to a route, and deciding how much of the page to send.
2
+ //
3
+ // Pure: no Request, no Response, no filesystem. That is what lets the same
4
+ // implementation serve three callers — the request handler, the prerenderer
5
+ // enumerating what to build, and the generated server bundle, which embeds its
6
+ // own route table and answers these without a manifest being passed around at
7
+ // all.
8
+ //
9
+ // Every host would otherwise write this, and would get the same two things
10
+ // wrong: answering /docs/new with [slug], and treating the layout chain as a
11
+ // set rather than a sequence.
12
+ /**
13
+ * Match a pathname against the manifest's segments.
14
+ *
15
+ * The manifest stores segments rather than a pattern string, because the
16
+ * pattern is the host's dialect — Laravel writes `{slug}`, Hono writes
17
+ * `:slug`, and neither is the build's business. Matching them directly means
18
+ * no dialect at all.
19
+ *
20
+ * Static segments beat dynamic ones at the same position: /docs/new is the
21
+ * page called new, not the page called [slug] with slug=new.
22
+ */
23
+ export function matchRoute(manifest, pathname) {
24
+ const parts = pathname.split('/').filter(Boolean);
25
+ let best = null;
26
+ let bestScore = -1;
27
+ for (const route of manifest.routes) {
28
+ const bound = bindSegments(route.segments, parts);
29
+ if (!bound)
30
+ continue;
31
+ // More static segments wins; a catch-all is the weakest possible match.
32
+ const score = route.segments.reduce((n, s) => n + (s.type === 'static' ? 2 : s.type === 'param' ? 1 : 0), 0);
33
+ if (score > bestScore) {
34
+ best = { route, params: bound };
35
+ bestScore = score;
36
+ }
37
+ }
38
+ return best;
39
+ }
40
+ /**
41
+ * The interceptor registered for a slot, if this url has one.
42
+ *
43
+ * Matched against the url being navigated *to*: `(.)posts/[slug]` intercepts
44
+ * /posts/anything, and the params it binds are the target's, not the page the
45
+ * modal opens over.
46
+ */
47
+ export function matchIntercept(manifest, pathname, slot) {
48
+ const parts = pathname.split('/').filter(Boolean);
49
+ let best = null;
50
+ let bestScore = -1;
51
+ for (const intercept of manifest.intercepts) {
52
+ if (intercept.slot !== slot)
53
+ continue;
54
+ const params = bindSegments(intercept.segments, parts);
55
+ if (!params)
56
+ continue;
57
+ // Scored exactly as matchRoute scores, and for the same reason: a static
58
+ // segment beats a dynamic one at the same position. Taking the first match
59
+ // instead made the two disagree about which route a url belongs to — and
60
+ // the guard is chosen by one while the content comes from the other, so a
61
+ // url could be checked against the unguarded route and rendered from the
62
+ // guarded one.
63
+ const score = intercept.segments.reduce((n, segment) => n + (segment.type === 'static' ? 2 : segment.type === 'param' ? 1 : 0), 0);
64
+ if (score > bestScore) {
65
+ best = { component: intercept.component, params };
66
+ bestScore = score;
67
+ }
68
+ }
69
+ return best;
70
+ }
71
+ function bindSegments(segments, parts) {
72
+ const params = {};
73
+ for (let i = 0; i < segments.length; i++) {
74
+ const segment = segments[i];
75
+ if (segment.type === 'catchAll') {
76
+ // Swallows the rest, including none of it.
77
+ params[segment.value] = parts.slice(i).join('/');
78
+ return params;
79
+ }
80
+ if (i >= parts.length)
81
+ return null;
82
+ if (segment.type === 'static') {
83
+ if (parts[i] !== segment.value)
84
+ return null;
85
+ continue;
86
+ }
87
+ params[segment.value] = decodeURIComponent(parts[i]);
88
+ }
89
+ return segments.length === parts.length ? params : null;
90
+ }
91
+ /**
92
+ * How much of the layout chain the client already holds.
93
+ *
94
+ * It sends the chain outermost-first; the shared prefix is what it can keep.
95
+ * Answering with a whole document instead is not merely wasteful — it replaces
96
+ * the root, and replacing the root unmounts every page retained behind the
97
+ * current one, so a half-typed form does not survive going back.
98
+ */
99
+ export function sharedDepth(held, chain) {
100
+ if (!held)
101
+ return 0;
102
+ const mounted = held.split(',').filter(Boolean);
103
+ let shared = 0;
104
+ while (shared < mounted.length && shared < chain.length && mounted[shared] === chain[shared]) {
105
+ shared++;
106
+ }
107
+ return shared;
108
+ }
109
+ /**
110
+ * What a page is remembered as, for retention and for the prefetch cache.
111
+ *
112
+ * The same url intercepted and not intercepted are two different things to go
113
+ * back to — a modal over the feed, and the post on its own page — so they
114
+ * cannot share a key or restoring one returns the other.
115
+ *
116
+ * Shared because both halves compute it: the host stores under this key and
117
+ * the client looks under it. Written out at each of the seven places that
118
+ * needed it, they only had to disagree once, and the symptom is a navigation
119
+ * that silently rebuilds a page it was holding.
120
+ */
121
+ export function retentionKey(path, interceptSlot) {
122
+ const normalised = normalisePath(path);
123
+ return interceptSlot ? `__intercept:${interceptSlot}:${normalised}` : normalised;
124
+ }
125
+ /**
126
+ * The same page written two ways is one key.
127
+ *
128
+ * A static host serves /orders as a directory, so the browser's url ends in a
129
+ * slash while the build wrote the page down as /orders. Left unequal, the
130
+ * entry retained for a page is never the one looked up on the way back: it
131
+ * stays in the document, hidden, while a second copy is fetched and rendered
132
+ * beside it — so the form you were filling in is there, and not the one you
133
+ * are looking at.
134
+ */
135
+ function normalisePath(path) {
136
+ // Absolute urls reach this from the initial page, which is identified by
137
+ // href rather than by the path a link would use.
138
+ const withoutOrigin = path.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]+/i, '');
139
+ const [pathname, rest = ''] = splitQuery(withoutOrigin);
140
+ return (pathname.replace(/\/+$/, '') || '/') + rest;
141
+ }
142
+ function splitQuery(url) {
143
+ const cut = url.search(/[?#]/);
144
+ return cut === -1 ? [url, ''] : [url.slice(0, cut), url.slice(cut)];
145
+ }
146
+ //# sourceMappingURL=routing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.js","sourceRoot":"","sources":["../src/routing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ export type SocketLike = {
2
+ write(data: string | Uint8Array): number;
3
+ flush(): void;
4
+ /**
5
+ * Hang up. Optional only because a caller may hold a stub; every real
6
+ * transport has one, and a frame stream that cannot be parsed has to be
7
+ * ended rather than resynchronised — see the invalid-length branch in
8
+ * worker.ts for what resuming costs.
9
+ */
10
+ end?(): void;
11
+ };
12
+ export interface SocketHandlers {
13
+ data(socket: SocketLike, data: Uint8Array): void | Promise<void>;
14
+ drain?(socket: SocketLike): void;
15
+ open?(socket: SocketLike): void;
16
+ close?(socket: SocketLike): void;
17
+ error?(socket: SocketLike, error: Error): void;
18
+ }
19
+ export interface ListenOptions {
20
+ unix?: string;
21
+ hostname?: string;
22
+ port?: number;
23
+ }
24
+ /** True when running under Bun rather than Node. */
25
+ export declare const isBun: boolean;
26
+ export declare const runtimeName: string;
27
+ /** Yield to the event loop so queued socket writes flush before continuing. */
28
+ export declare function yieldToEventLoop(): Promise<void>;
29
+ /** Every .ts/.js file under dir, as paths relative to it. */
30
+ export declare function scanScripts(dir: string): string[];
31
+ export interface Server {
32
+ stop(): void;
33
+ }
34
+ /** Listen on a Unix socket or TCP address, dispatching to Bun-shaped handlers. */
35
+ export declare function listen(options: ListenOptions, handlers: SocketHandlers): Server;
36
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAA;IACxC,KAAK,IAAI,IAAI,CAAA;IACb;;;;;OAKG;IACH,GAAG,CAAC,IAAI,IAAI,CAAA;CACb,CAAA;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAChE,KAAK,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IAChC,IAAI,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IAC/B,KAAK,CAAC,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IAChC,KAAK,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd;AAED,oDAAoD;AACpD,eAAO,MAAM,KAAK,SAA+D,CAAA;AAEjF,eAAO,MAAM,WAAW,QAAyB,CAAA;AAEjD,+EAA+E;AAC/E,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAEhD;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAkBjD;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,IAAI,IAAI,CAAA;CACb;AAED,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAW/E"}
@@ -0,0 +1,86 @@
1
+ // Runtime shim for the worker.
2
+ //
3
+ // The worker's only runtime-specific surface is the socket server, a yield to
4
+ // the event loop, and a directory walk. Bun and Node both provide all three;
5
+ // this presents them under one shape so worker.ts never branches.
6
+ //
7
+ // The write contract is Bun's: write() returns the number of bytes accepted, so
8
+ // a partial write can be queued and retried on drain. Node never writes
9
+ // partially — it queues internally — so the Node adapter always reports a full
10
+ // write and the caller's pending-buffer path simply never engages.
11
+ import { createServer } from 'node:net';
12
+ import { readdirSync, statSync } from 'node:fs';
13
+ import { join, relative } from 'node:path';
14
+ /** True when running under Bun rather than Node. */
15
+ export const isBun = typeof globalThis.Bun !== 'undefined';
16
+ export const runtimeName = isBun ? 'bun' : 'node';
17
+ /** Yield to the event loop so queued socket writes flush before continuing. */
18
+ export function yieldToEventLoop() {
19
+ return new Promise((resolve) => setTimeout(resolve, 0));
20
+ }
21
+ /** Every .ts/.js file under dir, as paths relative to it. */
22
+ export function scanScripts(dir) {
23
+ const found = [];
24
+ const walk = (current) => {
25
+ for (const entry of readdirSync(current)) {
26
+ const abs = join(current, entry);
27
+ if (statSync(abs).isDirectory()) {
28
+ walk(abs);
29
+ }
30
+ else if (/\.(ts|js)$/.test(entry)) {
31
+ found.push(relative(dir, abs));
32
+ }
33
+ }
34
+ };
35
+ walk(dir);
36
+ return found;
37
+ }
38
+ /** Listen on a Unix socket or TCP address, dispatching to Bun-shaped handlers. */
39
+ export function listen(options, handlers) {
40
+ if (isBun) {
41
+ const server = globalThis.Bun.listen({
42
+ ...(options.unix ? { unix: options.unix } : { hostname: options.hostname, port: options.port }),
43
+ socket: handlers,
44
+ });
45
+ return { stop: () => server.stop() };
46
+ }
47
+ return listenOnNode(options, handlers);
48
+ }
49
+ function listenOnNode(options, handlers) {
50
+ const server = createServer((connection) => {
51
+ // One adapter per connection: the worker uses the socket object as a Map
52
+ // key, so it has to be stable for the connection's lifetime.
53
+ const socket = {
54
+ write(data) {
55
+ const buffer = typeof data === 'string' ? Buffer.from(data, 'utf-8') : Buffer.from(data);
56
+ // Node buffers whatever the kernel would not take, so everything is
57
+ // always accepted. The boolean it returns is a backpressure hint, not
58
+ // a byte count.
59
+ connection.write(buffer);
60
+ return buffer.length;
61
+ },
62
+ flush() { },
63
+ end() {
64
+ connection.end();
65
+ },
66
+ };
67
+ connection.on('data', (chunk) => {
68
+ // Always bytes: the socket is never given an encoding, so node hands
69
+ // back a Buffer rather than a string.
70
+ void handlers.data(socket, chunk);
71
+ });
72
+ connection.on('drain', () => handlers.drain?.(socket));
73
+ connection.on('close', () => handlers.close?.(socket));
74
+ connection.on('error', (error) => handlers.error?.(socket, error));
75
+ handlers.open?.(socket);
76
+ });
77
+ server.on('error', (error) => handlers.error?.(null, error));
78
+ if (options.unix) {
79
+ server.listen(options.unix);
80
+ }
81
+ else {
82
+ server.listen(options.port, options.hostname);
83
+ }
84
+ return { stop: () => server.close() };
85
+ }
86
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export declare function isSafeRedirect(value: string): boolean;
2
+ /**
3
+ * The same check, as a refusal.
4
+ *
5
+ * Says the value back, deliberately. A destination that fails this is far more
6
+ * often a bug in what the app computed than an attack, and a message that does
7
+ * not name the value leaves nothing to go on.
8
+ */
9
+ export declare function assertSafeRedirect(value: string): void;
10
+ //# sourceMappingURL=safeUrl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safeUrl.d.ts","sourceRoot":"","sources":["../src/safeUrl.ts"],"names":[],"mappings":"AAeA,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAWrD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAQtD"}
@@ -0,0 +1,40 @@
1
+ // Whether a redirect destination is a url a browser should be sent to.
2
+ //
3
+ // A destination is routinely computed rather than written — redirect()'s own
4
+ // documentation describes reading one from a cookie as the usual case — and it
5
+ // reaches location.href, location.replace and an inline script. A scheme other
6
+ // than http(s) in any of those is script execution rather than navigation.
7
+ //
8
+ // Parsed rather than pattern-matched. javascript: can be written with mixed
9
+ // case, with a newline inside it, or behind leading control characters, and
10
+ // each of those is a different regex to get wrong. The URL parser normalises
11
+ // all of it already, so asking it for the protocol is shorter and correct.
12
+ /** A base that cannot collide with a real origin, for resolving relative urls. */
13
+ const NOWHERE = 'https://rsc-kit.invalid/';
14
+ export function isSafeRedirect(value) {
15
+ try {
16
+ const { protocol } = new URL(value, NOWHERE);
17
+ // http and https only. Not data:, not blob:, not javascript:, and not a
18
+ // custom app scheme — a browser handing one of those to another program is
19
+ // not something a render should be able to trigger.
20
+ return protocol === 'http:' || protocol === 'https:';
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ /**
27
+ * The same check, as a refusal.
28
+ *
29
+ * Says the value back, deliberately. A destination that fails this is far more
30
+ * often a bug in what the app computed than an attack, and a message that does
31
+ * not name the value leaves nothing to go on.
32
+ */
33
+ export function assertSafeRedirect(value) {
34
+ if (isSafeRedirect(value))
35
+ return;
36
+ throw new Error('Not a url a redirect may go to: ' + JSON.stringify(value) + '. ' +
37
+ 'A destination has to be a path or an http(s) url — any other scheme is ' +
38
+ 'handed to the browser as something to run rather than somewhere to go.');
39
+ }
40
+ //# sourceMappingURL=safeUrl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safeUrl.js","sourceRoot":"","sources":["../src/safeUrl.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,+EAA+E;AAC/E,2EAA2E;AAC3E,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAC7E,2EAA2E;AAE3E,kFAAkF;AAClF,MAAM,OAAO,GAAG,0BAA0B,CAAA;AAE1C,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;QAE5C,wEAAwE;QACxE,2EAA2E;QAC3E,oDAAoD;QACpD,OAAO,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,QAAQ,CAAA;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,IAAI,cAAc,CAAC,KAAK,CAAC;QAAE,OAAM;IAEjC,MAAM,IAAI,KAAK,CACb,kCAAkC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI;QAC/D,yEAAyE;QACzE,wEAAwE,CAC3E,CAAA;AACH,CAAC"}