@kasperrt/smolter 0.2.0 → 0.3.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 (264) hide show
  1. package/{src/internal → .generated}/client/index.ts +1 -0
  2. package/.generated/core/environment.ts +43 -0
  3. package/{src/internal → .generated}/core/errors.ts +1 -1
  4. package/{src/internal → .generated}/core/index.ts +1 -0
  5. package/.generated/core/navigation-router.ts +107 -0
  6. package/.generated/core/navigation.ts +147 -0
  7. package/.generated/core/presentation.ts +108 -0
  8. package/.generated/core/resource-types.ts +44 -0
  9. package/.generated/core/resources.ts +444 -0
  10. package/.generated/core/responses.ts +86 -0
  11. package/{src/internal → .generated}/core/router.ts +136 -63
  12. package/{src/internal → .generated}/core/serialize.ts +7 -2
  13. package/.generated/core/store.ts +25 -0
  14. package/.generated/core/submission.ts +116 -0
  15. package/{src/internal → .generated}/core/types.ts +18 -1
  16. package/.generated/core/url.ts +29 -0
  17. package/.generated/core/utils.ts +5 -0
  18. package/.generated/preact/await.tsx +41 -0
  19. package/.generated/preact/client.tsx +93 -0
  20. package/.generated/preact/context.ts +59 -0
  21. package/.generated/preact/document.tsx +198 -0
  22. package/{src/internal → .generated}/preact/fetcher.ts +43 -23
  23. package/.generated/preact/form.ts +58 -0
  24. package/{src/internal → .generated}/preact/hooks.ts +25 -28
  25. package/.generated/preact/index.ts +34 -0
  26. package/.generated/preact/lazy-route.ts +100 -0
  27. package/.generated/preact/navigation-state.ts +9 -0
  28. package/.generated/preact/navigation.tsx +147 -0
  29. package/.generated/preact/root-context.ts +21 -0
  30. package/.generated/preact/root-data.ts +45 -0
  31. package/.generated/preact/route-hooks.tsx +170 -0
  32. package/.generated/preact/route-types.ts +76 -0
  33. package/.generated/preact/router.tsx +107 -0
  34. package/{src/internal → .generated}/preact/routes.ts +17 -9
  35. package/.generated/react/context.ts +59 -0
  36. package/.generated/react/data.ts +25 -0
  37. package/{src/internal → .generated}/react/fetcher.ts +13 -11
  38. package/.generated/react/form.ts +58 -0
  39. package/{src/internal → .generated}/react/hooks.ts +9 -14
  40. package/{src/internal/preact → .generated/react}/index.ts +5 -1
  41. package/{src/internal → .generated}/react/link.ts +13 -21
  42. package/.generated/react/resources/components.tsx +404 -0
  43. package/{src/internal → .generated}/react/routes.ts +17 -9
  44. package/.generated/server/application.tsx +185 -0
  45. package/.generated/server/assets.ts +19 -0
  46. package/.generated/server/handler.ts +69 -0
  47. package/.generated/server/http.ts +55 -0
  48. package/{src/internal → .generated}/server/index.ts +3 -3
  49. package/.generated/server/node.ts +4 -0
  50. package/.generated/server/preact.ts +7 -0
  51. package/.generated/vite/application/browser-boundary.ts +42 -0
  52. package/.generated/vite/application/lazy-files.ts +136 -0
  53. package/.generated/vite/application/vite.ts +144 -0
  54. package/{src/preact-vite.ts → .generated/vite/preact.ts} +9 -3
  55. package/{src/react-vite.ts → .generated/vite/react.ts} +3 -3
  56. package/CHANGELOG.md +54 -0
  57. package/README.md +227 -35
  58. package/dist/client/deserialize.js +10 -0
  59. package/dist/{internal/client → client}/index.d.ts +1 -0
  60. package/dist/client/index.js +46 -0
  61. package/dist/core/environment.d.ts +12 -0
  62. package/dist/core/environment.js +23 -0
  63. package/dist/core/errors.js +7 -0
  64. package/dist/{internal/core → core}/index.d.ts +1 -0
  65. package/dist/core/match.js +33 -0
  66. package/dist/core/navigation-router.d.ts +15 -0
  67. package/dist/core/navigation-router.js +64 -0
  68. package/dist/core/navigation.d.ts +15 -0
  69. package/dist/core/navigation.js +92 -0
  70. package/dist/core/presentation.d.ts +5 -0
  71. package/dist/core/presentation.js +69 -0
  72. package/dist/core/resource-types.d.ts +37 -0
  73. package/dist/core/resources.d.ts +19 -0
  74. package/dist/core/resources.js +345 -0
  75. package/dist/core/responses.d.ts +15 -0
  76. package/dist/core/responses.js +48 -0
  77. package/dist/{internal/core → core}/router.d.ts +1 -1
  78. package/dist/core/router.js +204 -0
  79. package/dist/{internal/core → core}/serialize.d.ts +2 -0
  80. package/dist/core/serialize.js +17 -0
  81. package/dist/core/store.d.ts +6 -0
  82. package/dist/core/store.js +21 -0
  83. package/dist/core/submission.d.ts +30 -0
  84. package/dist/core/submission.js +41 -0
  85. package/dist/{internal/core → core}/types.d.ts +16 -1
  86. package/dist/core/url.d.ts +7 -0
  87. package/dist/core/url.js +12 -0
  88. package/dist/core/utils.d.ts +5 -0
  89. package/dist/core/utils.js +5 -0
  90. package/dist/files/index.js +64 -0
  91. package/dist/preact/await.d.ts +7 -0
  92. package/dist/preact/await.js +19 -0
  93. package/dist/preact/client.d.ts +7 -0
  94. package/dist/preact/client.js +67 -0
  95. package/dist/preact/context.d.ts +18 -0
  96. package/dist/preact/context.js +23 -0
  97. package/dist/preact/document.d.ts +15 -0
  98. package/dist/preact/document.js +155 -0
  99. package/dist/{internal/preact → preact}/fetcher.d.ts +9 -5
  100. package/dist/preact/fetcher.js +45 -0
  101. package/dist/{internal/preact → preact}/form.d.ts +5 -6
  102. package/dist/preact/form.js +21 -0
  103. package/dist/{internal/preact → preact}/hooks.d.ts +7 -6
  104. package/dist/preact/hooks.js +25 -0
  105. package/dist/preact/index.d.ts +18 -0
  106. package/dist/preact/index.js +15 -0
  107. package/dist/preact/lazy-route.d.ts +19 -0
  108. package/dist/preact/lazy-route.js +51 -0
  109. package/dist/preact/navigation-state.d.ts +2 -0
  110. package/dist/preact/navigation-state.js +9 -0
  111. package/dist/preact/navigation.d.ts +28 -0
  112. package/dist/preact/navigation.js +60 -0
  113. package/dist/preact/root-context.d.ts +13 -0
  114. package/dist/preact/root-context.js +9 -0
  115. package/dist/preact/root-data.d.ts +8 -0
  116. package/dist/preact/root-data.js +28 -0
  117. package/dist/preact/route-hooks.d.ts +32 -0
  118. package/dist/preact/route-hooks.js +94 -0
  119. package/dist/preact/route-types.d.ts +54 -0
  120. package/dist/preact/router.d.ts +16 -0
  121. package/dist/preact/router.js +63 -0
  122. package/dist/{internal/preact → preact}/routes.d.ts +1 -1
  123. package/dist/preact/routes.js +12 -0
  124. package/dist/{internal/preact → preact}/runtime.js +1 -1
  125. package/dist/react/context.d.ts +18 -0
  126. package/dist/react/context.js +23 -0
  127. package/dist/react/data.d.ts +5 -0
  128. package/dist/react/data.js +3 -0
  129. package/dist/{internal/react → react}/fetcher.d.ts +2 -2
  130. package/dist/react/fetcher.js +34 -0
  131. package/dist/{internal/react → react}/form.d.ts +5 -6
  132. package/dist/react/form.js +21 -0
  133. package/dist/react/hooks.js +25 -0
  134. package/dist/{internal/react → react}/index.d.ts +1 -1
  135. package/dist/react/index.js +10 -0
  136. package/dist/{internal/react → react}/link.d.ts +2 -2
  137. package/dist/react/link.js +27 -0
  138. package/dist/react/resources/components.d.ts +64 -0
  139. package/dist/react/resources/components.js +239 -0
  140. package/dist/{internal/react → react}/routes.d.ts +1 -1
  141. package/dist/react/routes.js +12 -0
  142. package/dist/react/runtime.js +1 -0
  143. package/dist/server/application.d.ts +4 -0
  144. package/dist/server/application.js +119 -0
  145. package/dist/server/assets.d.ts +5 -0
  146. package/dist/server/assets.js +7 -0
  147. package/dist/server/handler.d.ts +19 -0
  148. package/dist/server/handler.js +22 -0
  149. package/dist/server/http.d.ts +4 -0
  150. package/dist/server/http.js +27 -0
  151. package/dist/server/index.js +76 -0
  152. package/dist/server/node.d.ts +3 -0
  153. package/dist/server/node.js +3 -0
  154. package/dist/server/preact.d.ts +3 -0
  155. package/dist/server/preact.js +4 -0
  156. package/dist/vite/application/browser-boundary.d.ts +3 -0
  157. package/dist/vite/application/browser-boundary.js +35 -0
  158. package/dist/vite/application/lazy-files.d.ts +2 -0
  159. package/dist/vite/application/lazy-files.js +87 -0
  160. package/dist/vite/application/vite.d.ts +9 -0
  161. package/dist/vite/application/vite.js +101 -0
  162. package/dist/vite/index.js +104 -0
  163. package/dist/{preact-vite.d.ts → vite/preact.d.ts} +5 -3
  164. package/dist/vite/preact.js +15 -0
  165. package/dist/{react-vite.d.ts → vite/react.d.ts} +3 -3
  166. package/dist/vite/react.js +14 -0
  167. package/dist/vite/transport.js +35 -0
  168. package/jsr.json +28 -16
  169. package/package.json +116 -36
  170. package/dist/client.d.ts +0 -2
  171. package/dist/client.js +0 -2
  172. package/dist/core.d.ts +0 -2
  173. package/dist/core.js +0 -2
  174. package/dist/files.d.ts +0 -2
  175. package/dist/files.js +0 -2
  176. package/dist/internal/client/deserialize.js +0 -11
  177. package/dist/internal/client/index.js +0 -44
  178. package/dist/internal/core/errors.js +0 -6
  179. package/dist/internal/core/index.js +0 -7
  180. package/dist/internal/core/match.js +0 -27
  181. package/dist/internal/core/router.js +0 -189
  182. package/dist/internal/core/serialize.js +0 -13
  183. package/dist/internal/core/types.js +0 -5
  184. package/dist/internal/core/url.d.ts +0 -2
  185. package/dist/internal/core/url.js +0 -6
  186. package/dist/internal/files/index.js +0 -77
  187. package/dist/internal/preact/context.d.ts +0 -18
  188. package/dist/internal/preact/context.js +0 -20
  189. package/dist/internal/preact/fetcher.js +0 -28
  190. package/dist/internal/preact/form.js +0 -41
  191. package/dist/internal/preact/hooks.js +0 -35
  192. package/dist/internal/preact/index.d.ts +0 -9
  193. package/dist/internal/preact/index.js +0 -7
  194. package/dist/internal/preact/link.d.ts +0 -12
  195. package/dist/internal/preact/link.js +0 -28
  196. package/dist/internal/preact/routes.js +0 -22
  197. package/dist/internal/react/context.d.ts +0 -18
  198. package/dist/internal/react/context.js +0 -20
  199. package/dist/internal/react/fetcher.js +0 -28
  200. package/dist/internal/react/form.js +0 -41
  201. package/dist/internal/react/hooks.js +0 -35
  202. package/dist/internal/react/index.js +0 -7
  203. package/dist/internal/react/link.js +0 -28
  204. package/dist/internal/react/routes.js +0 -22
  205. package/dist/internal/react/runtime.js +0 -2
  206. package/dist/internal/server/index.js +0 -69
  207. package/dist/internal/vite/index.js +0 -132
  208. package/dist/internal/vite/transport.js +0 -42
  209. package/dist/preact-client.d.ts +0 -2
  210. package/dist/preact-client.js +0 -2
  211. package/dist/preact-server.d.ts +0 -2
  212. package/dist/preact-server.js +0 -2
  213. package/dist/preact-vite.js +0 -9
  214. package/dist/preact.d.ts +0 -2
  215. package/dist/preact.js +0 -2
  216. package/dist/react-client.d.ts +0 -2
  217. package/dist/react-client.js +0 -2
  218. package/dist/react-server.d.ts +0 -2
  219. package/dist/react-server.js +0 -2
  220. package/dist/react-vite.js +0 -9
  221. package/dist/react.d.ts +0 -2
  222. package/dist/react.js +0 -2
  223. package/dist/server.d.ts +0 -2
  224. package/dist/server.js +0 -2
  225. package/dist/utils.d.ts +0 -5
  226. package/dist/utils.js +0 -5
  227. package/dist/vite.d.ts +0 -2
  228. package/dist/vite.js +0 -2
  229. package/src/client.ts +0 -2
  230. package/src/core.ts +0 -2
  231. package/src/files.ts +0 -2
  232. package/src/internal/core/url.ts +0 -6
  233. package/src/internal/preact/context.ts +0 -46
  234. package/src/internal/preact/form.ts +0 -79
  235. package/src/internal/preact/link.ts +0 -46
  236. package/src/internal/react/context.ts +0 -46
  237. package/src/internal/react/form.ts +0 -79
  238. package/src/internal/react/index.ts +0 -13
  239. package/src/preact-client.ts +0 -2
  240. package/src/preact-server.ts +0 -2
  241. package/src/preact.ts +0 -2
  242. package/src/react-client.ts +0 -2
  243. package/src/react-server.ts +0 -2
  244. package/src/react.ts +0 -2
  245. package/src/server.ts +0 -2
  246. package/src/utils.ts +0 -5
  247. package/src/vite.ts +0 -2
  248. /package/{src/internal → .generated}/client/deserialize.ts +0 -0
  249. /package/{src/internal → .generated}/core/match.ts +0 -0
  250. /package/{src/internal → .generated}/files/index.ts +0 -0
  251. /package/{src/internal → .generated}/preact/runtime.ts +0 -0
  252. /package/{src/internal → .generated}/react/runtime.ts +0 -0
  253. /package/{src/internal → .generated}/vite/index.ts +0 -0
  254. /package/{src/internal → .generated}/vite/transport.ts +0 -0
  255. /package/dist/{internal/client → client}/deserialize.d.ts +0 -0
  256. /package/dist/{internal/core → core}/errors.d.ts +0 -0
  257. /package/dist/{internal/core → core}/match.d.ts +0 -0
  258. /package/dist/{internal/files → files}/index.d.ts +0 -0
  259. /package/dist/{internal/preact → preact}/runtime.d.ts +0 -0
  260. /package/dist/{internal/react → react}/hooks.d.ts +0 -0
  261. /package/dist/{internal/react → react}/runtime.d.ts +0 -0
  262. /package/dist/{internal/server → server}/index.d.ts +0 -0
  263. /package/dist/{internal/vite → vite}/index.d.ts +0 -0
  264. /package/dist/{internal/vite → vite}/transport.d.ts +0 -0
