@kestack/kabuki 0.0.0 → 0.0.2

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 (234) hide show
  1. package/README.md +156 -33
  2. package/dist/core/actor/actor.d.ts +24 -13
  3. package/dist/core/actor/actor.d.ts.map +1 -1
  4. package/dist/core/actor/actor.js +37 -26
  5. package/dist/core/actor/index.d.ts +2 -2
  6. package/dist/core/actor/index.d.ts.map +1 -1
  7. package/dist/core/actor/types.d.ts +91 -47
  8. package/dist/core/actor/types.d.ts.map +1 -1
  9. package/dist/core/history/history.d.ts +11 -3
  10. package/dist/core/history/history.d.ts.map +1 -1
  11. package/dist/core/history/history.js +106 -27
  12. package/dist/core/history/replay.d.ts +2 -0
  13. package/dist/core/history/replay.d.ts.map +1 -1
  14. package/dist/core/history/replay.js +1 -0
  15. package/dist/core/history/saved.d.ts +1 -1
  16. package/dist/core/history/saved.d.ts.map +1 -1
  17. package/dist/core/history/saved.js +13 -9
  18. package/dist/core/index.d.ts +1 -1
  19. package/dist/core/index.d.ts.map +1 -1
  20. package/dist/core/snapshot/invoke.d.ts +2 -3
  21. package/dist/core/snapshot/invoke.d.ts.map +1 -1
  22. package/dist/core/snapshot/invoke.js +42 -49
  23. package/dist/core/snapshot/restore.js +8 -7
  24. package/dist/core/snapshot/tree.d.ts +1 -1
  25. package/dist/core/snapshot/tree.d.ts.map +1 -1
  26. package/dist/core/snapshot/tree.js +1 -1
  27. package/dist/react/app.d.ts +30 -3
  28. package/dist/react/app.d.ts.map +1 -1
  29. package/dist/react/app.js +119 -40
  30. package/dist/react/drawn.d.ts +1 -1
  31. package/dist/react/drawn.d.ts.map +1 -1
  32. package/dist/react/drawn.js +2 -1
  33. package/dist/react/index.d.ts +3 -0
  34. package/dist/react/index.d.ts.map +1 -1
  35. package/dist/react/index.js +2 -0
  36. package/dist/react/lazy.d.ts +30 -0
  37. package/dist/react/lazy.d.ts.map +1 -0
  38. package/dist/react/lazy.js +56 -0
  39. package/dist/react/live.d.ts +7 -0
  40. package/dist/react/live.d.ts.map +1 -0
  41. package/dist/react/live.js +13 -0
  42. package/dist/react/view.d.ts +26 -6
  43. package/dist/react/view.d.ts.map +1 -1
  44. package/dist/react/view.js +17 -5
  45. package/dist/routing/adapter/adapter.d.ts +38 -0
  46. package/dist/routing/adapter/adapter.d.ts.map +1 -0
  47. package/dist/routing/adapter/adapter.js +10 -0
  48. package/dist/routing/adapter/browser/browser.d.ts +8 -0
  49. package/dist/routing/adapter/browser/browser.d.ts.map +1 -0
  50. package/dist/routing/adapter/browser/browser.js +33 -0
  51. package/dist/routing/adapter/browser/index.d.ts +2 -0
  52. package/dist/routing/adapter/browser/index.d.ts.map +1 -0
  53. package/dist/routing/adapter/browser/index.js +1 -0
  54. package/dist/routing/adapter/index.d.ts +3 -0
  55. package/dist/routing/adapter/index.d.ts.map +1 -0
  56. package/dist/routing/adapter/index.js +1 -0
  57. package/dist/routing/adapter/tanstack/index.d.ts +3 -0
  58. package/dist/routing/adapter/tanstack/index.d.ts.map +1 -0
  59. package/dist/routing/adapter/tanstack/index.js +1 -0
  60. package/dist/routing/adapter/tanstack/tanstack.d.ts +27 -0
  61. package/dist/routing/adapter/tanstack/tanstack.d.ts.map +1 -0
  62. package/dist/routing/adapter/tanstack/tanstack.js +38 -0
  63. package/dist/routing/capability/cache.d.ts +12 -0
  64. package/dist/routing/capability/cache.d.ts.map +1 -0
  65. package/dist/routing/capability/cache.js +170 -0
  66. package/dist/routing/capability/index.d.ts +6 -0
  67. package/dist/routing/capability/index.d.ts.map +1 -0
  68. package/dist/routing/capability/index.js +3 -0
  69. package/dist/routing/capability/loading.d.ts +82 -0
  70. package/dist/routing/capability/loading.d.ts.map +1 -0
  71. package/dist/routing/capability/loading.js +53 -0
  72. package/dist/routing/capability/navigation.d.ts +66 -0
  73. package/dist/routing/capability/navigation.d.ts.map +1 -0
  74. package/dist/routing/capability/navigation.js +65 -0
  75. package/dist/routing/index.d.ts +10 -0
  76. package/dist/routing/index.d.ts.map +1 -0
  77. package/dist/routing/index.js +4 -0
  78. package/dist/routing/page/index.d.ts +5 -0
  79. package/dist/routing/page/index.d.ts.map +1 -0
  80. package/dist/routing/page/index.js +2 -0
  81. package/dist/routing/page/page.d.ts +151 -0
  82. package/dist/routing/page/page.d.ts.map +1 -0
  83. package/dist/routing/page/page.js +121 -0
  84. package/dist/routing/page/status.d.ts +121 -0
  85. package/dist/routing/page/status.d.ts.map +1 -0
  86. package/dist/routing/page/status.js +69 -0
  87. package/dist/routing/route/index.d.ts +11 -0
  88. package/dist/routing/route/index.d.ts.map +1 -0
  89. package/dist/routing/route/index.js +6 -0
  90. package/dist/routing/route/loader.d.ts +98 -0
  91. package/dist/routing/route/loader.d.ts.map +1 -0
  92. package/dist/routing/route/loader.js +17 -0
  93. package/dist/routing/route/metadata.d.ts +44 -0
  94. package/dist/routing/route/metadata.d.ts.map +1 -0
  95. package/dist/routing/route/metadata.js +53 -0
  96. package/dist/routing/route/node.d.ts +162 -0
  97. package/dist/routing/route/node.d.ts.map +1 -0
  98. package/dist/routing/route/node.js +3 -0
  99. package/dist/routing/route/places.d.ts +15 -0
  100. package/dist/routing/route/places.d.ts.map +1 -0
  101. package/dist/routing/route/places.js +87 -0
  102. package/dist/routing/route/route.d.ts +53 -0
  103. package/dist/routing/route/route.d.ts.map +1 -0
  104. package/dist/routing/route/route.js +148 -0
  105. package/dist/routing/route/url.d.ts +13 -0
  106. package/dist/routing/route/url.d.ts.map +1 -0
  107. package/dist/routing/route/url.js +79 -0
  108. package/dist/routing/route/values.d.ts +12 -0
  109. package/dist/routing/route/values.d.ts.map +1 -0
  110. package/dist/routing/route/values.js +47 -0
  111. package/dist/routing/router/context.d.ts +123 -0
  112. package/dist/routing/router/context.d.ts.map +1 -0
  113. package/dist/routing/router/context.js +156 -0
  114. package/dist/routing/router/data.d.ts +117 -0
  115. package/dist/routing/router/data.d.ts.map +1 -0
  116. package/dist/routing/router/data.js +247 -0
  117. package/dist/routing/router/define.d.ts +52 -0
  118. package/dist/routing/router/define.d.ts.map +1 -0
  119. package/dist/routing/router/define.js +48 -0
  120. package/dist/routing/router/frame.d.ts +10 -0
  121. package/dist/routing/router/frame.d.ts.map +1 -0
  122. package/dist/routing/router/frame.js +10 -0
  123. package/dist/routing/router/index.d.ts +7 -0
  124. package/dist/routing/router/index.d.ts.map +1 -0
  125. package/dist/routing/router/index.js +3 -0
  126. package/dist/routing/router/router.d.ts +165 -0
  127. package/dist/routing/router/router.d.ts.map +1 -0
  128. package/dist/routing/router/router.js +801 -0
  129. package/dist/routing/routing.d.ts +72 -0
  130. package/dist/routing/routing.d.ts.map +1 -0
  131. package/dist/routing/routing.js +53 -0
  132. package/dist/routing/scroll/index.d.ts +4 -0
  133. package/dist/routing/scroll/index.d.ts.map +1 -0
  134. package/dist/routing/scroll/index.js +2 -0
  135. package/dist/routing/scroll/script.d.ts +6 -0
  136. package/dist/routing/scroll/script.d.ts.map +1 -0
  137. package/dist/routing/scroll/script.js +52 -0
  138. package/dist/routing/scroll/scroll.d.ts +75 -0
  139. package/dist/routing/scroll/scroll.d.ts.map +1 -0
  140. package/dist/routing/scroll/scroll.js +201 -0
  141. package/dist/routing/scroll/selector.d.ts +14 -0
  142. package/dist/routing/scroll/selector.d.ts.map +1 -0
  143. package/dist/routing/scroll/selector.js +40 -0
  144. package/dist/routing/scroll/session.d.ts +6 -0
  145. package/dist/routing/scroll/session.d.ts.map +1 -0
  146. package/dist/routing/scroll/session.js +23 -0
  147. package/dist/start/client/client.d.ts +11 -0
  148. package/dist/start/client/client.d.ts.map +1 -0
  149. package/dist/start/client/client.js +24 -0
  150. package/dist/start/client/index.d.ts +3 -0
  151. package/dist/start/client/index.d.ts.map +1 -0
  152. package/dist/start/client/index.js +2 -0
  153. package/dist/start/client/rpc.d.ts +14 -0
  154. package/dist/start/client/rpc.d.ts.map +1 -0
  155. package/dist/start/client/rpc.js +70 -0
  156. package/dist/start/common/app.d.ts +66 -0
  157. package/dist/start/common/app.d.ts.map +1 -0
  158. package/dist/start/common/app.js +23 -0
  159. package/dist/start/common/common.d.ts +26 -0
  160. package/dist/start/common/common.d.ts.map +1 -0
  161. package/dist/start/common/common.js +19 -0
  162. package/dist/start/common/document.d.ts +44 -0
  163. package/dist/start/common/document.d.ts.map +1 -0
  164. package/dist/start/common/document.js +58 -0
  165. package/dist/start/common/index.d.ts +9 -0
  166. package/dist/start/common/index.d.ts.map +1 -0
  167. package/dist/start/common/index.js +5 -0
  168. package/dist/start/common/page.d.ts +67 -0
  169. package/dist/start/common/page.d.ts.map +1 -0
  170. package/dist/start/common/page.js +29 -0
  171. package/dist/start/common/wire.d.ts +37 -0
  172. package/dist/start/common/wire.d.ts.map +1 -0
  173. package/dist/start/common/wire.js +73 -0
  174. package/dist/start/platform/cloudflare/cloudflare.d.ts +8 -0
  175. package/dist/start/platform/cloudflare/cloudflare.d.ts.map +1 -0
  176. package/dist/start/platform/cloudflare/cloudflare.js +31 -0
  177. package/dist/start/platform/cloudflare/index.d.ts +2 -0
  178. package/dist/start/platform/cloudflare/index.d.ts.map +1 -0
  179. package/dist/start/platform/cloudflare/index.js +1 -0
  180. package/dist/start/platform/index.d.ts +2 -0
  181. package/dist/start/platform/index.d.ts.map +1 -0
  182. package/dist/start/platform/index.js +1 -0
  183. package/dist/start/platform/platform.d.ts +31 -0
  184. package/dist/start/platform/platform.d.ts.map +1 -0
  185. package/dist/start/platform/platform.js +13 -0
  186. package/dist/start/server/cache.d.ts +4 -0
  187. package/dist/start/server/cache.d.ts.map +1 -0
  188. package/dist/start/server/cache.js +26 -0
  189. package/dist/start/server/draw.d.ts +10 -0
  190. package/dist/start/server/draw.d.ts.map +1 -0
  191. package/dist/start/server/draw.js +75 -0
  192. package/dist/start/server/index.d.ts +3 -0
  193. package/dist/start/server/index.d.ts.map +1 -0
  194. package/dist/start/server/index.js +2 -0
  195. package/dist/start/server/layers.d.ts +97 -0
  196. package/dist/start/server/layers.d.ts.map +1 -0
  197. package/dist/start/server/layers.js +37 -0
  198. package/dist/start/server/respond.d.ts +15 -0
  199. package/dist/start/server/respond.d.ts.map +1 -0
  200. package/dist/start/server/respond.js +35 -0
  201. package/dist/start/server/rpc.d.ts +11 -0
  202. package/dist/start/server/rpc.d.ts.map +1 -0
  203. package/dist/start/server/rpc.js +51 -0
  204. package/dist/start/server/server.d.ts +13 -0
  205. package/dist/start/server/server.d.ts.map +1 -0
  206. package/dist/start/server/server.js +138 -0
  207. package/dist/start/server/tiers.d.ts +14 -0
  208. package/dist/start/server/tiers.d.ts.map +1 -0
  209. package/dist/start/server/tiers.js +31 -0
  210. package/dist/start/vite/dev.d.ts +5 -0
  211. package/dist/start/vite/dev.d.ts.map +1 -0
  212. package/dist/start/vite/dev.js +56 -0
  213. package/dist/start/vite/entries.d.ts +27 -0
  214. package/dist/start/vite/entries.d.ts.map +1 -0
  215. package/dist/start/vite/entries.js +66 -0
  216. package/dist/start/vite/files.d.ts +22 -0
  217. package/dist/start/vite/files.d.ts.map +1 -0
  218. package/dist/start/vite/files.js +23 -0
  219. package/dist/start/vite/index.d.ts +2 -0
  220. package/dist/start/vite/index.d.ts.map +1 -0
  221. package/dist/start/vite/index.js +1 -0
  222. package/dist/start/vite/lazy.d.ts +4 -0
  223. package/dist/start/vite/lazy.d.ts.map +1 -0
  224. package/dist/start/vite/lazy.js +30 -0
  225. package/dist/start/vite/manifest.d.ts +18 -0
  226. package/dist/start/vite/manifest.d.ts.map +1 -0
  227. package/dist/start/vite/manifest.js +42 -0
  228. package/dist/start/vite/styles.d.ts +7 -0
  229. package/dist/start/vite/styles.d.ts.map +1 -0
  230. package/dist/start/vite/styles.js +58 -0
  231. package/dist/start/vite/vite.d.ts +11 -0
  232. package/dist/start/vite/vite.d.ts.map +1 -0
  233. package/dist/start/vite/vite.js +217 -0
  234. package/package.json +53 -3
