@kasperrt/smolter 0.2.0 → 0.3.1

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 (270) hide show
  1. package/.generated/client/deployment.ts +161 -0
  2. package/{src/internal → .generated}/client/index.ts +3 -0
  3. package/.generated/core/environment.ts +43 -0
  4. package/{src/internal → .generated}/core/errors.ts +1 -1
  5. package/{src/internal → .generated}/core/index.ts +1 -0
  6. package/.generated/core/navigation-router.ts +107 -0
  7. package/.generated/core/navigation.ts +147 -0
  8. package/.generated/core/presentation.ts +108 -0
  9. package/.generated/core/resource-types.ts +44 -0
  10. package/.generated/core/resources.ts +444 -0
  11. package/.generated/core/responses.ts +86 -0
  12. package/{src/internal → .generated}/core/router.ts +136 -63
  13. package/{src/internal → .generated}/core/serialize.ts +7 -2
  14. package/.generated/core/store.ts +25 -0
  15. package/.generated/core/submission.ts +116 -0
  16. package/{src/internal → .generated}/core/types.ts +18 -1
  17. package/.generated/core/url.ts +29 -0
  18. package/.generated/core/utils.ts +5 -0
  19. package/.generated/preact/await.tsx +41 -0
  20. package/.generated/preact/client.tsx +93 -0
  21. package/.generated/preact/context.ts +59 -0
  22. package/.generated/preact/document.tsx +198 -0
  23. package/{src/internal → .generated}/preact/fetcher.ts +43 -23
  24. package/.generated/preact/form.ts +58 -0
  25. package/{src/internal → .generated}/preact/hooks.ts +25 -28
  26. package/.generated/preact/index.ts +34 -0
  27. package/.generated/preact/lazy-route.ts +100 -0
  28. package/.generated/preact/navigation-state.ts +9 -0
  29. package/.generated/preact/navigation.tsx +147 -0
  30. package/.generated/preact/root-context.ts +21 -0
  31. package/.generated/preact/root-data.ts +45 -0
  32. package/.generated/preact/route-hooks.tsx +170 -0
  33. package/.generated/preact/route-types.ts +76 -0
  34. package/.generated/preact/router.tsx +107 -0
  35. package/{src/internal → .generated}/preact/routes.ts +17 -9
  36. package/.generated/react/context.ts +59 -0
  37. package/.generated/react/data.ts +25 -0
  38. package/{src/internal → .generated}/react/fetcher.ts +13 -11
  39. package/.generated/react/form.ts +58 -0
  40. package/{src/internal → .generated}/react/hooks.ts +9 -14
  41. package/{src/internal/preact → .generated/react}/index.ts +5 -1
  42. package/{src/internal → .generated}/react/link.ts +13 -21
  43. package/.generated/react/resources/components.tsx +404 -0
  44. package/{src/internal → .generated}/react/routes.ts +17 -9
  45. package/.generated/server/application.tsx +185 -0
  46. package/.generated/server/assets.ts +19 -0
  47. package/.generated/server/handler.ts +69 -0
  48. package/.generated/server/http.ts +55 -0
  49. package/{src/internal → .generated}/server/index.ts +3 -3
  50. package/.generated/server/node.ts +4 -0
  51. package/.generated/server/preact.ts +7 -0
  52. package/.generated/vite/application/browser-boundary.ts +42 -0
  53. package/.generated/vite/application/lazy-files.ts +138 -0
  54. package/.generated/vite/application/vite.ts +144 -0
  55. package/.generated/vite/deployment.ts +46 -0
  56. package/{src/internal → .generated}/vite/index.ts +11 -4
  57. package/{src/preact-vite.ts → .generated/vite/preact.ts} +14 -3
  58. package/{src/react-vite.ts → .generated/vite/react.ts} +8 -3
  59. package/CHANGELOG.md +63 -0
  60. package/README.md +277 -35
  61. package/dist/client/deployment.d.ts +12 -0
  62. package/dist/client/deployment.js +102 -0
  63. package/dist/client/deserialize.js +10 -0
  64. package/dist/{internal/client → client}/index.d.ts +2 -0
  65. package/dist/client/index.js +47 -0
  66. package/dist/core/environment.d.ts +12 -0
  67. package/dist/core/environment.js +23 -0
  68. package/dist/core/errors.js +7 -0
  69. package/dist/{internal/core → core}/index.d.ts +1 -0
  70. package/dist/core/match.js +33 -0
  71. package/dist/core/navigation-router.d.ts +15 -0
  72. package/dist/core/navigation-router.js +64 -0
  73. package/dist/core/navigation.d.ts +15 -0
  74. package/dist/core/navigation.js +92 -0
  75. package/dist/core/presentation.d.ts +5 -0
  76. package/dist/core/presentation.js +69 -0
  77. package/dist/core/resource-types.d.ts +37 -0
  78. package/dist/core/resources.d.ts +19 -0
  79. package/dist/core/resources.js +345 -0
  80. package/dist/core/responses.d.ts +15 -0
  81. package/dist/core/responses.js +48 -0
  82. package/dist/{internal/core → core}/router.d.ts +1 -1
  83. package/dist/core/router.js +204 -0
  84. package/dist/{internal/core → core}/serialize.d.ts +2 -0
  85. package/dist/core/serialize.js +17 -0
  86. package/dist/core/store.d.ts +6 -0
  87. package/dist/core/store.js +21 -0
  88. package/dist/core/submission.d.ts +30 -0
  89. package/dist/core/submission.js +41 -0
  90. package/dist/{internal/core → core}/types.d.ts +16 -1
  91. package/dist/core/url.d.ts +7 -0
  92. package/dist/core/url.js +12 -0
  93. package/dist/core/utils.d.ts +5 -0
  94. package/dist/core/utils.js +5 -0
  95. package/dist/files/index.js +64 -0
  96. package/dist/preact/await.d.ts +7 -0
  97. package/dist/preact/await.js +19 -0
  98. package/dist/preact/client.d.ts +7 -0
  99. package/dist/preact/client.js +68 -0
  100. package/dist/preact/context.d.ts +18 -0
  101. package/dist/preact/context.js +23 -0
  102. package/dist/preact/document.d.ts +15 -0
  103. package/dist/preact/document.js +155 -0
  104. package/dist/{internal/preact → preact}/fetcher.d.ts +9 -5
  105. package/dist/preact/fetcher.js +45 -0
  106. package/dist/{internal/preact → preact}/form.d.ts +5 -6
  107. package/dist/preact/form.js +21 -0
  108. package/dist/{internal/preact → preact}/hooks.d.ts +7 -6
  109. package/dist/preact/hooks.js +25 -0
  110. package/dist/preact/index.d.ts +18 -0
  111. package/dist/preact/index.js +15 -0
  112. package/dist/preact/lazy-route.d.ts +19 -0
  113. package/dist/preact/lazy-route.js +51 -0
  114. package/dist/preact/navigation-state.d.ts +2 -0
  115. package/dist/preact/navigation-state.js +9 -0
  116. package/dist/preact/navigation.d.ts +28 -0
  117. package/dist/preact/navigation.js +60 -0
  118. package/dist/preact/root-context.d.ts +13 -0
  119. package/dist/preact/root-context.js +9 -0
  120. package/dist/preact/root-data.d.ts +8 -0
  121. package/dist/preact/root-data.js +28 -0
  122. package/dist/preact/route-hooks.d.ts +32 -0
  123. package/dist/preact/route-hooks.js +94 -0
  124. package/dist/preact/route-types.d.ts +54 -0
  125. package/dist/preact/router.d.ts +16 -0
  126. package/dist/preact/router.js +63 -0
  127. package/dist/{internal/preact → preact}/routes.d.ts +1 -1
  128. package/dist/preact/routes.js +12 -0
  129. package/dist/{internal/preact → preact}/runtime.js +1 -1
  130. package/dist/react/context.d.ts +18 -0
  131. package/dist/react/context.js +23 -0
  132. package/dist/react/data.d.ts +5 -0
  133. package/dist/react/data.js +3 -0
  134. package/dist/{internal/react → react}/fetcher.d.ts +2 -2
  135. package/dist/react/fetcher.js +34 -0
  136. package/dist/{internal/react → react}/form.d.ts +5 -6
  137. package/dist/react/form.js +21 -0
  138. package/dist/react/hooks.js +25 -0
  139. package/dist/{internal/react → react}/index.d.ts +1 -1
  140. package/dist/react/index.js +10 -0
  141. package/dist/{internal/react → react}/link.d.ts +2 -2
  142. package/dist/react/link.js +27 -0
  143. package/dist/react/resources/components.d.ts +64 -0
  144. package/dist/react/resources/components.js +239 -0
  145. package/dist/{internal/react → react}/routes.d.ts +1 -1
  146. package/dist/react/routes.js +12 -0
  147. package/dist/react/runtime.js +1 -0
  148. package/dist/server/application.d.ts +4 -0
  149. package/dist/server/application.js +119 -0
  150. package/dist/server/assets.d.ts +5 -0
  151. package/dist/server/assets.js +7 -0
  152. package/dist/server/handler.d.ts +19 -0
  153. package/dist/server/handler.js +22 -0
  154. package/dist/server/http.d.ts +4 -0
  155. package/dist/server/http.js +27 -0
  156. package/dist/server/index.js +76 -0
  157. package/dist/server/node.d.ts +3 -0
  158. package/dist/server/node.js +3 -0
  159. package/dist/server/preact.d.ts +3 -0
  160. package/dist/server/preact.js +4 -0
  161. package/dist/vite/application/browser-boundary.d.ts +3 -0
  162. package/dist/vite/application/browser-boundary.js +35 -0
  163. package/dist/vite/application/lazy-files.d.ts +2 -0
  164. package/dist/vite/application/lazy-files.js +88 -0
  165. package/dist/vite/application/vite.d.ts +9 -0
  166. package/dist/vite/application/vite.js +101 -0
  167. package/dist/vite/deployment.d.ts +8 -0
  168. package/dist/vite/deployment.js +36 -0
  169. package/dist/vite/index.js +109 -0
  170. package/dist/{preact-vite.d.ts → vite/preact.d.ts} +6 -3
  171. package/dist/vite/preact.js +16 -0
  172. package/dist/{react-vite.d.ts → vite/react.d.ts} +4 -3
  173. package/dist/vite/react.js +15 -0
  174. package/dist/vite/transport.js +35 -0
  175. package/jsr.json +28 -16
  176. package/package.json +115 -35
  177. package/dist/client.d.ts +0 -2
  178. package/dist/client.js +0 -2
  179. package/dist/core.d.ts +0 -2
  180. package/dist/core.js +0 -2
  181. package/dist/files.d.ts +0 -2
  182. package/dist/files.js +0 -2
  183. package/dist/internal/client/deserialize.js +0 -11
  184. package/dist/internal/client/index.js +0 -44
  185. package/dist/internal/core/errors.js +0 -6
  186. package/dist/internal/core/index.js +0 -7
  187. package/dist/internal/core/match.js +0 -27
  188. package/dist/internal/core/router.js +0 -189
  189. package/dist/internal/core/serialize.js +0 -13
  190. package/dist/internal/core/types.js +0 -5
  191. package/dist/internal/core/url.d.ts +0 -2
  192. package/dist/internal/core/url.js +0 -6
  193. package/dist/internal/files/index.js +0 -77
  194. package/dist/internal/preact/context.d.ts +0 -18
  195. package/dist/internal/preact/context.js +0 -20
  196. package/dist/internal/preact/fetcher.js +0 -28
  197. package/dist/internal/preact/form.js +0 -41
  198. package/dist/internal/preact/hooks.js +0 -35
  199. package/dist/internal/preact/index.d.ts +0 -9
  200. package/dist/internal/preact/index.js +0 -7
  201. package/dist/internal/preact/link.d.ts +0 -12
  202. package/dist/internal/preact/link.js +0 -28
  203. package/dist/internal/preact/routes.js +0 -22
  204. package/dist/internal/react/context.d.ts +0 -18
  205. package/dist/internal/react/context.js +0 -20
  206. package/dist/internal/react/fetcher.js +0 -28
  207. package/dist/internal/react/form.js +0 -41
  208. package/dist/internal/react/hooks.js +0 -35
  209. package/dist/internal/react/index.js +0 -7
  210. package/dist/internal/react/link.js +0 -28
  211. package/dist/internal/react/routes.js +0 -22
  212. package/dist/internal/react/runtime.js +0 -2
  213. package/dist/internal/server/index.js +0 -69
  214. package/dist/internal/vite/index.js +0 -132
  215. package/dist/internal/vite/transport.js +0 -42
  216. package/dist/preact-client.d.ts +0 -2
  217. package/dist/preact-client.js +0 -2
  218. package/dist/preact-server.d.ts +0 -2
  219. package/dist/preact-server.js +0 -2
  220. package/dist/preact-vite.js +0 -9
  221. package/dist/preact.d.ts +0 -2
  222. package/dist/preact.js +0 -2
  223. package/dist/react-client.d.ts +0 -2
  224. package/dist/react-client.js +0 -2
  225. package/dist/react-server.d.ts +0 -2
  226. package/dist/react-server.js +0 -2
  227. package/dist/react-vite.js +0 -9
  228. package/dist/react.d.ts +0 -2
  229. package/dist/react.js +0 -2
  230. package/dist/server.d.ts +0 -2
  231. package/dist/server.js +0 -2
  232. package/dist/utils.d.ts +0 -5
  233. package/dist/utils.js +0 -5
  234. package/dist/vite.d.ts +0 -2
  235. package/dist/vite.js +0 -2
  236. package/src/client.ts +0 -2
  237. package/src/core.ts +0 -2
  238. package/src/files.ts +0 -2
  239. package/src/internal/core/url.ts +0 -6
  240. package/src/internal/preact/context.ts +0 -46
  241. package/src/internal/preact/form.ts +0 -79
  242. package/src/internal/preact/link.ts +0 -46
  243. package/src/internal/react/context.ts +0 -46
  244. package/src/internal/react/form.ts +0 -79
  245. package/src/internal/react/index.ts +0 -13
  246. package/src/preact-client.ts +0 -2
  247. package/src/preact-server.ts +0 -2
  248. package/src/preact.ts +0 -2
  249. package/src/react-client.ts +0 -2
  250. package/src/react-server.ts +0 -2
  251. package/src/react.ts +0 -2
  252. package/src/server.ts +0 -2
  253. package/src/utils.ts +0 -5
  254. package/src/vite.ts +0 -2
  255. /package/{src/internal → .generated}/client/deserialize.ts +0 -0
  256. /package/{src/internal → .generated}/core/match.ts +0 -0
  257. /package/{src/internal → .generated}/files/index.ts +0 -0
  258. /package/{src/internal → .generated}/preact/runtime.ts +0 -0
  259. /package/{src/internal → .generated}/react/runtime.ts +0 -0
  260. /package/{src/internal → .generated}/vite/transport.ts +0 -0
  261. /package/dist/{internal/client → client}/deserialize.d.ts +0 -0
  262. /package/dist/{internal/core → core}/errors.d.ts +0 -0
  263. /package/dist/{internal/core → core}/match.d.ts +0 -0
  264. /package/dist/{internal/files → files}/index.d.ts +0 -0
  265. /package/dist/{internal/preact → preact}/runtime.d.ts +0 -0
  266. /package/dist/{internal/react → react}/hooks.d.ts +0 -0
  267. /package/dist/{internal/react → react}/runtime.d.ts +0 -0
  268. /package/dist/{internal/server → server}/index.d.ts +0 -0
  269. /package/dist/{internal/vite → vite}/index.d.ts +0 -0
  270. /package/dist/{internal/vite → vite}/transport.d.ts +0 -0
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # smolter _
2
2
 