@@ -0,0 +1,239 @@
1
+ import { Component, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
2
+ import { jsx } from "react/jsx-runtime";
3
+ //#region .generated/react/resources/components.tsx
4
+ var RouterContext = createContext(null);
5
+ var RouteContext = createContext(null);
6
+ var FormContext = createContext(null);
7
+ function RouterProvider({ children, initialMatches = EMPTY_MATCHES, router }) {
8
+ useEffect(() => {
9
+ for (const match of initialMatches) router.load(match.routeId, match.params ?? {});
10
+ return () => router.dispose();
11
+ }, [initialMatches, router]);
12
+ return /* @__PURE__ */ jsx(RouterContext.Provider, {
13
+ value: router,
14
+ children
15
+ });
16
+ }
17
+ function Route({ children, params = {}, persistent = false, routeId }) {
18
+ const router = useRouter();
19
+ const stableParams = useStableParams(params);
20
+ const state = useRouteState(routeId, stableParams, true);
21
+ const route = router.getRoute(routeId);
22
+ const retry = useCallback(() => void router.retry(routeId, stableParams), [
23
+ routeId,
24
+ router,
25
+ stableParams
26
+ ]);
27
+ const contextValue = useMemo(() => ({
28
+ params: stableParams,
29
+ routeId
30
+ }), [routeId, stableParams]);
31
+ useEffect(() => {
32
+ if (persistent) router.persist(routeId, stableParams);
33
+ }, [
34
+ persistent,
35
+ routeId,
36
+ router,
37
+ stableParams
38
+ ]);
39
+ let content = children;
40
+ if (children === void 0 && route?.default) {
41
+ const RouteComponent = route.default;
42
+ content = /* @__PURE__ */ jsx(RouteComponent, {});
43
+ }
44
+ if (state.unexpectedError && route?.ErrorBoundary) {
45
+ const ErrorBoundary = route.ErrorBoundary;
46
+ content = /* @__PURE__ */ jsx(ErrorBoundary, { retry });
47
+ }
48
+ if (!state.unexpectedError && !state.loaderInitialized && route?.HydrateFallback) {
49
+ const HydrateFallback = route.HydrateFallback;
50
+ content = /* @__PURE__ */ jsx(HydrateFallback, {});
51
+ }
52
+ return /* @__PURE__ */ jsx(RouteContext.Provider, {
53
+ value: contextValue,
54
+ children: /* @__PURE__ */ jsx(RenderErrorBoundary, {
55
+ onRetry: retry,
56
+ resetKey: `${routeId}:${JSON.stringify(stableParams)}`,
57
+ ...route?.ErrorBoundary ? { fallback: route.ErrorBoundary } : {},
58
+ children: content
59
+ })
60
+ });
61
+ }
62
+ function useLoaderData() {
63
+ const route = useCurrentRoute();
64
+ return useRouteLoaderData(route.routeId, route.params);
65
+ }
66
+ function useRouteLoaderData(routeId, params = {}) {
67
+ return useRouteState(routeId, useStableParams(params), true).loaderData;
68
+ }
69
+ function useActionData() {
70
+ const route = useCurrentRoute();
71
+ return useRouteState(route.routeId, route.params, false).actionData;
72
+ }
73
+ function useNavigation() {
74
+ const route = useCurrentRoute();
75
+ return { state: useRouteState(route.routeId, route.params, false).navigationState };
76
+ }
77
+ function useRevalidator() {
78
+ const route = useCurrentRoute();
79
+ const router = useRouter();
80
+ const state = useRouteState(route.routeId, route.params, false);
81
+ return useMemo(() => ({
82
+ revalidate: () => router.load(route.routeId, route.params, true),
83
+ state: state.navigationState === "loading" ? "loading" : "idle"
84
+ }), [
85
+ route,
86
+ router,
87
+ state.navigationState
88
+ ]);
89
+ }
90
+ function Form({ action, children, input, params }) {
91
+ const currentRoute = useContext(RouteContext);
92
+ const router = useRouter();
93
+ const routeId = action ?? currentRoute?.routeId ?? "";
94
+ const routeParams = useStableParams(params ?? currentRoute?.params ?? {});
95
+ const submit = useCallback((override) => router.submit(routeId, override === void 0 ? input : override, routeParams), [
96
+ input,
97
+ routeId,
98
+ routeParams,
99
+ router
100
+ ]);
101
+ if (!routeId) throw new Error("Form requires an action or a parent Route.");
102
+ return /* @__PURE__ */ jsx(FormContext.Provider, {
103
+ value: submit,
104
+ children
105
+ });
106
+ }
107
+ function useSubmit() {
108
+ const submit = useContext(FormContext);
109
+ if (!submit) throw new Error("useSubmit must be used inside Form.");
110
+ return submit;
111
+ }
112
+ function useFetcher(options = {}) {
113
+ const currentRoute = useContext(RouteContext);
114
+ const router = useRouter();
115
+ const routeId = options.routeId ?? currentRoute?.routeId ?? "";
116
+ const params = useStableParams(options.params ?? currentRoute?.params ?? {});
117
+ const [data, setData] = useState(null);
118
+ const [error, setError] = useState("");
119
+ const [state, setState] = useState("idle");
120
+ const controller = useRef(null);
121
+ useEffect(() => () => {
122
+ controller.current?.abort();
123
+ }, []);
124
+ const run = useCallback(async (mutating, input, target) => {
125
+ controller.current?.abort();
126
+ const nextController = new AbortController();
127
+ const { signal } = nextController;
128
+ const targetId = target.routeId ?? routeId;
129
+ const targetParams = target.params ?? params;
130
+ controller.current = nextController;
131
+ setError("");
132
+ setState(mutating ? "submitting" : "loading");
133
+ const result = await (mutating ? router.fetchAction(targetId, input, targetParams, signal) : router.fetchLoader(targetId, targetParams, signal));
134
+ if (signal.aborted) return result;
135
+ controller.current = null;
136
+ setData(result.data);
137
+ setError(result.error);
138
+ setState("idle");
139
+ if (result.unexpected && currentRoute) router.reportUnexpected(currentRoute.routeId, currentRoute.params);
140
+ return result;
141
+ }, [
142
+ currentRoute,
143
+ params,
144
+ routeId,
145
+ router
146
+ ]);
147
+ const submit = useCallback((input, target = {}) => run(true, input, target), [run]);
148
+ const load = useCallback((target = {}) => run(false, void 0, target), [run]);
149
+ const BoundForm = useCallback(({ children, input }) => /* @__PURE__ */ jsx(FormContext.Provider, {
150
+ value: (override) => submit(override === void 0 ? input : override),
151
+ children
152
+ }), [submit]);
153
+ if (!routeId) throw new Error("useFetcher requires a routeId or a parent Route.");
154
+ return [useMemo(() => ({
155
+ data,
156
+ error,
157
+ state
158
+ }), [
159
+ data,
160
+ error,
161
+ state
162
+ ]), useMemo(() => ({
163
+ Form: BoundForm,
164
+ load,
165
+ submit
166
+ }), [
167
+ BoundForm,
168
+ load,
169
+ submit
170
+ ])];
171
+ }
172
+ function useRouter() {
173
+ const router = useContext(RouterContext);
174
+ if (!router) throw new Error("RouterProvider is missing.");
175
+ return router;
176
+ }
177
+ function useCurrentRoute() {
178
+ const route = useContext(RouteContext);
179
+ if (!route) throw new Error("This hook requires a parent Route.");
180
+ return route;
181
+ }
182
+ function useRouteState(routeId, params, loadOnMount) {
183
+ const router = useRouter();
184
+ const subscribe = useCallback((listener) => router.subscribe(routeId, params, listener), [
185
+ params,
186
+ routeId,
187
+ router
188
+ ]);
189
+ const getSnapshot = useCallback(() => router.getSnapshot(routeId, params), [
190
+ params,
191
+ routeId,
192
+ router
193
+ ]);
194
+ const state = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
195
+ useEffect(() => {
196
+ if (loadOnMount) router.load(routeId, params);
197
+ }, [
198
+ loadOnMount,
199
+ params,
200
+ routeId,
201
+ router
202
+ ]);
203
+ return state;
204
+ }
205
+ function useStableParams(params) {
206
+ const key = JSON.stringify(Object.entries(params).sort(([left], [right]) => left.localeCompare(right)));
207
+ const ref = useRef({
208
+ key,
209
+ value: params
210
+ });
211
+ if (ref.current.key !== key) ref.current = {
212
+ key,
213
+ value: params
214
+ };
215
+ return ref.current.value;
216
+ }
217
+ var RenderErrorBoundary = class extends Component {
218
+ state = { error: null };
219
+ static getDerivedStateFromError(error) {
220
+ return { error };
221
+ }
222
+ componentDidUpdate(previousProps) {
223
+ if (this.state.error && previousProps.resetKey !== this.props.resetKey) this.setState({ error: null });
224
+ }
225
+ render() {
226
+ if (this.state.error && this.props.fallback) {
227
+ const ErrorBoundary = this.props.fallback;
228
+ return /* @__PURE__ */ jsx(ErrorBoundary, { retry: () => {
229
+ this.setState({ error: null });
230
+ this.props.onRetry();
231
+ } });
232
+ }
233
+ if (this.state.error) throw this.state.error;
234
+ return this.props.children;
235
+ }
236
+ };
237
+ var EMPTY_MATCHES = [];
238
+ //#endregion
239
+ export { Form, Route, RouterProvider, useActionData, useFetcher, useLoaderData, useNavigation, useRevalidator, useRouteLoaderData, useRouter, useSubmit };
@@ -3,7 +3,7 @@
3
3
  * @module
4
4
  */
5
5
  import type { RouteModule } from "../core/index.ts";
6
- import type { ComponentType, ReactNode } from "./runtime.ts";
6
+ import { type ComponentType, type ReactNode } from "./runtime.ts";
7
7
  /** Renderable route module with optional loading and error components. */
8
8
  export interface ViewModule extends RouteModule {
9
9
  default?: ComponentType;
@@ -0,0 +1,12 @@
1
+ import { createElement, useMemo } from "./runtime.js";
2
+ import { useRouter, useSnapshot } from "./context.js";
3
+ //#region .generated/react/routes.ts
4
+ /** Renders the first matching registered module. */
5
+ function Routes({ fallback }) {
6
+ const router = useRouter();
7
+ const state = useSnapshot();
8
+ const component = router.routes.find((route) => route.path === state.path)?.[state.error ? "ErrorBoundary" : state.state === "loading" && !state.loaded ? "HydrateFallback" : "default"];
9
+ return useMemo(() => component && createElement(component, { error: state.error }), [component, state.error]) ?? state.error?.message ?? fallback;
10
+ }
11
+ //#endregion
12
+ export { Routes };
@@ -0,0 +1 @@
1
+ export { createContext, createElement, useContext, useEffect, useMemo, useSyncExternalStore } from "react";
@@ -0,0 +1,4 @@
1
+ /** @jsxImportSource preact */
2
+ import type { AppRoute, RootModule } from "../preact/route-types.ts";
3
+ export declare function renderApplication(request: Request, routes: AppRoute[], rootModule: RootModule, entry: string): Promise<Response>;
4
+ export declare function dispatchRoute(request: Request, routes: AppRoute[], document?: boolean): Promise<Response>;
@@ -0,0 +1,119 @@
1
+ import { matchRoutes } from "../core/match.js";
2
+ import { createDataRouter } from "../core/router.js";
3
+ import { normalizeResult, resolveData } from "../core/responses.js";
4
+ import { RouterApplication, prepareRoutes } from "../preact/router.js";
5
+ import { loadRouter } from "./index.js";
6
+ import { jsx } from "preact/jsx-runtime";
7
+ import { renderToStringAsync } from "preact-render-to-string";
8
+ //#region .generated/server/application.tsx
9
+ /** @jsxImportSource preact */
10
+ async function renderApplication(request, routes, rootModule, entry) {
11
+ routes = routes.map((route) => ({
12
+ ...route,
13
+ path: route.routePath ?? route.path
14
+ }));
15
+ const match = matchRoutes(routes, new URL(request.url).pathname);
16
+ if (match && !match.route.default) return dispatchRoute(request, routes, true);
17
+ let actionResponse;
18
+ let actionData;
19
+ if (request.method !== "GET" && request.method !== "HEAD") {
20
+ actionResponse = await dispatchRoute(request, routes, true);
21
+ if (actionResponse.headers.has("Location") || !actionResponse.headers.get("Content-Type")?.includes("application/json")) return actionResponse;
22
+ actionData = await normalizeResult(await actionResponse.json());
23
+ }
24
+ const root = await resolveData(await rootModule.loader?.({
25
+ request,
26
+ params: {},
27
+ signal: request.signal
28
+ }));
29
+ let redirectResponse;
30
+ const prepared = prepareRoutes(routes.map((route) => ({
31
+ ...route,
32
+ ErrorBoundary: route.ErrorBoundary ?? rootModule.ErrorBoundary
33
+ }))).map((route) => {
34
+ if (!route.loader) return route;
35
+ const loader = route.loader;
36
+ return {
37
+ ...route,
38
+ loader: async (args) => {
39
+ const value = await loader(args);
40
+ if (value instanceof Response && value.headers.has("Location")) redirectResponse = value;
41
+ return value;
42
+ }
43
+ };
44
+ });
45
+ let router = await loadRouter(prepared, request);
46
+ if (redirectResponse) {
47
+ router.dispose();
48
+ return redirectResponse;
49
+ }
50
+ if (actionResponse) {
51
+ const hydrationData = {
52
+ ...router.getSnapshot(),
53
+ actionData: actionData instanceof Error ? void 0 : actionData,
54
+ ...actionData instanceof Error && { error: actionData }
55
+ };
56
+ router.dispose();
57
+ router = createDataRouter(router.routes, { hydrationData });
58
+ }
59
+ const snapshot = router.getSnapshot();
60
+ const html = await renderToStringAsync(/* @__PURE__ */ jsx(RouterApplication, {
61
+ router,
62
+ routes,
63
+ root,
64
+ rootModule,
65
+ entry,
66
+ document: true
67
+ }));
68
+ router.dispose();
69
+ const headers = new Headers(actionResponse?.headers);
70
+ headers.delete("Content-Length");
71
+ headers.delete("Content-Encoding");
72
+ headers.set("Content-Type", "text/html; charset=utf-8");
73
+ headers.set("Cache-Control", "no-store");
74
+ return new Response(`<!doctype html>${html}`, {
75
+ status: snapshot.error ? Number("status" in snapshot.error ? snapshot.error.status : 500) : actionResponse?.status ?? 200,
76
+ headers
77
+ });
78
+ }
79
+ async function dispatchRoute(request, routes, document = false) {
80
+ const match = matchRoutes(routes.map((route) => ({
81
+ ...route,
82
+ path: route.routePath ?? route.path
83
+ })), new URL(request.url).pathname);
84
+ if (!match) return Response.json({ error: "No route" }, { status: 404 });
85
+ const handler = request.method === "GET" || request.method === "HEAD" ? match.route.loader : match.route.action;
86
+ if (!handler) return Response.json({ error: "No handler" }, { status: 405 });
87
+ const value = await Promise.resolve().then(() => handler({
88
+ request,
89
+ params: match.params,
90
+ signal: request.signal
91
+ })).then((result) => result, (error) => error);
92
+ if (value instanceof Response) {
93
+ const location = value.headers.get("Location");
94
+ if (document) return value;
95
+ if (!location) {
96
+ if (value.ok) return value;
97
+ return Response.json({ __routeError: {
98
+ message: await value.text(),
99
+ status: value.status,
100
+ statusText: value.statusText
101
+ } });
102
+ }
103
+ const headers = new Headers(value.headers);
104
+ headers.delete("Location");
105
+ headers.set("X-Smolter-Redirect", location);
106
+ return new Response(null, { headers });
107
+ }
108
+ if (value instanceof Error) return Response.json({ __routeError: {
109
+ ...value,
110
+ message: value.message
111
+ } });
112
+ if (value && typeof value === "object" && "__routeData" in value) {
113
+ const result = value;
114
+ return Response.json(await resolveData(result.value), document ? result.init : { headers: result.init.headers });
115
+ }
116
+ return Response.json(await normalizeResult(value) ?? null);
117
+ }
118
+ //#endregion
119
+ export { dispatchRoute, renderApplication };
@@ -0,0 +1,5 @@
1
+ export interface RouteAssets {
2
+ path: string;
3
+ files: string[];
4
+ }
5
+ export declare function routePreloads(routes: RouteAssets[], pathname: string, base: string): string;
@@ -0,0 +1,7 @@
1
+ import { matchRoutes } from "../core/match.js";
2
+ //#region .generated/server/assets.ts
3
+ function routePreloads(routes, pathname, base) {
4
+ return ((matchRoutes(routes, pathname)?.route)?.files ?? []).map((file) => `<${base.replace(/\/$/, "")}/${file}>; rel=modulepreload`).join(", ");
5
+ }
6
+ //#endregion
7
+ export { routePreloads };
@@ -0,0 +1,19 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ /** Server module produced by an application's SSR entrypoint. */
3
+ export interface ServerBuild {
4
+ default: (request: Request, entry: string) => Response | Promise<Response>;
5
+ handleDataRequest: (request: Request) => Response | Promise<Response>;
6
+ }
7
+ export interface RequestHandlerOptions {
8
+ build: ServerBuild;
9
+ /** Browser entry URL resolved from Vite's production manifest. */
10
+ entry: string;
11
+ }
12
+ export interface ServerIncomingMessage extends IncomingMessage {
13
+ /** Express supplies this using the application's trust proxy policy. */
14
+ protocol?: string;
15
+ originalUrl?: string;
16
+ }
17
+ export type NextHandler = (error?: unknown) => void;
18
+ /** Node/Express middleware; mount after metrics, tracing and static assets. */
19
+ export declare function createRequestHandler({ build, entry, }: RequestHandlerOptions): (incoming: ServerIncomingMessage, outgoing: ServerResponse, next: NextHandler) => Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { send, webRequest } from "./http.js";
2
+ //#region .generated/server/handler.ts
3
+ /** Node/Express middleware; mount after metrics, tracing and static assets. */
4
+ function createRequestHandler({ build, entry }) {
5
+ return (incoming, outgoing, next) => {
6
+ const handle = async () => {
7
+ const url = new URL(incoming.originalUrl ?? incoming.url ?? "/", "http://local");
8
+ const dataRequest = url.pathname === "/_smolter";
9
+ const target = dataRequest ? url.searchParams.get("url") : void 0;
10
+ if (dataRequest && (!target?.startsWith("/") || new URL(target, "http://local").origin !== "http://local")) {
11
+ await send(new Response("Invalid route URL", { status: 400 }), outgoing);
12
+ return;
13
+ }
14
+ const request = await webRequest(incoming, outgoing, target ?? void 0);
15
+ const response = dataRequest ? await build.handleDataRequest(request) : await build.default(request, entry);
16
+ await send(response, outgoing);
17
+ };
18
+ return handle().catch(next);
19
+ };
20
+ }
21
+ //#endregion
22
+ export { createRequestHandler };
@@ -0,0 +1,4 @@
1
+ import type { ServerResponse } from "node:http";
2
+ import type { ServerIncomingMessage } from "./handler.ts";
3
+ export declare function webRequest(incoming: ServerIncomingMessage, outgoing: ServerResponse, target?: string): Promise<Request>;
4
+ export declare function send(response: Response, outgoing: ServerResponse): Promise<void>;
@@ -0,0 +1,27 @@
1
+ //#region .generated/server/http.ts
2
+ async function webRequest(incoming, outgoing, target) {
3
+ const origin = `${incoming.protocol ?? "http"}://${incoming.headers.host}`;
4
+ const headers = new Headers();
5
+ for (const [name, value] of Object.entries(incoming.headers)) if (value) headers.set(name, Array.isArray(value) ? value.join(", ") : value);
6
+ const controller = new AbortController();
7
+ outgoing.once("close", () => controller.abort());
8
+ const chunks = [];
9
+ for await (const chunk of incoming) chunks.push(chunk);
10
+ return new Request(new URL(target ?? incoming.originalUrl ?? incoming.url ?? "/", origin), {
11
+ method: incoming.method,
12
+ headers,
13
+ signal: controller.signal,
14
+ ...incoming.method !== "GET" && incoming.method !== "HEAD" && { body: new Uint8Array(Buffer.concat(chunks)) }
15
+ });
16
+ }
17
+ async function send(response, outgoing) {
18
+ outgoing.statusCode = response.status;
19
+ response.headers.forEach((value, name) => {
20
+ if (name !== "set-cookie") outgoing.setHeader(name, value);
21
+ });
22
+ const cookies = response.headers.getSetCookie();
23
+ if (cookies.length) outgoing.setHeader("set-cookie", cookies.map((cookie) => process.env.NODE_ENV === "production" ? cookie : cookie.replace(/; Secure/gi, "")));
24
+ outgoing.end(new Uint8Array(await response.arrayBuffer()));
25
+ }
26
+ //#endregion
27
+ export { send, webRequest };
@@ -0,0 +1,76 @@
1
+ import { safeWrapAsync } from "../core/errors.js";
2
+ import { matchRoutes } from "../core/match.js";
3
+ import { createDataRouter } from "../core/router.js";
4
+ import { serialize } from "../core/serialize.js";
5
+ //#region .generated/server/index.ts
6
+ /**
7
+ * Request-scoped server rendering and HTTP data dispatch.
8
+ * @module
9
+ */
10
+ /** Create one router per server request, await this, then render the provider. */
11
+ async function loadRouter(routes, input) {
12
+ const url = typeof input === "string" ? input : input.url;
13
+ const wrap = (handler) => {
14
+ if (!handler || typeof input === "string") return handler;
15
+ return (args) => {
16
+ const signal = AbortSignal.any([input.signal, args.signal]);
17
+ const request = new Request(args.request, { signal });
18
+ input.headers.forEach((value, name) => {
19
+ if (!request.headers.has(name)) request.headers.set(name, value);
20
+ });
21
+ return handler({
22
+ ...args,
23
+ request,
24
+ signal
25
+ });
26
+ };
27
+ };
28
+ const serverRoutes = routes.map((route) => ({
29
+ ...route,
30
+ clientLoader: void 0,
31
+ clientAction: void 0,
32
+ loader: wrap(route.loader),
33
+ action: wrap(route.action)
34
+ }));
35
+ const router = createDataRouter(serverRoutes, { url });
36
+ await router.navigate(url);
37
+ const snapshot = router.getSnapshot();
38
+ if (!snapshot.error && snapshot.data === void 0 && routes.some((route) => route.path === snapshot.path && route.clientLoader && !route.loader)) return createDataRouter(serverRoutes, {
39
+ url,
40
+ hydrationData: {
41
+ ...snapshot,
42
+ loaded: false,
43
+ state: "loading"
44
+ }
45
+ });
46
+ return router;
47
+ }
48
+ /** Dispatch an HTTP data request to a registered server loader or action. */
49
+ async function handleRequest(routes, request) {
50
+ const match = matchRoutes(routes, new URL(request.url).pathname);
51
+ const reading = request.method === "GET";
52
+ if (!match) return Response.json({ error: "No route" }, { status: 404 });
53
+ if (!reading && ![
54
+ "POST",
55
+ "PUT",
56
+ "PATCH",
57
+ "DELETE"
58
+ ].includes(request.method)) return Response.json({ error: "Method not allowed" }, { status: 405 });
59
+ const handler = reading ? match.route.loader : match.route.action;
60
+ if (!handler) return Response.json({ error: `No ${reading ? "loader" : "action"}` }, { status: 405 });
61
+ const result = await safeWrapAsync(() => handler({
62
+ request,
63
+ params: match.params,
64
+ signal: request.signal
65
+ }));
66
+ const error = result.error ?? (result.data instanceof Error && result.data);
67
+ if (error) return Response.json({ error: error.message }, { status: 500 });
68
+ if (result.data instanceof Response) {
69
+ const location = result.data.headers.get("Location");
70
+ if (location) return new Response(null, { headers: { "X-Smolter-Redirect": location } });
71
+ return result.data;
72
+ }
73
+ return Response.json(result.data ?? null, { headers: { "Cache-Control": "no-store" } });
74
+ }
75
+ //#endregion
76
+ export { handleRequest, loadRouter, serialize };
@@ -0,0 +1,3 @@
1
+ /** Optional application integration. @module */
2
+ export * from "./handler.ts";
3
+ export * from "./http.ts";
@@ -0,0 +1,3 @@
1
+ import { send, webRequest } from "./http.js";
2
+ import { createRequestHandler } from "./handler.js";
3
+ export { createRequestHandler, send, webRequest };
@@ -0,0 +1,3 @@
1
+ /** Preact server rendering and HTTP data dispatch. @module */
2
+ export { dispatchRoute, renderApplication, } from "./application.tsx";
3
+ export * from "./index.ts";
@@ -0,0 +1,4 @@
1
+ import { serialize } from "../core/serialize.js";
2
+ import { handleRequest, loadRouter } from "./index.js";
3
+ import { dispatchRoute, renderApplication } from "./application.js";
4
+ export { dispatchRoute, handleRequest, loadRouter, renderApplication, serialize };
@@ -0,0 +1,3 @@
1
+ import type { Plugin } from "vite";
2
+ /** Check emitted modules, after tree shaking, rather than import strings. */
3
+ export declare function browserBoundary(): Plugin;
@@ -0,0 +1,35 @@
1
+ import { builtinModules } from "node:module";
2
+ //#region .generated/vite/application/browser-boundary.ts
3
+ var builtins = new Set(builtinModules.flatMap((id) => [id, `node:${id}`]));
4
+ var forbiddenModules = [
5
+ /(?:vite-browser-external|__vite-browser-external)/,
6
+ /\/node_modules\/(?:react|react-dom|react-router|react-router-dom)\//,
7
+ /\/smolter\/dist\/(?:internal\/)?(?:server|vite|files)\//,
8
+ /\/smolter\/packages\/(?:server|vite)\/src\//,
9
+ /\/vite\/dist\/node\//,
10
+ /\/src\/[^?]*\.server\.[cm]?[jt]sx?(?:\?|$)/
11
+ ];
12
+ /** Check emitted modules, after tree shaking, rather than import strings. */
13
+ function browserBoundary() {
14
+ return {
15
+ name: "smolter-browser-boundary",
16
+ apply: "build",
17
+ applyToEnvironment: (environment) => environment.name === "client",
18
+ generateBundle(_options, bundle) {
19
+ for (const output of Object.values(bundle)) {
20
+ if (output.type !== "chunk") continue;
21
+ for (const id of [...output.imports, ...output.dynamicImports]) {
22
+ if (!builtins.has(id)) continue;
23
+ this.error(`Node builtin in browser chunk ${output.fileName}: ${id}`);
24
+ }
25
+ for (const id of Object.keys(output.modules)) {
26
+ const normalized = id.replaceAll("\\", "/");
27
+ if (!forbiddenModules.some((pattern) => pattern.test(normalized))) continue;
28
+ this.error(`Unexpected browser module in ${output.fileName}: ${id}`);
29
+ }
30
+ }
31
+ }
32
+ };
33
+ }
34
+ //#endregion
35
+ export { browserBoundary };
@@ -0,0 +1,2 @@
1
+ import { type Plugin } from "vite";
2
+ export declare function lazyFiles(directory: string): Plugin;