@@ -0,0 +1,82 @@
1
+ import { Context, Effect, Layer, Stream } from 'effect';
2
+ import type { LoadError, Outcome, PlaceSchema } from '../route/index.ts';
3
+ /** A place to load: its dotted name and what its Loader is handed, every path param down to it and its own query. */
4
+ export interface Wanted {
5
+ readonly name: string;
6
+ readonly params: Readonly<Record<string, unknown>>;
7
+ }
8
+ /**
9
+ * What loading one place came to, as `Loading` answers it. `stale` is a
10
+ * copy the Data cache kept: shown at once, and loaded again behind it.
11
+ * `at` is when the Data cache had the data arrive, in milliseconds since
12
+ * the epoch; `cached` that it is a copy; `took` how long its load took.
13
+ */
14
+ export type Answer = Outcome & {
15
+ readonly stale?: boolean;
16
+ readonly at?: number;
17
+ /** From a copy the Data cache kept, not a load. */
18
+ readonly cached?: boolean;
19
+ /** How long, in milliseconds, from asking for it to its answer. */
20
+ readonly took?: number;
21
+ };
22
+ declare const Loading_base: Context.ServiceClass<Loading, "@kestack/kabuki/routing/Loading", {
23
+ /**
24
+ * Loads each place: from a copy the Data cache keeps, else from its
25
+ * Loader. `again`, behind a stale copy shown, never answers from a copy
26
+ * but joins a load on its way; `fresh`, a reload's, joins nothing.
27
+ */
28
+ readonly load: (places: ReadonlyArray<Wanted>, options?: {
29
+ readonly fresh?: boolean;
30
+ readonly again?: boolean;
31
+ }) => Effect.Effect<Readonly<Record<string, Answer>>>;
32
+ /**
33
+ * Loads the places at once, and answers each by its dotted name as it
34
+ * comes, so a quick place never waits on a slow one. The Data cache
35
+ * asks through this when there is one.
36
+ */
37
+ readonly each?: (places: ReadonlyArray<Wanted>) => Stream.Stream<readonly [string, Answer]>;
38
+ /** Starts loading the places the Data cache has no copy of, to keep: nothing waits for it. */
39
+ readonly preload?: (places: ReadonlyArray<Wanted>) => Effect.Effect<void>;
40
+ /** Drops every copy the Data cache keeps. */
41
+ readonly forget?: Effect.Effect<void>;
42
+ /**
43
+ * Keeps data a place already has, as if it had arrived `at`, now
44
+ * unless said: what a server drew the page with, so Back to it is at
45
+ * once. A copy already kept stays.
46
+ */
47
+ readonly keep?: (place: Wanted, data: unknown, at?: number) => Effect.Effect<void>;
48
+ }>;
49
+ /**
50
+ * The Capability the Router uses, if the Layer has it, to
51
+ * load the data of places: many at once, each answered on its own, never
52
+ * failing. In a browser under Start it is the Loader RPC's client behind
53
+ * the Data cache; in a test, a memory one. With none, a place with data
54
+ * stays loading.
55
+ */
56
+ export declare class Loading extends Loading_base {
57
+ }
58
+ /** A Loader's run as what it came to: its data, not found, a Redirect, or what broke. */
59
+ export declare const outcomeOf: <R>(run: Effect.Effect<unknown, LoadError, R>) => Effect.Effect<Outcome, never, R>;
60
+ /** What `memory` needs of a Routes tree: every place, with its Render Mode. */
61
+ export interface RoutesWithPlaces {
62
+ readonly every: ReadonlyArray<PlaceSchema>;
63
+ /** The Router's options: how many copies the Data cache keeps. */
64
+ readonly options?: {
65
+ readonly cacheSize?: number;
66
+ };
67
+ }
68
+ type AnyLoader = (params: never, request: Request) => Effect.Effect<unknown, LoadError, any>;
69
+ type ServicesOf<L> = {
70
+ [K in keyof L & string]-?: L[K] extends (...args: never) => Effect.Effect<any, any, infer R> ? R : never;
71
+ }[keyof L & string];
72
+ /**
73
+ * A `Loading` that runs the Loaders in place, as `router.loaders` types them:
74
+ * for tests, stories, a server, and an app with no server. A dynamic or
75
+ * client place's Loader is handed `request()`, a plain GET of
76
+ * `http://memory/` by default.
77
+ */
78
+ export declare const memory: <L extends Readonly<Record<string, AnyLoader>>>(routes: RoutesWithPlaces, loaders: L, options?: {
79
+ readonly request?: () => Request;
80
+ }) => Layer.Layer<Loading, never, ServicesOf<L>>;
81
+ export {};
82
+ //# sourceMappingURL=loading.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loading.d.ts","sourceRoot":"","sources":["../../../src/routing/capability/loading.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { Cause, Clock, Context, Effect, Layer, Stream } from 'effect';
2
+ /**
3
+ * The Capability the Router uses, if the Layer has it, to
4
+ * load the data of places: many at once, each answered on its own, never
5
+ * failing. In a browser under Start it is the Loader RPC's client behind
6
+ * the Data cache; in a test, a memory one. With none, a place with data
7
+ * stays loading.
8
+ */
9
+ export class Loading extends Context.Service()('@kestack/kabuki/routing/Loading') {
10
+ }
11
+ /** A Loader's Redirect as an outcome: to its Route or its URL, and permanent only if it said so. */
12
+ const redirectOf = (error) => ({
13
+ _tag: 'Redirect',
14
+ ...(error.href === undefined ? { route: error.route } : { href: error.href }),
15
+ ...(error.permanent ? { permanent: true } : {}),
16
+ });
17
+ /** A Loader's run as what it came to: its data, not found, a Redirect, or what broke. */
18
+ export const outcomeOf = (run) => run.pipe(Effect.map((data) => ({ _tag: 'Loaded', data })), Effect.catch((error) => Effect.succeed(error._tag === 'NotFound' ? { _tag: 'NotFound' } : redirectOf(error))), Effect.catchCause((cause) => {
19
+ const error = Cause.squash(cause);
20
+ return Effect.succeed({
21
+ _tag: 'Broken',
22
+ reason: error instanceof Error ? error.message : String(error),
23
+ });
24
+ }));
25
+ /**
26
+ * A `Loading` that runs the Loaders in place, as `router.loaders` types them:
27
+ * for tests, stories, a server, and an app with no server. A dynamic or
28
+ * client place's Loader is handed `request()`, a plain GET of
29
+ * `http://memory/` by default.
30
+ */
31
+ export const memory = (routes, loaders, options) => Layer.effect(Loading, Effect.gen(function* () {
32
+ const services = yield* Effect.context();
33
+ const modes = new Map(routes.every.map((place) => [place.name, place]));
34
+ const request = options?.request ?? (() => new Request('http://memory/'));
35
+ const each = (places) => Stream.fromIterable(places).pipe(Stream.mapEffect((place) => {
36
+ const loader = loaders[place.name];
37
+ const run = Effect.suspend(() => !loader
38
+ ? Effect.die(new Error(`[@kestack/kabuki/routing] no Loader for ${place.name}`))
39
+ : modes.get(place.name)?.render === 'static'
40
+ ? loader(place.params)
41
+ : loader(place.params, request()));
42
+ return Effect.gen(function* () {
43
+ const began = yield* Clock.currentTimeMillis;
44
+ const outcome = yield* outcomeOf(run);
45
+ const took = (yield* Clock.currentTimeMillis) - began;
46
+ return [place.name, { ...outcome, took }];
47
+ });
48
+ }, { concurrency: 'unbounded', unordered: true }), Stream.provideContext(services));
49
+ return {
50
+ each,
51
+ load: (places) => Stream.runCollect(each(places)).pipe(Effect.map((answers) => Object.fromEntries(answers))),
52
+ };
53
+ }));
@@ -0,0 +1,66 @@
1
+ import { Context, Schema } from 'effect';
2
+ import type { Effect } from 'effect';
3
+ /** A Route of any app: what the router moves to. */
4
+ export type AnyRoute = {
5
+ readonly _tag: string;
6
+ };
7
+ /** How an element is scrolled into view: as `Element.scrollIntoView` takes it. */
8
+ export interface HashScrollOptions {
9
+ readonly behavior?: 'auto' | 'instant' | 'smooth';
10
+ readonly block?: 'start' | 'center' | 'end' | 'nearest';
11
+ readonly inline?: 'start' | 'center' | 'end' | 'nearest';
12
+ }
13
+ /** What a push or replace may say besides where it goes, as TanStack Router's navigations say it. */
14
+ export interface MoveOptions {
15
+ /** `false`: the move leaves every scroll as it is, restoring and resetting nothing. */
16
+ readonly resetScroll?: boolean;
17
+ /** The element the move goes to on the page, by its id: `#setup`. */
18
+ readonly hash?: string;
19
+ /** How that element is scrolled into view; `false` leaves it. */
20
+ readonly hashScrollIntoView?: boolean | HashScrollOptions;
21
+ }
22
+ /** The Schemas of a move's options, for the Messages that carry them. */
23
+ export declare const moveOptions: {
24
+ resetScroll: Schema.optionalKey<Schema.Boolean>;
25
+ hash: Schema.optionalKey<Schema.String>;
26
+ hashScrollIntoView: Schema.optionalKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
27
+ readonly behavior: Schema.optionalKey<Schema.Literals<readonly ["auto", "instant", "smooth"]>>;
28
+ readonly block: Schema.optionalKey<Schema.Literals<readonly ["start", "center", "end", "nearest"]>>;
29
+ readonly inline: Schema.optionalKey<Schema.Literals<readonly ["start", "center", "end", "nearest"]>>;
30
+ }>]>>;
31
+ };
32
+ declare const Navigation_base: Context.ServiceClass<Navigation, "@kestack/kabuki/routing/Navigation", {
33
+ readonly push: (to: AnyRoute, options?: MoveOptions) => Effect.Effect<void>;
34
+ readonly replace: (to: AnyRoute, options?: MoveOptions) => Effect.Effect<void>;
35
+ readonly back: Effect.Effect<void>;
36
+ readonly forward: Effect.Effect<void>;
37
+ /** Back, if the entry before is `route`; else replace this entry with it. */
38
+ readonly backTo: (to: AnyRoute) => Effect.Effect<void>;
39
+ /** Load every place on screen again, its data shown meanwhile, and drop every other copy the Data cache keeps. */
40
+ readonly reload: Effect.Effect<void>;
41
+ /** Warm the Data cache, and the code of the lazy Views, for a Route the app may move to. */
42
+ readonly preload: (to: AnyRoute) => Effect.Effect<void>;
43
+ }>;
44
+ /**
45
+ * The Capability the router Provides to everything below it: Requests that
46
+ * move the app, each one Message to the router, with who asked. Every page
47
+ * Requires it, so a page's Update returns `Router.push(…)` and the like as
48
+ * Commands; any other Actor below the router lists it in its Requires.
49
+ */
50
+ export declare class Navigation extends Navigation_base {
51
+ }
52
+ /** Move to a Route, as a new entry: `{ resetScroll: false }` leaves the page scrolled as it is, `{ hash }` goes to an element on it. */
53
+ export declare const push: (to: AnyRoute, options?: MoveOptions) => import("../../core/actor/types.ts").Command<void, Navigation>;
54
+ /** Put a Route in place of this entry: Back skips what was here. */
55
+ export declare const replace: (to: AnyRoute, options?: MoveOptions) => import("../../core/actor/types.ts").Command<void, Navigation>;
56
+ /** The browser's Back, or the memory history's. */
57
+ export declare const back: () => import("../../core/actor/types.ts").Command<void, Navigation>;
58
+ export declare const forward: () => import("../../core/actor/types.ts").Command<void, Navigation>;
59
+ /** Close something with a Route of its own, like a dialog: Back if the app came from `route`, else replace this entry with it. */
60
+ export declare const backTo: (to: AnyRoute) => import("../../core/actor/types.ts").Command<void, Navigation>;
61
+ /** Load every place on screen again, as after a save: the data shown stays until the new data comes. */
62
+ export declare const reload: () => import("../../core/actor/types.ts").Command<void, Navigation>;
63
+ /** Warm the Data cache for a Route the app may move to next. */
64
+ export declare const preload: (to: AnyRoute) => import("../../core/actor/types.ts").Command<void, Navigation>;
65
+ export {};
66
+ //# sourceMappingURL=navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../../src/routing/capability/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,oDAAoD;AACpD,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;IAClD,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;IACxD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAC;CAC1D;AAED,qGAAqG;AACrG,MAAM,WAAW,WAAW;IAC1B,uFAAuF;IACvF,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAC;CAC3D;AAMD,yEAAyE;AACzE,eAAO,MAAM,WAAW;IACtB,WAAW;IACX,IAAI;IACJ,kBAAkB;;;;;CAYnB,CAAC;;mBAWiB,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;sBACzD,CAChB,EAAE,EAAE,QAAQ,EACZ,OAAO,CAAC,EAAE,WAAW,KAClB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;mBACT,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;sBAChB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IACrC,6EAA6E;qBAC5D,CAAC,EAAE,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IACtD,kHAAkH;qBACjG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IACpC,4FAA4F;sBAC1E,CAAC,EAAE,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;;AArB3D;;;;;GAKG;AACH,qBAAa,UAAW,SAAQ,eAiBS;CAAG;AAkC5C,wIAAwI;AACxI,eAAO,MAAM,IAAI,OAAQ,QAAQ,YAAW,WAAW,kEACnB,CAAC;AACrC,oEAAoE;AACpE,eAAO,MAAM,OAAO,OAAQ,QAAQ,YAAW,WAAW,kEACpB,CAAC;AACvC,mDAAmD;AACnD,eAAO,MAAM,IAAI,qEAAkB,CAAC;AACpC,eAAO,MAAM,OAAO,qEAAqB,CAAC;AAC1C,kIAAkI;AAClI,eAAO,MAAM,MAAM,OAAQ,QAAQ,kEAA6B,CAAC;AACjE,wGAAwG;AACxG,eAAO,MAAM,MAAM,qEAAoB,CAAC;AACxC,gEAAgE;AAChE,eAAO,MAAM,OAAO,OAAQ,QAAQ,kEAA8B,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { Context, Schema } from 'effect';
2
+ import { Command } from '../../core/index.js';
3
+ const position = Schema.optionalKey(Schema.Literals(['start', 'center', 'end', 'nearest']));
4
+ /** The Schemas of a move's options, for the Messages that carry them. */
5
+ export const moveOptions = {
6
+ resetScroll: Schema.optionalKey(Schema.Boolean),
7
+ hash: Schema.optionalKey(Schema.String),
8
+ hashScrollIntoView: Schema.optionalKey(Schema.Union([
9
+ Schema.Boolean,
10
+ Schema.Struct({
11
+ behavior: Schema.optionalKey(Schema.Literals(['auto', 'instant', 'smooth'])),
12
+ block: position,
13
+ inline: position,
14
+ }),
15
+ ])),
16
+ };
17
+ /**
18
+ * The Capability the router Provides to everything below it: Requests that
19
+ * move the app, each one Message to the router, with who asked. Every page
20
+ * Requires it, so a page's Update returns `Router.push(…)` and the like as
21
+ * Commands; any other Actor below the router lists it in its Requires.
22
+ */
23
+ export class Navigation extends Context.Service()('@kestack/kabuki/routing/Navigation') {
24
+ }
25
+ const Route = Schema.Struct({ _tag: Schema.String });
26
+ const args = Schema.Struct({ route: Route });
27
+ const moving = Schema.Struct({ route: Route, ...moveOptions });
28
+ const pushing = Command.make('push', {
29
+ args: moving,
30
+ run: ({ route, ...options }) => Navigation.use((navigation) => navigation.push(route, options)),
31
+ });
32
+ const replacing = Command.make('replace', {
33
+ args: moving,
34
+ run: ({ route, ...options }) => Navigation.use((navigation) => navigation.replace(route, options)),
35
+ });
36
+ const backing = Command.make('back', {
37
+ run: () => Navigation.use((navigation) => navigation.back),
38
+ });
39
+ const forwarding = Command.make('forward', {
40
+ run: () => Navigation.use((navigation) => navigation.forward),
41
+ });
42
+ const backingTo = Command.make('back to', {
43
+ args,
44
+ run: ({ route }) => Navigation.use((navigation) => navigation.backTo(route)),
45
+ });
46
+ const reloading = Command.make('reload', {
47
+ run: () => Navigation.use((navigation) => navigation.reload),
48
+ });
49
+ const preloading = Command.make('preload', {
50
+ args,
51
+ run: ({ route }) => Navigation.use((navigation) => navigation.preload(route)),
52
+ });
53
+ /** Move to a Route, as a new entry: `{ resetScroll: false }` leaves the page scrolled as it is, `{ hash }` goes to an element on it. */
54
+ export const push = (to, options = {}) => pushing({ route: to, ...options });
55
+ /** Put a Route in place of this entry: Back skips what was here. */
56
+ export const replace = (to, options = {}) => replacing({ route: to, ...options });
57
+ /** The browser's Back, or the memory history's. */
58
+ export const back = () => backing();
59
+ export const forward = () => forwarding();
60
+ /** Close something with a Route of its own, like a dialog: Back if the app came from `route`, else replace this entry with it. */
61
+ export const backTo = (to) => backingTo({ route: to });
62
+ /** Load every place on screen again, as after a save: the data shown stays until the new data comes. */
63
+ export const reload = () => reloading();
64
+ /** Warm the Data cache for a Route the app may move to next. */
65
+ export const preload = (to) => preloading({ route: to });
@@ -0,0 +1,10 @@
1
+ export { Page, Router } from './routing.ts';
2
+ export { Loading, Navigation } from './capability/index.ts';
3
+ export type { Answer, AnyRoute, MoveOptions, Wanted, } from './capability/index.ts';
4
+ export { useRouter } from './router/index.ts';
5
+ export { useElementScrollRestoration } from './scroll/index.ts';
6
+ export type { HashScroll, ScrollLocation, ScrollRestorationOptions, } from './scroll/index.ts';
7
+ export type { LinkProps, Preload, RouterDefinition, RouterState, } from './router/index.ts';
8
+ export type { DataInfo, From, PageBuild, PageDefinition, PageProps, PlaceInfo, Status, } from './page/index.ts';
9
+ export type { Metadata, RenderMode, RouteOf } from './route/index.ts';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EACV,MAAM,EACN,QAAQ,EACR,WAAW,EACX,MAAM,GACP,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EACV,UAAU,EACV,cAAc,EACd,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,QAAQ,EACR,IAAI,EACJ,SAAS,EACT,cAAc,EACd,SAAS,EACT,SAAS,EACT,MAAM,GACP,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { Page, Router } from './routing.js';
2
+ export { Loading, Navigation } from './capability/index.js';
3
+ export { useRouter } from './router/index.js';
4
+ export { useElementScrollRestoration } from './scroll/index.js';
@@ -0,0 +1,5 @@
1
+ export { Page } from './page.ts';
2
+ export type { AnyPageBuild, AnyPageDefinition, BuiltPage, PageBehaviorOf, PageBuild, PageDefinition, PageOptions, PageProps, Rootless, } from './page.ts';
3
+ export { DataInfoSchema, dataInfoOf, knownOf, placeInfoOf, statusSchema, } from './status.ts';
4
+ export type { DataInfo, From, Known, PlaceInfo, Status, Where, } from './status.ts';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routing/page/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,cAAc,EACd,SAAS,EACT,cAAc,EACd,WAAW,EACX,SAAS,EACT,QAAQ,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,cAAc,EACd,UAAU,EACV,OAAO,EACP,WAAW,EACX,YAAY,GACb,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,QAAQ,EACR,IAAI,EACJ,KAAK,EACL,SAAS,EACT,MAAM,EACN,KAAK,GACN,MAAM,aAAa,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { Page } from './page.js';
2
+ export { DataInfoSchema, dataInfoOf, knownOf, placeInfoOf, statusSchema, } from './status.js';
@@ -0,0 +1,151 @@
1
+ import { Schema } from 'effect';
2
+ import type { ActorType, AnyActor, Behavior, Defined, KidsByState, OpenOfBehavior, Requires, Tagged, Types } from '../../core/index.ts';
3
+ import { Navigation } from '../capability/index.ts';
4
+ import type { Codec, Fields, LevelOf, MetadataOf, Node, RenderMode, Simplify } from '../route/index.ts';
5
+ import type { Status, Where } from './status.ts';
6
+ type Single = {
7
+ readonly _tag: 'Single';
8
+ };
9
+ /** What `Page.define` takes: where the page is and what it loads, then the Actor's own Schemas. */
10
+ export interface PageOptions<P extends Fields, Q extends Fields, D, M extends RenderMode | undefined, C extends object, Model, State, Msg, R extends Requires> {
11
+ /** Its path under the page above: `/projects/:id`. None, or `/`, adds nothing. */
12
+ readonly path?: string;
13
+ /** The Schema of each path param it names. */
14
+ readonly params?: P;
15
+ /** The Schema of each query field it reads; a change in one loads its data again. */
16
+ readonly query?: Q;
17
+ /** The Schema of what its Loader gives: it needs one, in `loaders.server.ts`. */
18
+ readonly data?: Codec<D>;
19
+ /** How it is first drawn: static, dynamic or client; its parent's unless said. */
20
+ readonly render?: M;
21
+ /**
22
+ * How long, in milliseconds, a copy of its data stays fresh once it
23
+ * arrives: a Move or a Preload shows a fresh copy and loads nothing, and
24
+ * an older one is shown and loaded again behind it. Its parent's unless
25
+ * said, else the Router's. A static page has none: its data is fresh
26
+ * until the next deploy.
27
+ */
28
+ readonly staleTime?: M extends 'static' ? never : number;
29
+ /** What it says about itself, from its params, query and data once there. */
30
+ readonly metadata?: MetadataOf<P, Q, D>;
31
+ /** The definitions of the pages under it, each named by its key here. */
32
+ readonly pages?: C;
33
+ /** The Capabilities its own Workers, Commands and Provides use. `Navigation` is always among them. */
34
+ readonly requires?: R;
35
+ readonly model?: Codec<Model>;
36
+ readonly state?: Codec<State>;
37
+ readonly message?: Codec<Msg>;
38
+ }
39
+ /** What a page is handed as Props. */
40
+ export type PageProps<P extends Fields, Q extends Fields, D, C> = Simplify<{
41
+ /** Its own path params. */
42
+ readonly params: Schema.Struct.Type<P>;
43
+ /** Its own query. */
44
+ readonly query: Schema.Struct.Type<Q>;
45
+ /** Where it sits; `route.page` is the Route one level down, if the URL goes on. */
46
+ readonly route: Where<LevelOf<C>>;
47
+ } & ([D] extends [never] ? {} : {
48
+ /** Its data: loading, failed, or there. */
49
+ readonly status: Status<D>;
50
+ })>;
51
+ /** A page definition: a node of the Routes, and the definition of its Actor. */
52
+ export interface PageDefinition<P extends Fields = any, Q extends Fields = any, C extends object = any, D = any, M extends RenderMode | undefined = any, Model = any, State extends Tagged = any, Msg extends Tagged = any, R extends Requires = any> extends Node<P, Q, C, D, M>, Defined<'Page', void, PageProps<P, Q, D, C>, Model, State, Msg, readonly [...R, typeof Navigation]> {
53
+ /** Its types, as written: read by the types alone, never there. */
54
+ readonly '~page'?: {
55
+ readonly params: P;
56
+ readonly query: Q;
57
+ readonly children: C;
58
+ readonly data: [D];
59
+ readonly model: Model;
60
+ readonly state: State;
61
+ readonly message: Msg;
62
+ readonly requires: R;
63
+ };
64
+ }
65
+ export type AnyPageDefinition = PageDefinition;
66
+ /** A built page: its Actor, the definition it was built from, and the builds of the pages under it. */
67
+ export type PageBuild<Def = AnyPageDefinition, A = AnyActor, Pages = any> = A & {
68
+ /** The definition it was built from. */
69
+ readonly page: Def;
70
+ /** The build of each page under it, by name. */
71
+ readonly pages: Pages;
72
+ };
73
+ export type AnyPageBuild = PageBuild<AnyPageDefinition, AnyActor, any>;
74
+ /** The build of each page under a definition: one per page it names, built from that page's definition. */
75
+ type PagesOf<C> = {
76
+ readonly [K in keyof C]: PageBuild<C[K], AnyActor, any>;
77
+ };
78
+ /** What `Page.build` takes besides the Actor's behavior: the builds of the pages under it, if it has any. */
79
+ type WithPages<C> = [keyof C] extends [never] ? {
80
+ readonly pages?: never;
81
+ } : {
82
+ readonly pages: PagesOf<C>;
83
+ };
84
+ /** What the types of a page definition hold, as written. */
85
+ interface PageTypes {
86
+ readonly params: Fields;
87
+ readonly query: Fields;
88
+ readonly children: object;
89
+ readonly data: [unknown];
90
+ readonly model: unknown;
91
+ readonly state: Tagged;
92
+ readonly message: Tagged;
93
+ readonly requires: Requires;
94
+ }
95
+ type PropsOf<T extends PageTypes> = PageProps<T['params'], T['query'], T['data'][0], T['children']>;
96
+ type RequiresOf<T extends PageTypes> = readonly [
97
+ ...T['requires'],
98
+ typeof Navigation
99
+ ];
100
+ /** How the page `D` defines may be built, as written in `B`. */
101
+ export type PageBehaviorOf<D, B> = D extends {
102
+ readonly '~page'?: infer T extends PageTypes;
103
+ } ? Behavior<void, PropsOf<T>, T['model'], T['state'], T['message'], RequiresOf<T>, B> & WithPages<T['children']> : never;
104
+ /** Each page under it, as a Child of the page: there while the URL names it. */
105
+ type PageKids<Pages> = {
106
+ readonly [K in keyof Pages]: {
107
+ readonly actor: Pages[K];
108
+ readonly while: Function;
109
+ readonly key: Function;
110
+ };
111
+ };
112
+ /** What the pages under it still need from above. */
113
+ type PagesOpen<Pages> = {
114
+ [K in keyof Pages]: Types<Pages[K]>['open'];
115
+ }[keyof Pages];
116
+ type PagesIn<B> = B extends {
117
+ readonly pages: infer Pages;
118
+ } ? Pages : {};
119
+ /** The page `D` defines, built with `B`. */
120
+ export type BuiltPage<D, B> = D extends {
121
+ readonly '~page'?: infer T extends PageTypes;
122
+ } ? PageBuild<D, ActorType<'Page', void, PropsOf<T>, T['model'], T['state'], T['message'], {
123
+ readonly [S in T['state']['_tag']]: KidsByState<T['state'], B>[S] & PageKids<PagesIn<B>>;
124
+ }, OpenOfBehavior<RequiresOf<T>, B> | PagesOpen<PagesIn<B>>>, PagesIn<B>> : never;
125
+ /** The root page is no place: a page with a path, params or data is refused. */
126
+ export type Rootless<G> = G extends {
127
+ readonly '~page'?: {
128
+ readonly params: infer P;
129
+ readonly data: [infer D];
130
+ };
131
+ } ? [D] extends [never] ? [keyof P] extends [never] ? unknown : {
132
+ readonly 'the root page has no params': never;
133
+ } : {
134
+ readonly 'the root page has no data': never;
135
+ } : unknown;
136
+ /**
137
+ * A page definition: where it is in the URL, what it loads, the pages under
138
+ * it, and its own Schemas. Its Props are worked out from them.
139
+ */
140
+ declare const define: <const P extends Fields = {}, const Q extends Fields = {}, D = never, M extends RenderMode | undefined = undefined, const C extends {
141
+ readonly [name: string]: AnyPageDefinition;
142
+ } = {}, Model = {}, State extends Tagged = Single, Msg extends Tagged = never, const R extends Requires = readonly []>(options: PageOptions<P, Q, D, M, C, Model, State, Msg, R>) => PageDefinition<P, Q, C, D, M, Model, State, Msg, R>;
143
+ declare function build<D extends AnyPageDefinition>(definition: D & ({} extends PageBehaviorOf<D, {}> ? unknown : never)): BuiltPage<D, {}>;
144
+ declare function build<D extends AnyPageDefinition, const B extends PageBehaviorOf<D, B>>(definition: D, behavior: B): BuiltPage<D, B>;
145
+ /** Pages: `define` says what one is, `build` how it behaves. */
146
+ export declare const Page: {
147
+ define: typeof define;
148
+ build: typeof build;
149
+ };
150
+ export {};
151
+ //# sourceMappingURL=page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../../src/routing/page/page.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ import { Schema } from 'effect';
2
+ import { Actor } from '../../core/index.js';
3
+ import { Navigation } from '../capability/index.js';
4
+ import { levelSchema, paramsKey, pick } from '../route/index.js';
5
+ import { statusSchema, whereSchema } from './status.js';
6
+ /**
7
+ * A page definition: where it is in the URL, what it loads, the pages under
8
+ * it, and its own Schemas. Its Props are worked out from them.
9
+ */
10
+ const define = (options) => {
11
+ const params = (options.params ?? {});
12
+ const query = (options.query ?? {});
13
+ const pages = (options.pages ?? {});
14
+ const props = Schema.Struct({
15
+ params: Schema.Struct(params),
16
+ query: Schema.Struct(query),
17
+ route: whereSchema(Object.keys(pages).length > 0 ? levelSchema(pages, false) : undefined),
18
+ ...(options.data ? { status: statusSchema(options.data) } : {}),
19
+ });
20
+ return {
21
+ name: 'Page',
22
+ shape: {
23
+ props,
24
+ requires: [...(options.requires ?? []), Navigation],
25
+ ...(options.model ? { model: options.model } : {}),
26
+ ...(options.state ? { state: options.state } : {}),
27
+ ...(options.message ? { message: options.message } : {}),
28
+ },
29
+ path: options.path ?? '/',
30
+ params,
31
+ query,
32
+ input: options.data,
33
+ render: options.render,
34
+ staleTime: options.staleTime,
35
+ metadata: options.metadata,
36
+ children: pages,
37
+ };
38
+ };
39
+ /** What the places below `name` start from: each level hands down only the statuses under it. */
40
+ const belowOf = (data, name) => {
41
+ if (data === undefined)
42
+ return undefined;
43
+ const below = Object.entries(data).filter(([each]) => each.startsWith(`${name}.`));
44
+ return below.length === 0 ? undefined : Object.fromEntries(below);
45
+ };
46
+ /** Its Child for each page under it: there while the URL names it, made again when its params change. */
47
+ const pagesOf = (definitions, builds) => Object.fromEntries(Object.entries(definitions).map(([tag, definition]) => {
48
+ const build = builds[tag];
49
+ if (!build)
50
+ throw new Error(`[@kestack/kabuki/routing] the page "${tag}" has no build in \`pages\``);
51
+ if (build.page !== definition)
52
+ throw new Error(`[@kestack/kabuki/routing] the build given for the page "${tag}" is built from another page's definition`);
53
+ // Named after its key, for devtools and errors.
54
+ if (build.name === 'Page') {
55
+ build.name = tag;
56
+ build.definition.name = tag;
57
+ }
58
+ const valueOf = ({ props }) => props.route.page;
59
+ const nameOf = ({ props }) => props.route.name ? `${props.route.name}.${tag}` : tag;
60
+ return [
61
+ tag,
62
+ {
63
+ actor: build,
64
+ while: (data) => data.props.route.page?._tag === tag,
65
+ key: (data) => paramsKey(definition, valueOf(data)),
66
+ props: (data) => {
67
+ const value = valueOf(data);
68
+ const { route } = data.props;
69
+ const name = nameOf(data);
70
+ const below = belowOf(route.data, name);
71
+ return {
72
+ params: pick(definition.params, value),
73
+ query: pick(definition.query, value),
74
+ route: {
75
+ name,
76
+ ...(value['page'] === undefined ? {} : { page: value['page'] }),
77
+ params: { ...route.params, ...pick(definition.params, value) },
78
+ notFound: null,
79
+ ...(below === undefined ? {} : { data: below }),
80
+ },
81
+ ...(definition.input
82
+ ? {
83
+ // The Router hands every page with data its status;
84
+ // before it does, the page's own render, its Stale
85
+ // time not known here.
86
+ status: route.data?.[name] ?? {
87
+ _tag: 'Loading',
88
+ info: {
89
+ render: definition.render ?? 'static',
90
+ staleTime: null,
91
+ },
92
+ },
93
+ }
94
+ : {}),
95
+ };
96
+ },
97
+ },
98
+ ];
99
+ }));
100
+ /**
101
+ * A page built: how it behaves, as `Actor.build` takes it, and under
102
+ * `pages` the build of each page its definition names.
103
+ */
104
+ function build(definition, behavior = {}) {
105
+ const { pages: builds = {}, ...written } = behavior;
106
+ const pages = definition.children;
107
+ const own = [
108
+ ...Object.keys(written.children ?? {}),
109
+ ...Object.values(written.state ?? {}).flatMap((block) => Object.keys(block?.children ?? {})),
110
+ ];
111
+ for (const name of own)
112
+ if (name in pages)
113
+ throw new Error(`[@kestack/kabuki/routing] a page's Child "${name}" has the name of one of its pages`);
114
+ const actor = Actor.build(definition, {
115
+ ...written,
116
+ children: { ...written.children, ...pagesOf(pages, builds) },
117
+ });
118
+ return Object.assign(actor, { page: definition, pages: builds });
119
+ }
120
+ /** Pages: `define` says what one is, `build` how it behaves. */
121
+ export const Page = { define, build };