3
3
  <!-- bundlejs:start -->
4
- [![react gzip size](https://deno.bundlejs.com/?q=%40kasperrt%2Fsmolter%400.2.0%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&badge)](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.2.0%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&bundle)
5
- [![preact gzip size](https://deno.bundlejs.com/?q=%40kasperrt%2Fsmolter%400.2.0%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&badge)](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.2.0%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&bundle)
4
+ [![react gzip size](https://deno.bundlejs.com/?q=%40kasperrt%2Fsmolter%400.3.1%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&badge)](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.3.1%2Freact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22react%22%2C%22react%2F*%22%5D%7D%7D&bundle)
5
+ [![preact gzip size](https://deno.bundlejs.com/?q=%40kasperrt%2Fsmolter%400.3.1%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&badge)](https://bundlejs.com/?q=%40kasperrt%2Fsmolter%400.3.1%2Fpreact&treeshake=%5B*%5D&config=%7B%22compression%22%3A%22gzip%22%2C%22esbuild%22%3A%7B%22external%22%3A%5B%22preact%22%2C%22preact%2F*%22%5D%7D%7D&bundle)
6
6
  <!-- bundlejs:end -->
7
7
 
8
8
  A typed data router for React and Preact. **Zero runtime dependencies.**
@@ -20,6 +20,8 @@ Start with [loaders](https://kasperrt.github.io/smolter/react/docs/loaders/), [a
20
20
 
21
21
  ## Contents
22
22
 
23
+ - [Deployment updates](#deployment-updates)
24
+
23
25
  - [Install](#install)
24
26
  - [Try the four standalone examples](#try-the-four-standalone-examples)
25
27
  - [Choose file routing or manual routes](#choose-file-routing-or-manual-routes)
@@ -29,7 +31,10 @@ Start with [loaders](https://kasperrt.github.io/smolter/react/docs/loaders/), [a
29
31
  - [Types follow the handler](#types-follow-the-handler)
30
32
  - [Server rendering](#server-rendering)
31
33
  - [Package entries](#package-entries)
34
+ - [Application integration](#application-integration)
35
+ - [Resource subscriptions](#resource-subscriptions)
32
36
  - [Size and checks](#size-and-checks)
37
+ - [Routing without data hooks](#routing-without-data-hooks)
33
38
 
34
39
  ## Install
35
40
 
@@ -40,7 +45,7 @@ pnpm add @kasperrt/smolter
40
45
  ```
41
46
 
42
47
  ```ts
43
- import { createRouter, useFetcher } from "@kasperrt/smolter/react";
48
+ import { createDataRouter, useFetcher } from "@kasperrt/smolter/react";
44
49
  // Preact: import from "@kasperrt/smolter/preact".
45
50
  ```
46
51
 
@@ -61,7 +66,8 @@ pnpm dev
61
66
  Each app has its own entrypoints, routes, and data code. Only UI components are
62
67
  shared. Preact examples do not import React. Each includes:
63
68
 
64
- - `/`: counter, fetcher POST, automatic and manual revalidation.
69
+ - `/`: a short introduction and a link to the docs.
70
+ - `/examples/counter`: fetcher POST, automatic and manual revalidation.
65
71
  - `/tasks`: list, create form, and a fetcher-only task preview.
66
72
  - `/tasks/:id`: PATCH, DELETE, validation, and skipping no-op revalidation.
67
73
  - `/about`: the separation between data and rendering.
@@ -94,10 +100,10 @@ export default defineConfig({
94
100
 
95
101
  ```ts
96
102
  // app.ts
97
- import { createRouter } from "@kasperrt/smolter/react";
103
+ import { createDataRouter } from "@kasperrt/smolter/react";
98
104
  import routes from "virtual:smolter/routes";
99
105
 
100
- export const router = createRouter(routes, { browser: true });
106
+ export const router = createDataRouter(routes, { browser: true });
101
107
  ```
102
108
 
103
109
  | File or folder | URL |
@@ -136,14 +142,14 @@ Outside Vite, `flatRoutes(directory)` from `@kasperrt/smolter/react/vite` return
136
142
  ### Manual routes
137
143
 
138
144
  ```tsx
139
- import { createRouter, defineRoute } from "@kasperrt/smolter/react";
145
+ import { createDataRouter, defineRoute } from "@kasperrt/smolter/react";
140
146
  import * as tasks from "./tasks/route";
141
147
  import * as task from "./task/route";
142
148
 
143
149
  export const tasksRoute = defineRoute("/tasks", tasks);
144
150
  export const taskRoute = defineRoute("/tasks/:id", task);
145
151
  export const routes = [tasksRoute, taskRoute];
146
- export const router = createRouter(routes, { browser: true });
152
+ export const router = createDataRouter(routes, { browser: true });
147
153
  ```
148
154
 
149
155
  Or pass handlers directly to `defineRoute(path, { loader, action, default: View })`.
@@ -171,11 +177,12 @@ export { default } from "./view";
171
177
  export { loader } from "./loader";
172
178
  export { action } from "./action";
173
179
 
174
- export { clientLoader } from "./client-loader";
175
- export { clientAction } from "./client-action";
180
+ export { clientLoader } from "./clientLoader";
181
+ export { clientAction } from "./clientAction";
176
182
 
177
- export { shouldRevalidate } from "./should-revalidate";
178
- export { HydrateFallback, ErrorBoundary } from "../../boundaries";
183
+ export { shouldRevalidate } from "./shouldRevalidate";
184
+ export { HydrateFallback } from "./HydrateFallback";
185
+ export { ErrorBoundary } from "./ErrorBoundary";
179
186
  ```
180
187
 
181
188
  With the optional server integration:
@@ -187,7 +194,7 @@ With the optional server integration:
187
194
  - SSR executes the server loader and hydration reuses that data.
188
195
 
189
196
  ```ts
190
- // client-loader.ts
197
+ // clientLoader.ts
191
198
  import type { ClientLoaderArgs } from "@kasperrt/smolter/react/client";
192
199
  import type { loader } from "./loader";
193
200
 
@@ -197,7 +204,7 @@ export async function clientLoader({ serverLoader }: ClientLoaderArgs<typeof loa
197
204
  ```
198
205
 
199
206
  ```ts
200
- // client-action.ts
207
+ // clientAction.ts
201
208
  import type { ClientActionArgs } from "@kasperrt/smolter/react/client";
202
209
  import type { action } from "./action";
203
210
 
@@ -258,7 +265,7 @@ routerPlugin({
258
265
 
259
266
  Each table exports `routes`. The standalone examples contain both tables.
260
267
 
261
- Without the optional integration, `createRouter` invokes handlers locally:
268
+ Without the optional integration, `createDataRouter` invokes handlers locally:
262
269
  client handlers take precedence, with ordinary handlers as fallbacks.
263
270
  `loadRouter` selects server handlers only. Core-only usage does not create HTTP
264
271
  endpoints or compiler boundaries by itself.
@@ -319,7 +326,7 @@ Fetcher results belong to `fetcher.data`, not `useActionData()`.
319
326
  ## Decide when to revalidate
320
327
 
321
328
  ```ts
322
- // should-revalidate.ts
329
+ // shouldRevalidate.ts
323
330
  import type { RevalidationArgs } from "@kasperrt/smolter/react";
324
331
 
325
332
  export function shouldRevalidate({ actionResult, defaultShouldRevalidate }: RevalidationArgs) {
@@ -344,7 +351,25 @@ and explicit `fetcher.load()` calls always load their destination.
344
351
  This controls loader refreshes; it does not rerun actions or reload the document.
345
352
  Existing page data stays visible during manual and post-action refreshes.
346
353
 
347
- `const revalidate = useRevalidator(); await revalidate()` requests a manual refresh.
354
+ `useRevalidator()` returns an async function. Target a registered route ID or path
355
+ pattern to refresh only its active data, including loaded fetchers:
356
+
357
+ ```tsx
358
+ const revalidate = useRevalidator();
359
+
360
+ await revalidate({ route: "/tasks/:id" });
361
+ await revalidate({ route: "root" });
362
+ await revalidate({ route: ["root", "/tasks/:id"] });
363
+ ```
364
+
365
+ Without a target, `revalidate()` refreshes the active page, loaded fetchers, and
366
+ registered root/layout data. `shouldRevalidate` still applies. Inactive routes
367
+ are not loaded, unknown targets do nothing, and revalidation never navigates.
368
+
369
+ The Preact root integration uses `root` as its default ID. Configure its
370
+ `loaderPath` with a registered data route to enable root refreshes. Custom React
371
+ or Preact layouts can use `router.registerRevalidator(id, callback)`; call its
372
+ returned cleanup function when the layout is removed.
348
373
  The task-detail examples return `{ changed: false }` for an unchanged title;
349
374
  the corresponding loader is skipped.
350
375
 
@@ -381,17 +406,17 @@ const hydrationJSON = serialize(router.getSnapshot());
381
406
 
382
407
  Create a fresh router per request. Embed the serialized snapshot in a JSON
383
408
  script, restore it with `deserialize` from `@kasperrt/smolter/react/client`, and pass it as
384
- `hydrationData` to `createRouter`. This preserves loader errors through hydration
409
+ `hydrationData` to `createDataRouter`. This preserves loader errors through hydration
385
410
  without exposing server stacks or causes. Error boundaries should display the
386
411
  error message, not its stack.
387
412
 
388
413
  ```ts
389
414
  import { deserialize } from "@kasperrt/smolter/react/client";
390
- import { createRouter } from "@kasperrt/smolter/react";
415
+ import { createDataRouter } from "@kasperrt/smolter/react";
391
416
  import routes from "virtual:smolter/routes";
392
417
 
393
418
  const data = document.getElementById("smolter-data");
394
- const router = createRouter(routes, {
419
+ const router = createDataRouter(routes, {
395
420
  browser: true,
396
421
  hydrationData: data?.textContent ? deserialize(data.textContent) : undefined,
397
422
  });
@@ -406,7 +431,7 @@ The SSR examples use the same server route definitions for HTML and data request
406
431
 
407
432
  ## Package entries
408
433
 
409
- Use `@kasperrt/smolter/react` for React 18+ and `@kasperrt/smolter/preact` for Preact 10.25+.
434
+ Use `@kasperrt/smolter/react` for React 18+ and `@kasperrt/smolter/preact` for Preact 11.0.1+.
410
435
  Choose one renderer for your application. Both expose the same routing API:
411
436
 
412
437
  | Purpose | React | Preact |
@@ -421,39 +446,196 @@ The main renderer entries never import server or build modules. Named imports
421
446
  are tree-shaken; optional transport is included only when you import `/client`.
422
447
  Server entries do not import either UI renderer. Vite entries are build-time only.
423
448
 
424
- **Migrating to 0.2.0:** replace `/core` imports for types, `createRouter`, and
449
+ **Migrating renderer subpaths:** replace `/core` imports for types, router factories, and
425
450
  `defineRoute` with your renderer entry. Use `/utils` for `matchRoutes`,
426
451
  `internalURL` and `safeWrapAsync`. Move `/client`, `/server`,
427
452
  `/files`, and `/vite` imports to the corresponding renderer-prefixed path above.
428
453
  The bare package root and old top-level subpaths are no longer exported.
429
454
 
455
+ See the [0.3 migration guide](https://kasperrt.github.io/smolter/react/docs/migration-0.3/) and [changelog](./CHANGELOG.md).
456
+
457
+ **Router factory migration:** code that previously used `createRouter` with
458
+ loaders, actions, fetchers or revalidation must now use `createDataRouter`.
459
+ `createRouter` now creates a navigation-only router. SSR and file-router
460
+ application helpers select `createDataRouter` automatically.
461
+
430
462
  On JSR, install `@kasperrt/smolter` using the
431
463
  [JSR package instructions](https://jsr.io/docs/npm-compatibility) and import
432
- `@kasperrt/smolter/react` or `@kasperrt/smolter/preact`. Its `core`, `client`,
433
- `server`, and `files` subpaths match npm. The Vite plugin is npm-only:
464
+ `@kasperrt/smolter/react` or `@kasperrt/smolter/preact`. Its renderer, client, server and utility subpaths match npm. The Vite plugin is npm-only:
434
465
  use `@kasperrt/smolter/react/vite` from the npm package when adding file routing.
435
466
 
467
+ ## Application integration
468
+
469
+ Import routing, forms and data hooks from `@kasperrt/smolter/preact`.
470
+ Document rendering is optional: `@kasperrt/smolter/preact/document` supplies
471
+ `Document`, `Meta`, `Links`, `Scripts` and `ScrollRestoration`.
472
+ `@kasperrt/smolter/preact/client` supplies `mountApplication`,
473
+ `configureApplication` and the server transport helpers. Mounting connects
474
+ hydration, root data and navigation presentation to the same core router.
475
+ These modules remain tree-shakable; basic routing does not import the document
476
+ or mounting code. The size table also measures all three together, because
477
+ moving APIs to subpaths does not make their implementation disappear.
478
+
479
+ ```ts
480
+ // vite.config.ts
481
+ import { defineConfig } from "vite";
482
+ import { applicationPlugins } from "@kasperrt/smolter/preact/vite";
483
+
484
+ export default defineConfig({
485
+ plugins: applicationPlugins({ directory: "src/routes" }),
486
+ });
487
+ ```
488
+
489
+ ```tsx
490
+ // src/entry.client.tsx
491
+ import routes from "virtual:smolter/routes";
492
+ import { mountApplication } from "@kasperrt/smolter/preact/client";
493
+ import * as root from "./root";
494
+
495
+ mountApplication(routes, root);
496
+ ```
497
+
498
+ `root` exports a default component containing `Outlet` and a `Layout` wrapping
499
+ it in `Document`, a head, and a body containing `Scripts`. Its optional loader
500
+ supplies root data. `renderApplication(request, routes, root, entry)` from
501
+ `preact/server` renders the same tree and serializes hydration data;
502
+ `dispatchRoute(request, routes)` handles route data requests. Server rendering
503
+ requires the optional `preact-render-to-string` peer.
504
+
505
+ Deployment-specific URL mapping can be configured once at startup with
506
+ `configureApplication({ resolveURL, isDocumentHandoff, leaveDocument,
507
+ entryScript })`. Do not put user-specific or request-specific state there.
508
+ The library does not define application domains, ports, or branding.
509
+
510
+ `utils/http` contains the Node request/response adapter; `utils/assets` contains
511
+ manifest/preload helpers; `utils/responses` contains response normalization.
512
+ Keep the first two in server/build entrypoints. Browser application imports do
513
+ not include them.
514
+
515
+ ### Virtual modules and entrypoints
516
+
517
+ `virtual:smolter/routes` is the default route-array export generated by the Vite
518
+ plugin from `src/routes`. It is not a physical file or a runtime package.
519
+ The server build retains loaders/actions; the browser build strips their
520
+ re-exports and installs server proxies. `entry.client.tsx` and `entry.server.tsx`
521
+ are application-owned files wiring those manifests to hydration and SSR.
522
+ Manual tables can be imported directly instead.
523
+
524
+ See the [virtual routes guide](https://kasperrt.github.io/smolter/react/docs/virtual-routes)
525
+ for the exact contract, TypeScript declaration, and both entrypoint examples.
526
+
527
+ ### Express hosting
528
+
529
+ Keep Express in control of metrics, tracing, authentication and static files.
530
+ Mount `createRequestHandler({ build, entry })` from
531
+ `@kasperrt/smolter/utils/http` after those middleware. `build` is the compiled
532
+ server entry and `entry` is the browser entry URL from Vite's manifest. Do not
533
+ consume request bodies with Express body parsers before this handler.
534
+ The adapter has no Express dependency and forwards failures to `next(error)`.
535
+
536
+ See the [Express guide](https://kasperrt.github.io/smolter/react/docs/express)
537
+ for a complete server example and current buffering limitations.
538
+
539
+ ## Resource subscriptions
540
+
541
+ `@kasperrt/smolter/react/data` adds subscriptions to named resources when their
542
+ lifetime is independent of URL navigation. The state and cancellation logic
543
+ lives in core; the entry adds React bindings without DOM or HTTP globals.
544
+
545
+ ```ts
546
+ import { createResourceRouter } from "@kasperrt/smolter/react/data";
547
+
548
+ const resources = createResourceRouter(new Map([
549
+ ["settings", {
550
+ loader: async ({ signal }) => ({
551
+ data: await readSettings(signal),
552
+ error: "",
553
+ }),
554
+ action: async ({ input, signal }) => ({
555
+ data: await saveSettings(input, signal),
556
+ error: "",
557
+ }),
558
+ }],
559
+ ]));
560
+ ```
561
+
562
+ `ResourceProvider` owns the store lifetime. `Resource` selects a resource by
563
+ `routeId` and params. `useResourceData` reads it; `useRouteLoaderData` reads a
564
+ named resource. `useResourceFetcher` returns `[state, operations]`, with
565
+ independent `load` and `submit` operations. Successful mutations consult the
566
+ resource's `shouldRevalidate` before reloading its data. Last-subscriber cleanup
567
+ aborts pending work; `persistent` keeps a resource until explicitly disposed.
568
+ This optional entry does not contribute to the base React bundle measurement.
569
+
436
570
  ## Size and checks
437
571
 
438
- `pnpm size` measures every browser export with esbuild, Terser, and gzip level 9.
439
- Frameworks are excluded; library dependencies are included. It compares the same
440
- build settings against pinned wouter entries. Both adapters must be strictly
441
- smaller than their corresponding wouter entry; exceeding either fails the check. Optional server transport is measured separately and included in SSR
442
- example labels. File discovery is build-only.
572
+ | Need | Public import |
573
+ | --- | --- |
574
+ | Navigation only | `@kasperrt/smolter/react` or `/preact`, using `createRouter` |
575
+ | Loaders, actions, forms and fetchers | Same entry, using `createDataRouter` |
576
+ | Browser server-request transport | Renderer `/client` subpath |
577
+ | Preact application hydration | `@kasperrt/smolter/preact/client` |
578
+ | Preact document components | `@kasperrt/smolter/preact/document` |
579
+ | Lazy Preact route modules | `@kasperrt/smolter/preact/lazy-route` |
580
+ | Optional React resources | `@kasperrt/smolter/react/data` |
581
+ | Server rendering / Vite integration | Renderer `/server` / `/vite` subpaths; keep out of browser imports |
582
+
583
+ The measurements below describe retained exports, not the cost of an import
584
+ statement. Browser transport rows include the router and transport together;
585
+ application rows include the listed modules together. Server and Vite modules
586
+ have no browser cost when kept in their intended server/build entrypoints.
587
+
588
+ Use `createRouter` for navigation only, or `createDataRouter` for loaders,
589
+ actions, fetchers and revalidation. Both are named exports from
590
+ `@kasperrt/smolter/preact` and `/react`. No extra package or plugin is required.
591
+ The production bundler can remove the data engine when only `createRouter` is used.
592
+
593
+ `pnpm size` regenerates this table and the site's size data using identical
594
+ minifiers. The routing-only gate compares our selected exports with wouter's
595
+ routing subset: `Router`, `Route`, `Switch`, `Link`, `useParams`, and `useLocation`.
596
+ Both complete wouter entries are reported too. The APIs differ; this is not
597
+ a claim of identical features.
443
598
 
444
599
  <!-- size-comparison:start -->
445
600
  | Browser exports | Gzip bytes |
446
601
  | --- | ---: |
447
- | `@kasperrt/smolter/react` | 2445 |
448
- | `@kasperrt/smolter/preact` | 2445 |
449
- | `@kasperrt/smolter/react + @kasperrt/smolter/react/client` | 2782 |
450
- | `@kasperrt/smolter/preact + @kasperrt/smolter/preact/client` | 2781 |
602
+ | `React Router: declarative routing subset` | 12882 |
603
+ | `React Router: data routing subset` | 29780 |
604
+ | `React: routing-only named exports` | 1245 |
605
+ | `Preact: routing-only named exports` | 1405 |
606
+ | `@kasperrt/smolter/react` | 2873 |
607
+ | `@kasperrt/smolter/preact` | 4748 |
608
+ | `@kasperrt/smolter/react + @kasperrt/smolter/react/client` | 3870 |
609
+ | `@kasperrt/smolter/preact + server transport` | 5697 |
610
+ | `@kasperrt/smolter/preact + client + document (application without lazy routes)` | 8033 |
611
+ | `@kasperrt/smolter/preact + client + document + lazy-route (file-routed application)` | 8317 |
612
+ | `@kasperrt/smolter/react/data (optional resource API)` | 2962 |
613
+ | `React: common routing/data exports` | 2683 |
614
+ | `Preact: common routing/data exports` | 2966 |
615
+ | `wouter: routing subset (Router, Route, Switch, Link, useParams, useLocation)` | 2854 |
616
+ | `wouter-preact: same routing subset` | 2211 |
451
617
  | `wouter@3.13.0` | 3091 |
452
618
  | `wouter-preact@3.13.0` | 2450 |
453
619
  | `react-router@8.4.0` | 60994 |
454
620
  <!-- size-comparison:end -->
455
621
 
456
- The table is generated by `pnpm size` from all exports of each listed entry,
622
+ The common-export rows measure the same named API set: `createDataRouter`,
623
+ `defineRoute`, `RouterProvider`, `Routes`, `Link`, `Form`, `useRouter`,
624
+ `useLoaderData`, `useActionData`, `useParams`, `useLocation`,
625
+ `useRevalidator` and `useFetcher`. Preact’s link and submission APIs currently
626
+ have additional behavior; equal names do not imply identical implementations.
627
+ React Router's declarative subset retains `BrowserRouter`, `Routes`, `Route`,
628
+ `Link`, `useParams`, and `useLocation`. Its data subset retains
629
+ `createBrowserRouter`, `RouterProvider`, `Link`, `Form`, `useLoaderData`,
630
+ `useActionData`, `useParams`, `useLocation`, `useRevalidator`, and `useFetcher`.
631
+ React Router has no dedicated Preact entry; these are React API measurements,
632
+ not a Preact compatibility build. The selected APIs have different capabilities.
633
+
634
+ The routing-only rows retain `createRouter`, `defineRoute`, `RouterProvider`,
635
+ `Routes`, `Link`, `useParams` and `useLocation`. Full Preact also exposes richer
636
+ application APIs. These tiers overlap: do not add the rows together.
637
+
638
+ The table is generated by `pnpm size` from the named subsets above or all exports of each other listed entry,
457
639
  using production ESM bundles, esbuild, Terser, and gzip level 9. React, React DOM,
458
640
  and Preact are external in every bundle. Transitive library dependencies are
459
641
  included. React Router provides a broader API; this compares complete entry
@@ -461,6 +643,43 @@ sizes, not equivalent feature sets or the minimum code for one route.
461
643
  `@kasperrt/smolter/react/client` adds browser transport for server loaders/actions; server-only
462
644
  rendering code and the Vite plugin are not shipped to the browser.
463
645
 
646
+ ### Routing without data hooks
647
+
648
+ ```tsx
649
+ import { render } from "preact";
650
+ import {
651
+ createRouter,
652
+ defineRoute,
653
+ Link,
654
+ RouterProvider,
655
+ Routes,
656
+ } from "@kasperrt/smolter/preact";
657
+ import Home from "./routes/home";
658
+ import About from "./routes/about";
659
+
660
+ const router = createRouter([
661
+ defineRoute("/", { default: Home }),
662
+ defineRoute("/about", { default: About }),
663
+ ], { browser: true });
664
+
665
+ render(
666
+ <RouterProvider router={router}>
667
+ <nav>
668
+ <Link href="/">Home</Link>
669
+ <Link href="/about">About</Link>
670
+ </nav>
671
+ <Routes />
672
+ </RouterProvider>,
673
+ document.getElementById("app")!,
674
+ );
675
+ ```
676
+
677
+ Add `useParams` for dynamic paths such as `/tasks/:id` and `useLocation` for
678
+ the current URL. Both are included in the routing-only measurement, even though
679
+ this example does not need them. Switch the factory to `createDataRouter` when
680
+ adding loaders, actions or fetchers, importing it from the same entry. SSR hydration and file-routed applications use the
681
+ larger client/document/lazy-route tier; they are not represented by the routing-only number.
682
+
464
683
  Exact current measurements are generated in
465
684
  [`packages/ui/src/size.json`](./packages/ui/src/size.json).
466
685
 
@@ -486,8 +705,31 @@ PRs. [Release workflow](https://github.com/kasperrt/smolter/blob/main/RELEASING.
486
705
  To test a local package build in another project:
487
706
 
488
707
  ```sh
489
- pnpm --dir packages/smolter pack --pack-destination ../..
708
+ pnpm pack
490
709
  pnpm add /absolute/path/to/kasperrt-smolter-0.0.1.tgz
491
710
  ```
492
711
 
493
712
  Workspace structure and rendering checks are documented in [CONTRIBUTING.md](./CONTRIBUTING.md).
713
+
714
+ ## Deployment updates
715
+
716
+ For production tabs that survive a deployment, add `deploymentPlugin()` from
717
+ `@kasperrt/smolter/react/vite` or `/preact/vite`, then connect the router:
718
+
719
+ ```ts
720
+ import deployment from "virtual:smolter/deployment";
721
+ import { watchDeployment } from "@kasperrt/smolter/preact/client";
722
+
723
+ watchDeployment(router, deployment);
724
+ ```
725
+
726
+ Serve the emitted `smolter-manifest.json` with `Cache-Control: no-store` and HTML
727
+ with `no-cache`. Navigation checks its build version before loading another route;
728
+ a newer version reloads the destination, including its query and hash. Vite chunk
729
+ failures are checked again. Hash-only links skip the check, failed checks continue
730
+ normally, and a session marker prevents reload loops. Development/SSR builds
731
+ disable the helper. This optional client module adds nothing to the routing-only
732
+ entrypoints and never replays form submissions.
733
+
734
+ See [deployment updates](https://kasperrt.github.io/smolter/react/docs/deployments/)
735
+ for Express, CDN paths, TypeScript declarations and unsaved-state handling.
@@ -0,0 +1,12 @@
1
+ import type { NavigationRouter } from "../core/index.ts";
2
+ /** Build identity and the fresh, non-cacheable manifest URL emitted by deploymentPlugin. */
3
+ export interface DeploymentOptions {
4
+ version: string;
5
+ manifestURL: string;
6
+ /** Maximum time to wait for the update check before continuing navigation. */
7
+ timeout?: number;
8
+ /** Override document navigation, for example to ask about unsaved changes. */
9
+ reload?: (destination: URL) => void;
10
+ }
11
+ /** Check for deployment changes before navigation. Returns a cleanup function. */
12
+ export declare function watchDeployment(router: NavigationRouter, options: DeploymentOptions | null): () => void;
@@ -0,0 +1,102 @@
1
+ //#region .generated/client/deployment.ts
2
+ /** Check for deployment changes before navigation. Returns a cleanup function. */
3
+ function watchDeployment(router, options) {
4
+ if (!options || typeof window === "undefined") return () => {};
5
+ const { version, manifestURL, timeout = 2e3 } = options;
6
+ const navigate = router.navigate;
7
+ const dispose = router.dispose;
8
+ const manifest = new URL(manifestURL, location.href);
9
+ const key = `smolter:deployment:${manifest.href}:${version}`;
10
+ let sequence = 0;
11
+ let stopped = false;
12
+ let replay = false;
13
+ let destination = new URL(location.href);
14
+ let pending;
15
+ let controller;
16
+ function changed() {
17
+ if (pending) return pending;
18
+ controller = new AbortController();
19
+ const timer = setTimeout(() => controller?.abort(), timeout);
20
+ pending = fetch(manifest, {
21
+ cache: "no-store",
22
+ signal: controller.signal
23
+ }).then(async (response) => {
24
+ if (!response.ok) return false;
25
+ const data = await response.json();
26
+ return typeof data === "object" && data !== null && "version" in data && typeof data.version === "string" && data.version.length > 0 && data.version !== version;
27
+ }).catch(() => false).finally(() => {
28
+ clearTimeout(timer);
29
+ pending = void 0;
30
+ });
31
+ return pending;
32
+ }
33
+ async function reload(target) {
34
+ const allowed = await Promise.resolve().then(() => {
35
+ if (sessionStorage.getItem(key)) return false;
36
+ sessionStorage.setItem(key, "1");
37
+ return true;
38
+ }).catch(() => false);
39
+ if (allowed && !stopped) {
40
+ if (options?.reload) options.reload(target);
41
+ if (!options?.reload) {
42
+ if (target.href === location.href) location.reload();
43
+ if (target.href !== location.href) location.assign(target.href);
44
+ }
45
+ }
46
+ return allowed && !stopped;
47
+ }
48
+ const guarded = async (href, operation) => {
49
+ const target = new URL(href, router.getSnapshot().url);
50
+ const currentURL = new URL(router.getSnapshot().url);
51
+ const current = ++sequence;
52
+ destination = target;
53
+ if (target.origin === location.origin && (target.pathname !== currentURL.pathname || target.search !== currentURL.search) && await changed()) {
54
+ if (current !== sequence || stopped || await reload(target)) return;
55
+ }
56
+ if (current === sequence && !stopped) await navigate(href, operation);
57
+ };
58
+ const pop = (event) => {
59
+ if (replay) return;
60
+ const target = new URL(location.href);
61
+ const previous = new URL(router.getSnapshot().url);
62
+ if (target.pathname === previous.pathname && target.search === previous.search) return;
63
+ event.stopImmediatePropagation();
64
+ const current = ++sequence;
65
+ destination = target;
66
+ changed().then(async (updated) => {
67
+ if (stopped || current !== sequence) return;
68
+ if (updated && await reload(target)) return;
69
+ replay = true;
70
+ window.dispatchEvent(new PopStateEvent("popstate", { state: event.state }));
71
+ replay = false;
72
+ });
73
+ };
74
+ const preloadError = () => {
75
+ if (router.getSnapshot().state === "submitting") return;
76
+ const current = sequence;
77
+ const target = destination;
78
+ changed().then((updated) => {
79
+ if (updated && current === sequence && !stopped) return reload(target);
80
+ });
81
+ };
82
+ function cleanup() {
83
+ stopped = true;
84
+ sequence++;
85
+ controller?.abort();
86
+ window.removeEventListener("popstate", pop, true);
87
+ window.removeEventListener("vite:preloadError", preloadError);
88
+ if (router.navigate === guarded) router.navigate = navigate;
89
+ if (router.dispose === guardedDispose) router.dispose = dispose;
90
+ }
91
+ function guardedDispose() {
92
+ cleanup();
93
+ dispose();
94
+ }
95
+ router.navigate = guarded;
96
+ router.dispose = guardedDispose;
97
+ window.addEventListener("popstate", pop, true);
98
+ window.addEventListener("vite:preloadError", preloadError);
99
+ return cleanup;
100
+ }
101
+ //#endregion
102
+ export { watchDeployment };
@@ -0,0 +1,10 @@
1
+ //#region .generated/client/deserialize.ts
2
+ /** Restore a trusted snapshot produced by @kasperrt/smolter/server serialize, including its route error. */
3
+ function deserialize(json) {
4
+ const snapshot = JSON.parse(json);
5
+ const error = snapshot.error;
6
+ if (error) snapshot.error = Object.assign(new Error(error.message), { name: error.name });
7
+ return snapshot;
8
+ }
9
+ //#endregion
10
+ export { deserialize };
@@ -7,6 +7,7 @@ export type { ClientActionArgs, ClientLoaderArgs } from "../core/index.ts";
7
7
  export { deserialize } from "./deserialize.ts";
8
8
  /** Browser-safe route exports, excluding server implementation code. */
9
9
  export interface ClientRouteModule {
10
+ id?: string;
10
11
  default?: unknown;
11
12
  HydrateFallback?: unknown;
12
13
  ErrorBoundary?: unknown;
@@ -21,3 +22,4 @@ export interface ServerHandlers {
21
22
  }
22
23
  /** Browser transport for server routes. File-router builds generate this call. */
23
24
  export declare function serverRoute<M extends RouteModule = RouteModule>(path: string, module: ClientRouteModule, handlers?: ServerHandlers): Route<M>;
25
+ export { type DeploymentOptions, watchDeployment } from "./deployment.ts";
@@ -0,0 +1,47 @@
1
+ import { deserialize } from "./deserialize.js";
2
+ import { watchDeployment } from "./deployment.js";
3
+ //#region .generated/client/index.ts
4
+ /** Browser transport for server routes. File-router builds generate this call. */
5
+ function serverRoute(path, module, handlers = {
6
+ loader: true,
7
+ action: true
8
+ }) {
9
+ const remote = async ({ request, signal }) => {
10
+ const url = new URL(request.url);
11
+ const response = await fetch(`/_smolter?url=${encodeURIComponent(url.pathname + url.search)}`, {
12
+ method: request.method,
13
+ signal,
14
+ ...request.method !== "GET" && { body: await request.formData() }
15
+ });
16
+ const location = response.headers.get("X-Smolter-Redirect");
17
+ if (location) return new Response(null, {
18
+ status: 303,
19
+ headers: { Location: location }
20
+ });
21
+ const data = await response.json();
22
+ if (!response.ok) return new Error(data.error ?? "Route request failed");
23
+ return data;
24
+ };
25
+ return {
26
+ ...module,
27
+ path,
28
+ ...handlers.loader && { loader: remote },
29
+ ...handlers.action && { action: remote },
30
+ ...module.clientLoader && { clientLoader: (args) => module.clientLoader({
31
+ ...args,
32
+ serverLoader: () => remote(args)
33
+ }) },
34
+ ...module.clientAction && { clientAction: (args) => {
35
+ const request = args.request.clone();
36
+ return module.clientAction({
37
+ ...args,
38
+ serverAction: () => remote({
39
+ ...args,
40
+ request: request.clone()
41
+ })
42
+ });
43
+ } }
44
+ };
45
+ }
46
+ //#endregion
47
+ export { deserialize, serverRoute, watchDeployment };