@kasperrt/smolter 0.4.2 → 0.4.4

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 (151) hide show
  1. package/.generated/client/hash-history.ts +27 -0
  2. package/.generated/client/hydration.ts +98 -0
  3. package/.generated/client/root-data.ts +52 -0
  4. package/.generated/core/environment.ts +1 -0
  5. package/.generated/core/navigation.ts +1 -0
  6. package/.generated/core/prepare-routes.ts +50 -0
  7. package/.generated/core/presentation.ts +1 -1
  8. package/.generated/core/responses.ts +12 -0
  9. package/.generated/core/utils.ts +5 -2
  10. package/.generated/preact/await.tsx +1 -0
  11. package/.generated/preact/client.tsx +8 -27
  12. package/.generated/preact/deferred-hydration.ts +1 -98
  13. package/.generated/preact/document.tsx +10 -0
  14. package/.generated/preact/index.ts +11 -2
  15. package/.generated/preact/lazy-route.ts +7 -0
  16. package/.generated/preact/navigation.tsx +19 -5
  17. package/.generated/preact/root-data.ts +1 -45
  18. package/.generated/preact/route-hooks.tsx +12 -2
  19. package/.generated/preact/route-types.ts +14 -0
  20. package/.generated/preact/router.tsx +5 -48
  21. package/.generated/react/client.tsx +130 -0
  22. package/.generated/react/document.tsx +235 -0
  23. package/.generated/react/fetcher.ts +29 -7
  24. package/.generated/react/hooks.ts +20 -18
  25. package/.generated/react/index.ts +28 -2
  26. package/.generated/react/lazy-route.ts +116 -0
  27. package/.generated/react/link.ts +1 -38
  28. package/.generated/react/navigation-state.ts +13 -0
  29. package/.generated/react/navigation.ts +145 -0
  30. package/.generated/react/outlet.ts +48 -0
  31. package/.generated/react/root-context.ts +22 -0
  32. package/.generated/react/route-hooks.ts +159 -0
  33. package/.generated/react/route-types.ts +91 -0
  34. package/.generated/react/router.ts +50 -0
  35. package/.generated/react/runtime.ts +1 -0
  36. package/.generated/server/application.tsx +18 -250
  37. package/.generated/server/assets.ts +7 -0
  38. package/.generated/server/dispatch.ts +87 -0
  39. package/.generated/server/handler.ts +3 -0
  40. package/.generated/server/http.ts +2 -0
  41. package/.generated/server/index.ts +2 -0
  42. package/.generated/server/node.ts +5 -2
  43. package/.generated/server/preact.ts +5 -2
  44. package/.generated/server/react.ts +34 -0
  45. package/.generated/server/render-application.ts +203 -0
  46. package/.generated/vite/application/lazy-files.ts +2 -1
  47. package/.generated/vite/application/vite.ts +12 -4
  48. package/.generated/vite/react.ts +11 -0
  49. package/CHANGELOG.md +20 -0
  50. package/README.md +48 -48
  51. package/dist/client/hash-history.d.ts +2 -0
  52. package/dist/client/hash-history.js +26 -0
  53. package/dist/client/hydration.d.ts +10 -0
  54. package/dist/{preact/deferred-hydration.js → client/hydration.js} +1 -1
  55. package/dist/client/root-data.d.ts +14 -0
  56. package/dist/{preact → client}/root-data.js +1 -1
  57. package/dist/core/environment.d.ts +1 -0
  58. package/dist/core/navigation.d.ts +1 -0
  59. package/dist/core/prepare-routes.d.ts +7 -0
  60. package/dist/core/prepare-routes.js +28 -0
  61. package/dist/core/presentation.js +1 -1
  62. package/dist/core/responses.d.ts +12 -0
  63. package/dist/core/responses.js +5 -0
  64. package/dist/core/utils.d.ts +5 -2
  65. package/dist/core/utils.js +1 -2
  66. package/dist/preact/await.d.ts +1 -0
  67. package/dist/preact/await.js +1 -0
  68. package/dist/preact/client.d.ts +7 -1
  69. package/dist/preact/client.js +11 -27
  70. package/dist/preact/deferred-hydration.d.ts +1 -10
  71. package/dist/preact/document.d.ts +10 -0
  72. package/dist/preact/document.js +11 -2
  73. package/dist/preact/index.d.ts +6 -2
  74. package/dist/preact/index.js +1 -1
  75. package/dist/preact/lazy-route.d.ts +2 -0
  76. package/dist/preact/lazy-route.js +6 -0
  77. package/dist/preact/navigation-state.js +1 -1
  78. package/dist/preact/navigation.d.ts +6 -0
  79. package/dist/preact/navigation.js +9 -5
  80. package/dist/preact/root-data.d.ts +1 -8
  81. package/dist/preact/route-hooks.d.ts +10 -0
  82. package/dist/preact/route-hooks.js +13 -3
  83. package/dist/preact/route-types.d.ts +14 -0
  84. package/dist/preact/router.d.ts +5 -2
  85. package/dist/preact/router.js +4 -26
  86. package/dist/react/client.d.ts +20 -0
  87. package/dist/react/client.js +86 -0
  88. package/dist/react/document.d.ts +25 -0
  89. package/dist/react/document.js +169 -0
  90. package/dist/react/fetcher.d.ts +8 -4
  91. package/dist/react/fetcher.js +12 -1
  92. package/dist/react/form.js +1 -1
  93. package/dist/react/hooks.d.ts +7 -6
  94. package/dist/react/index.d.ts +13 -2
  95. package/dist/react/index.js +7 -2
  96. package/dist/react/lazy-route.d.ts +22 -0
  97. package/dist/react/lazy-route.js +57 -0
  98. package/dist/react/link.d.ts +1 -12
  99. package/dist/react/navigation-state.d.ts +2 -0
  100. package/dist/react/navigation-state.js +9 -0
  101. package/dist/react/navigation.d.ts +33 -0
  102. package/dist/react/navigation.js +64 -0
  103. package/dist/react/outlet.d.ts +16 -0
  104. package/dist/react/outlet.js +26 -0
  105. package/dist/react/root-context.d.ts +14 -0
  106. package/dist/react/root-context.js +10 -0
  107. package/dist/react/route-hooks.d.ts +34 -0
  108. package/dist/react/route-hooks.js +89 -0
  109. package/dist/react/route-types.d.ts +69 -0
  110. package/dist/react/router.d.ts +10 -0
  111. package/dist/react/router.js +22 -0
  112. package/dist/react/runtime.d.ts +1 -1
  113. package/dist/react/runtime.js +1 -1
  114. package/dist/server/application.d.ts +4 -5
  115. package/dist/server/application.js +9 -154
  116. package/dist/server/assets.d.ts +2 -0
  117. package/dist/server/assets.js +6 -0
  118. package/dist/server/dispatch.d.ts +5 -0
  119. package/dist/server/dispatch.js +50 -0
  120. package/dist/server/handler.d.ts +3 -0
  121. package/dist/server/http.d.ts +2 -0
  122. package/dist/server/http.js +2 -0
  123. package/dist/server/index.d.ts +1 -0
  124. package/dist/server/index.js +2 -1
  125. package/dist/server/node.d.ts +5 -1
  126. package/dist/server/preact.d.ts +5 -1
  127. package/dist/server/preact.js +2 -1
  128. package/dist/server/react.d.ts +10 -0
  129. package/dist/server/react.js +22 -0
  130. package/dist/server/render-application.d.ts +31 -0
  131. package/dist/server/render-application.js +118 -0
  132. package/dist/vite/application/lazy-files.d.ts +1 -1
  133. package/dist/vite/application/lazy-files.js +2 -2
  134. package/dist/vite/application/vite.d.ts +2 -1
  135. package/dist/vite/application/vite.js +12 -5
  136. package/dist/vite/react.d.ts +4 -0
  137. package/dist/vite/react.js +9 -1
  138. package/jsr.json +10 -6
  139. package/package.json +21 -12
  140. package/.generated/core/resource-types.ts +0 -44
  141. package/.generated/core/resources.ts +0 -444
  142. package/.generated/react/data.ts +0 -25
  143. package/.generated/react/resources/components.tsx +0 -404
  144. package/dist/core/resource-types.d.ts +0 -37
  145. package/dist/core/resources.d.ts +0 -19
  146. package/dist/core/resources.js +0 -345
  147. package/dist/react/data.d.ts +0 -5
  148. package/dist/react/data.js +0 -3
  149. package/dist/react/link.js +0 -27
  150. package/dist/react/resources/components.d.ts +0 -64
  151. package/dist/react/resources/components.js +0 -239
@@ -1,444 +0,0 @@
1
- import { safeWrapAsync } from "./errors.ts";
2
- import type {
3
- DataResult,
4
- Params,
5
- ResourceModule,
6
- RouteState,
7
- } from "./resource-types.ts";
8
-
9
- const IDLE_ROUTE_STATE: RouteState = {
10
- actionData: null,
11
- actionError: "",
12
- actionState: "idle",
13
- loaderData: null,
14
- loaderError: "",
15
- loaderInitialized: false,
16
- loaderState: "idle",
17
- navigationState: "idle",
18
- unexpectedError: false,
19
- };
20
-
21
- export class ResourceRouter {
22
- readonly #cleanupTimers = new Map<string, ReturnType<typeof setTimeout>>();
23
- readonly #controllers = new Map<string, AbortController>();
24
- readonly #inFlightLoads = new Map<string, Promise<DataResult<unknown>>>();
25
- readonly #listeners = new Map<string, Set<() => void>>();
26
- readonly #persistentRoutes = new Set<string>();
27
- readonly #routes: ReadonlyMap<string, ResourceModule>;
28
- readonly #states = new Map<string, RouteState>();
29
-
30
- constructor(routes: ReadonlyMap<string, ResourceModule>) {
31
- this.#routes = routes;
32
- }
33
-
34
- dispose() {
35
- for (const controller of this.#controllers.values()) controller.abort();
36
-
37
- this.#controllers.clear();
38
-
39
- for (const timer of this.#cleanupTimers.values()) clearTimeout(timer);
40
-
41
- this.#cleanupTimers.clear();
42
- this.#inFlightLoads.clear();
43
- this.#listeners.clear();
44
- this.#persistentRoutes.clear();
45
- }
46
-
47
- getRoute(routeId: string): ResourceModule | null {
48
- return this.#routes.get(routeId) ?? null;
49
- }
50
-
51
- getSnapshot(routeId: string, params: Params): RouteState {
52
- return (
53
- this.#states.get(createRouteKey(routeId, params)) ?? IDLE_ROUTE_STATE
54
- );
55
- }
56
-
57
- hydrateRoute<Data>(routeId: string, data: Data, params: Params = {}) {
58
- const routeKey = createRouteKey(routeId, params);
59
- const currentState = this.#states.get(routeKey) ?? IDLE_ROUTE_STATE;
60
- this.#setState(routeKey, {
61
- ...currentState,
62
- loaderData: data,
63
- loaderError: "",
64
- loaderInitialized: true,
65
- loaderState: "idle",
66
- navigationState:
67
- currentState.actionState === "submitting" ? "submitting" : "idle",
68
- unexpectedError: false,
69
- });
70
- }
71
-
72
- persist(routeId: string, params: Params = {}) {
73
- const routeKey = createRouteKey(routeId, params);
74
- this.#persistentRoutes.add(routeKey);
75
- const cleanupTimer = this.#cleanupTimers.get(routeKey);
76
-
77
- if (!cleanupTimer) return;
78
-
79
- clearTimeout(cleanupTimer);
80
- this.#cleanupTimers.delete(routeKey);
81
- }
82
-
83
- disposeRoute(routeId: string, params: Params = {}) {
84
- this.#disposeRouteKey(createRouteKey(routeId, params));
85
- }
86
-
87
- #disposeRouteKey(routeKey: string) {
88
- this.#persistentRoutes.delete(routeKey);
89
- const cleanupTimer = this.#cleanupTimers.get(routeKey);
90
-
91
- if (cleanupTimer) clearTimeout(cleanupTimer);
92
-
93
- this.#cleanupTimers.delete(routeKey);
94
- this.#controllers.get(routeKey)?.abort();
95
- this.#controllers.get(`${routeKey}:action`)?.abort();
96
- this.#controllers.delete(routeKey);
97
- this.#controllers.delete(`${routeKey}:action`);
98
- this.#inFlightLoads.delete(routeKey);
99
- this.#states.delete(routeKey);
100
- this.#notify(routeKey);
101
- }
102
-
103
- load<Data>(
104
- routeId: string,
105
- params: Params = {},
106
- force = false,
107
- ): Promise<DataResult<Data>> {
108
- const routeKey = createRouteKey(routeId, params);
109
- const route = this.#routes.get(routeId);
110
-
111
- if (!route?.loader) {
112
- return Promise.resolve<DataResult<Data>>({
113
- data: null,
114
- error: "Route loader not found.",
115
- });
116
- }
117
-
118
- const currentState = this.#states.get(routeKey);
119
-
120
- if (!force && currentState?.loaderInitialized) {
121
- return Promise.resolve({
122
- data: currentState?.loaderData as Data,
123
- error: currentState?.loaderError ?? "",
124
- });
125
- }
126
-
127
- const currentLoad = this.#inFlightLoads.get(routeKey);
128
-
129
- if (force && currentLoad) {
130
- this.#controllers.get(routeKey)?.abort();
131
- this.#inFlightLoads.delete(routeKey);
132
- }
133
-
134
- if (!force && currentLoad) return currentLoad as Promise<DataResult<Data>>;
135
-
136
- const controller = this.#replaceController(routeKey);
137
- this.#setState(routeKey, {
138
- ...(currentState ?? IDLE_ROUTE_STATE),
139
- loaderError: "",
140
- loaderState: "loading",
141
- navigationState:
142
- currentState?.actionState === "submitting" ? "submitting" : "loading",
143
- unexpectedError: false,
144
- });
145
- const load = this.#runLoader(route, routeKey, params, controller);
146
- this.#inFlightLoads.set(routeKey, load);
147
-
148
- return load as Promise<DataResult<Data>>;
149
- }
150
-
151
- subscribe(routeId: string, params: Params, listener: () => void): () => void {
152
- const routeKey = createRouteKey(routeId, params);
153
- const cleanupTimer = this.#cleanupTimers.get(routeKey);
154
-
155
- if (cleanupTimer) {
156
- clearTimeout(cleanupTimer);
157
- this.#cleanupTimers.delete(routeKey);
158
- }
159
-
160
- const listeners = this.#listeners.get(routeKey) ?? new Set<() => void>();
161
- listeners.add(listener);
162
- this.#listeners.set(routeKey, listeners);
163
-
164
- return () => {
165
- if (!listeners.delete(listener) || listeners.size > 0) return;
166
-
167
- this.#listeners.delete(routeKey);
168
-
169
- if (this.#persistentRoutes.has(routeKey)) return;
170
-
171
- const timer = setTimeout(
172
- () => this.#disposeRouteKey(routeKey),
173
- routeCleanupGraceMs,
174
- );
175
- this.#cleanupTimers.set(routeKey, timer);
176
- };
177
- }
178
-
179
- async submit<Data>(
180
- routeId: string,
181
- input: unknown,
182
- params: Params = {},
183
- ): Promise<DataResult<Data>> {
184
- const routeKey = createRouteKey(routeId, params);
185
- const route = this.#routes.get(routeId);
186
-
187
- if (!route?.action) {
188
- return { data: null, error: "Route action not found." };
189
- }
190
-
191
- const controllerKey = `${routeKey}:action`;
192
- const controller = this.#replaceController(controllerKey);
193
- const currentState = this.#states.get(routeKey) ?? IDLE_ROUTE_STATE;
194
- this.#setState(routeKey, {
195
- ...currentState,
196
- actionError: "",
197
- actionState: "submitting",
198
- navigationState: "submitting",
199
- unexpectedError: false,
200
- });
201
- const { error, data: result } = await safeWrapAsync(() =>
202
- route.action?.({ input, params, signal: controller.signal }),
203
- );
204
-
205
- if (this.#controllers.get(controllerKey) === controller) {
206
- this.#controllers.delete(controllerKey);
207
- }
208
-
209
- if (controller.signal.aborted) return { data: null, error: "" };
210
-
211
- const latestState = this.#states.get(routeKey) ?? IDLE_ROUTE_STATE;
212
-
213
- if (error || !result) {
214
- const publicError = "The request could not be completed.";
215
- this.#setState(routeKey, {
216
- ...latestState,
217
- actionData: null,
218
- actionError: publicError,
219
- actionState: "idle",
220
- navigationState:
221
- latestState.loaderState === "loading" ? "loading" : "idle",
222
- unexpectedError: true,
223
- });
224
-
225
- return { data: null, error: publicError, unexpected: true };
226
- }
227
-
228
- this.#setState(routeKey, {
229
- ...latestState,
230
- actionData: result.data,
231
- actionError: result.error,
232
- actionState: "idle",
233
- navigationState:
234
- latestState.loaderState === "loading" ? "loading" : "idle",
235
- unexpectedError: false,
236
- });
237
- const actionResult = {
238
- data: result.data as Data | null,
239
- error: result.error,
240
- };
241
- const { error: revalidationError, data: shouldRevalidate } =
242
- await safeWrapAsync(
243
- () =>
244
- route.shouldRevalidate?.({
245
- actionResult: result,
246
- defaultShouldRevalidate: Boolean(route.loader),
247
- params,
248
- }) ?? Boolean(route.loader),
249
- );
250
-
251
- if (revalidationError) {
252
- const publicError = "The request could not be completed.";
253
- this.#setState(routeKey, {
254
- ...(this.#states.get(routeKey) ?? IDLE_ROUTE_STATE),
255
- actionError: publicError,
256
- actionState: "idle",
257
- unexpectedError: true,
258
- });
259
-
260
- return { data: null, error: publicError, unexpected: true };
261
- }
262
-
263
- if (shouldRevalidate) await this.load(routeId, params, true);
264
-
265
- return actionResult;
266
- }
267
-
268
- async fetchLoader<Data>(
269
- routeId: string,
270
- params: Params,
271
- signal: AbortSignal,
272
- ): Promise<DataResult<Data>> {
273
- const route = this.#routes.get(routeId);
274
-
275
- if (!route?.loader) {
276
- return { data: null, error: "Route loader not found." };
277
- }
278
-
279
- const { error, data: result } = await safeWrapAsync(() =>
280
- route.loader?.({ params, signal }),
281
- );
282
-
283
- if (signal.aborted) return { data: null, error: "" };
284
-
285
- if (error || !result) {
286
- return {
287
- data: null,
288
- error: "The route could not be loaded.",
289
- unexpected: true,
290
- };
291
- }
292
-
293
- return result as DataResult<Data>;
294
- }
295
-
296
- async fetchAction<Data>(
297
- routeId: string,
298
- input: unknown,
299
- params: Params,
300
- signal: AbortSignal,
301
- ): Promise<DataResult<Data>> {
302
- const route = this.#routes.get(routeId);
303
-
304
- if (!route?.action) {
305
- return { data: null, error: "Route action not found." };
306
- }
307
-
308
- const { error, data: result } = await safeWrapAsync(() =>
309
- route.action?.({ input, params, signal }),
310
- );
311
-
312
- if (signal.aborted) return { data: null, error: "" };
313
-
314
- if (error || !result) {
315
- return {
316
- data: null,
317
- error: "The request could not be completed.",
318
- unexpected: true,
319
- };
320
- }
321
-
322
- const { error: revalidationError, data: shouldRevalidate } =
323
- await safeWrapAsync(
324
- () =>
325
- route.shouldRevalidate?.({
326
- actionResult: result,
327
- defaultShouldRevalidate: Boolean(route.loader),
328
- params,
329
- }) ?? Boolean(route.loader),
330
- );
331
-
332
- if (signal.aborted) return { data: null, error: "" };
333
-
334
- if (revalidationError) {
335
- return {
336
- data: null,
337
- error: "The request could not be completed.",
338
- unexpected: true,
339
- };
340
- }
341
-
342
- if (shouldRevalidate) await this.load(routeId, params, true);
343
-
344
- return result as DataResult<Data>;
345
- }
346
-
347
- retry(routeId: string, params: Params): Promise<DataResult<unknown>> {
348
- const routeKey = createRouteKey(routeId, params);
349
- this.#states.set(routeKey, IDLE_ROUTE_STATE);
350
- this.#notify(routeKey);
351
-
352
- return this.load(routeId, params, true);
353
- }
354
-
355
- reportUnexpected(routeId: string, params: Params) {
356
- const routeKey = createRouteKey(routeId, params);
357
- const currentState = this.#states.get(routeKey) ?? IDLE_ROUTE_STATE;
358
- this.#setState(routeKey, { ...currentState, unexpectedError: true });
359
- }
360
-
361
- async #runLoader(
362
- route: ResourceModule,
363
- routeKey: string,
364
- params: Params,
365
- controller: AbortController,
366
- ): Promise<DataResult<unknown>> {
367
- const { error, data: result } = await safeWrapAsync(
368
- () =>
369
- route.loader?.({ params, signal: controller.signal }) ??
370
- Promise.resolve({ data: null, error: "" }),
371
- );
372
-
373
- if (this.#controllers.get(routeKey) === controller) {
374
- this.#controllers.delete(routeKey);
375
- this.#inFlightLoads.delete(routeKey);
376
- }
377
-
378
- if (controller.signal.aborted) return { data: null, error: "" };
379
-
380
- if (error || !result) {
381
- const publicError = "The route could not be loaded.";
382
- this.#setState(routeKey, {
383
- actionData: null,
384
- actionError: "",
385
- actionState: "idle",
386
- loaderData: null,
387
- loaderError: publicError,
388
- loaderInitialized: true,
389
- loaderState: "idle",
390
- navigationState: "idle",
391
- unexpectedError: true,
392
- });
393
-
394
- return { data: null, error: publicError, unexpected: true };
395
- }
396
-
397
- const currentState = this.#states.get(routeKey) ?? IDLE_ROUTE_STATE;
398
- this.#setState(routeKey, {
399
- ...currentState,
400
- loaderData: result.data,
401
- loaderError: result.error,
402
- loaderInitialized: true,
403
- loaderState: "idle",
404
- navigationState:
405
- currentState.actionState === "submitting" ? "submitting" : "idle",
406
- unexpectedError: false,
407
- });
408
-
409
- return result;
410
- }
411
-
412
- #replaceController(key: string) {
413
- this.#controllers.get(key)?.abort();
414
- const controller = new AbortController();
415
- this.#controllers.set(key, controller);
416
-
417
- return controller;
418
- }
419
-
420
- #setState(routeKey: string, state: RouteState) {
421
- this.#states.set(routeKey, state);
422
- this.#notify(routeKey);
423
- }
424
-
425
- #notify(routeKey: string) {
426
- for (const listener of this.#listeners.get(routeKey) ?? []) listener();
427
- }
428
- }
429
-
430
- export function createResourceRouter(
431
- routes: ReadonlyMap<string, ResourceModule>,
432
- ): ResourceRouter {
433
- return new ResourceRouter(routes);
434
- }
435
-
436
- function createRouteKey(routeId: string, params: Params) {
437
- const normalizedParams = Object.entries(params).sort(([left], [right]) =>
438
- left.localeCompare(right),
439
- );
440
-
441
- return `${routeId}:${JSON.stringify(normalizedParams)}`;
442
- }
443
-
444
- const routeCleanupGraceMs = 100;
@@ -1,25 +0,0 @@
1
- /** Resource subscriptions independent of URL navigation. @module */
2
- export type {
3
- ActionFunctionArgs as ResourceActionArgs,
4
- DataResult as ResourceResult,
5
- LoaderFunctionArgs as ResourceLoaderArgs,
6
- ShouldRevalidateFunctionArgs as ResourceRevalidationArgs,
7
- } from "../core/resource-types.ts";
8
- export {
9
- createResourceRouter,
10
- ResourceRouter,
11
- } from "../core/resources.ts";
12
- export type { ResourceRouteModule } from "./resources/components.tsx";
13
- export {
14
- Form as ResourceForm,
15
- Route as Resource,
16
- RouterProvider as ResourceProvider,
17
- useActionData as useResourceActionData,
18
- useFetcher as useResourceFetcher,
19
- useLoaderData as useResourceData,
20
- useNavigation as useResourceNavigation,
21
- useRevalidator as useResourceRevalidator,
22
- useRouteLoaderData,
23
- useRouter as useResourceRouter,
24
- useSubmit as useResourceSubmit,
25
- } from "./resources/components.tsx";