@kestack/kabuki 0.0.1 → 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,156 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ // oxlint-disable-next-line no-restricted-imports -- the Metadata is written to the document, and a link watches its hover and its place on screen: each only an effect may do.
3
+ import { createContext, use, useEffect, useRef } from 'react';
4
+ import { MANAGED, tagsOf } from '../route/index.js';
5
+ export const RouterContext = createContext(undefined);
6
+ /** What a link says its push leaves the scroll as: `false`, as it is. */
7
+ const RESET_SCROLL = 'data-reset-scroll';
8
+ /** How a link says its hash's element is scrolled into view, as JSON. */
9
+ const HASH_SCROLL = 'data-hash-scroll-into-view';
10
+ /** What a link clicked says besides where it goes: its hash, and what its `Router.Link` said of scroll. */
11
+ export const linkOptions = (link, url) => {
12
+ const hash = decodeURIComponent(url.hash.slice(1));
13
+ const said = link.getAttribute(HASH_SCROLL);
14
+ let hashScrollIntoView;
15
+ try {
16
+ hashScrollIntoView = said === null ? undefined : JSON.parse(said);
17
+ }
18
+ catch { }
19
+ return {
20
+ ...(link.getAttribute(RESET_SCROLL) === 'false'
21
+ ? { resetScroll: false }
22
+ : {}),
23
+ ...(hash ? { hash } : {}),
24
+ ...(hashScrollIntoView === undefined ? {} : { hashScrollIntoView }),
25
+ };
26
+ };
27
+ export const useDrawing = (what) => {
28
+ const drawing = use(RouterContext);
29
+ if (!drawing)
30
+ throw new Error(`[@kestack/kabuki/routing] ${what} is drawn outside a Router.app`);
31
+ return drawing;
32
+ };
33
+ /**
34
+ * Writes the Metadata to the document after every move, and when a page's
35
+ * data comes: the title, and every tag marked `data-kabuki`, which replace
36
+ * the ones marked before. Tags nobody marked are left alone, and an app
37
+ * routing in memory never touches the document.
38
+ */
39
+ export function MetadataWriter({ metadata }) {
40
+ const tags = tagsOf(metadata);
41
+ const written = JSON.stringify([metadata.title, tags]);
42
+ useEffect(() => {
43
+ if (metadata.title !== undefined)
44
+ document.title = metadata.title;
45
+ for (const old of document.head.querySelectorAll(`[${MANAGED}]`))
46
+ old.remove();
47
+ for (const { tag, attributes } of tags) {
48
+ const element = document.createElement(tag);
49
+ for (const [name, value] of Object.entries(attributes))
50
+ element.setAttribute(name, value);
51
+ document.head.append(element);
52
+ }
53
+ // oxlint-disable-next-line react-hooks/exhaustive-deps -- `written` is the Metadata, as one string.
54
+ }, [written]);
55
+ return null;
56
+ }
57
+ /**
58
+ * A link to a Route. It draws a plain `<a href>`, so a new tab, a copied
59
+ * link and a crawler all see the real URL; the router catches a plain click.
60
+ * It asks the Router to preload where it goes, as `preload` says: each ask
61
+ * is one Message, so the Timeline shows it.
62
+ */
63
+ export function Link({ to, preload, preloadDelay, resetScroll, hash, hashScrollIntoView, onMouseEnter, onMouseLeave, onFocus, onBlur, onTouchStart, ...rest }) {
64
+ const drawing = useDrawing('Router.Link');
65
+ const mode = preload ?? drawing.linkPreload.mode;
66
+ const wait = preloadDelay ?? drawing.linkPreload.delay;
67
+ const ref = useRef(null);
68
+ const timer = useRef(undefined);
69
+ const latest = useRef({ to, ask: drawing.preload });
70
+ latest.current = { to, ask: drawing.preload };
71
+ const href = `${drawing.href(to)}${hash ? `#${encodeURIComponent(hash)}` : ''}`;
72
+ useEffect(() => {
73
+ const link = ref.current;
74
+ if (mode !== 'viewport' ||
75
+ !link ||
76
+ typeof IntersectionObserver === 'undefined')
77
+ return;
78
+ const seen = new IntersectionObserver((entries) => {
79
+ if (!entries.some((entry) => entry.isIntersecting))
80
+ return;
81
+ seen.disconnect();
82
+ latest.current.ask(latest.current.to);
83
+ });
84
+ seen.observe(link);
85
+ return () => seen.disconnect();
86
+ }, [mode, href]);
87
+ useEffect(() => () => clearTimeout(timer.current), []);
88
+ const intend = () => {
89
+ if (mode !== 'intent')
90
+ return;
91
+ clearTimeout(timer.current);
92
+ timer.current = setTimeout(() => latest.current.ask(latest.current.to), wait);
93
+ };
94
+ const drop = () => clearTimeout(timer.current);
95
+ return (_jsx("a", { ...rest, ref: ref, href: href, ...(resetScroll === false ? { [RESET_SCROLL]: 'false' } : {}), ...(hashScrollIntoView === undefined
96
+ ? {}
97
+ : { [HASH_SCROLL]: JSON.stringify(hashScrollIntoView) }), onMouseEnter: (event) => {
98
+ intend();
99
+ onMouseEnter?.(event);
100
+ }, onMouseLeave: (event) => {
101
+ drop();
102
+ onMouseLeave?.(event);
103
+ }, onFocus: (event) => {
104
+ intend();
105
+ onFocus?.(event);
106
+ }, onBlur: (event) => {
107
+ drop();
108
+ onBlur?.(event);
109
+ }, onTouchStart: (event) => {
110
+ if (mode === 'intent')
111
+ latest.current.ask(latest.current.to);
112
+ onTouchStart?.(event);
113
+ } }));
114
+ }
115
+ /**
116
+ * The router, for a component drawn inside a `Router.app`: an address bar, a
117
+ * history list, a retry button. Name the app's Route,
118
+ * `useRouter<Route>()` with `type Route = typeof router.route.Type`, to read
119
+ * its Routes as that type: every Route the Router holds is one of them.
120
+ */
121
+ export const useRouter = () => {
122
+ const drawing = useDrawing('useRouter');
123
+ const { entries, at, go, move, pending, metadata } = drawing;
124
+ const route = entries[at].route;
125
+ const state = {
126
+ route,
127
+ href: drawing.href(route),
128
+ entries,
129
+ at,
130
+ move,
131
+ pending,
132
+ metadata,
133
+ title: metadata.title,
134
+ canGoBack: at > 0,
135
+ canGoForward: at < entries.length - 1,
136
+ back: () => go(-1),
137
+ forward: () => go(1),
138
+ goTo: (to) => go(to - at),
139
+ reload: drawing.reload,
140
+ preload: drawing.preload,
141
+ };
142
+ return state;
143
+ };
144
+ /**
145
+ * A button that loads every page on screen again: for a failed page's
146
+ * screen, so the page whose Loader broke retries. Any other button props
147
+ * pass through.
148
+ */
149
+ export function Retry({ onClick, type = 'button', ...rest }) {
150
+ const { reload } = useDrawing('Router.Retry');
151
+ return (_jsx("button", { ...rest, type: type, onClick: (event) => {
152
+ onClick?.(event);
153
+ if (!event.defaultPrevented)
154
+ reload();
155
+ } }));
156
+ }
@@ -0,0 +1,117 @@
1
+ import { Effect, Schema } from 'effect';
2
+ import type { AnyRoute } from '../capability/index.ts';
3
+ import type { DataInfo, Known, Status } from '../page/index.ts';
4
+ import type { Codec, Place } from '../route/index.ts';
5
+ /** One place's data, as the Router holds it. */
6
+ export interface Held {
7
+ /** The place's key: a place with another key is another place. */
8
+ readonly key: string;
9
+ readonly status: Status<unknown>;
10
+ /** How many reloads its data has seen. */
11
+ readonly reload: number;
12
+ /** What its Loader was handed, as one string: a new query loads it again. */
13
+ readonly for: string;
14
+ }
15
+ export type Holding = Readonly<Record<string, Held>>;
16
+ /** What the Router reads of the Routes for the data of its places. */
17
+ export interface DataRoutes<Route> {
18
+ readonly data: ReadonlyArray<readonly [string, Codec<unknown>]>;
19
+ readonly places: (of: Route) => ReadonlyArray<Place>;
20
+ }
21
+ /** What the Schemas of the data need of the Routes. */
22
+ type DataSchemas = Pick<DataRoutes<never>, 'data'>;
23
+ /** The data of the places with data, by dotted name, each with its own Schema. */
24
+ export declare const holdingSchema: (routes: DataSchemas) => Codec<Holding>;
25
+ /** The Router's Messages for the data of its places. */
26
+ export declare const placeMessages: (routes: DataSchemas) => {
27
+ PlaceLoaded: {
28
+ answer: Codec<{
29
+ readonly name: string;
30
+ readonly data: unknown;
31
+ }>;
32
+ key: Schema.String;
33
+ stale: Schema.Boolean;
34
+ info: Schema.Struct<{
35
+ readonly render: Schema.Literals<readonly ["static", "dynamic", "client"]>;
36
+ readonly staleTime: Schema.NullOr<Schema.Number>;
37
+ readonly from: Schema.Literals<readonly ["server", "browser", "cache"]>;
38
+ readonly arrivedAt: Schema.Number;
39
+ readonly freshUntil: Schema.NullOr<Schema.Number>;
40
+ readonly took: Schema.NullOr<Schema.Number>;
41
+ }>;
42
+ reload: Schema.Number;
43
+ for: Schema.String;
44
+ };
45
+ PlaceFailed: {
46
+ name: Schema.String;
47
+ key: Schema.String;
48
+ reason: Schema.String;
49
+ notFound: Schema.Boolean;
50
+ reload: Schema.Number;
51
+ for: Schema.String;
52
+ };
53
+ PlaceRedirected: {
54
+ route: Schema.Struct<{
55
+ readonly _tag: Schema.String;
56
+ }>;
57
+ };
58
+ RedirectedAway: {
59
+ href: Schema.String;
60
+ id: Schema.optionalKey<Schema.Number>;
61
+ };
62
+ };
63
+ export type PlaceMessage = {
64
+ readonly _tag: 'PlaceLoaded';
65
+ readonly answer: {
66
+ readonly name: string;
67
+ readonly data: unknown;
68
+ };
69
+ readonly key: string;
70
+ readonly stale: boolean;
71
+ readonly info: DataInfo;
72
+ readonly reload: number;
73
+ readonly for: string;
74
+ } | {
75
+ readonly _tag: 'PlaceFailed';
76
+ readonly name: string;
77
+ readonly key: string;
78
+ readonly reason: string;
79
+ readonly notFound: boolean;
80
+ readonly reload: number;
81
+ readonly for: string;
82
+ } | {
83
+ readonly _tag: 'PlaceRedirected';
84
+ readonly route: AnyRoute;
85
+ } | {
86
+ readonly _tag: 'RedirectedAway';
87
+ readonly href: string;
88
+ /** The held move it ends, if it came from one. */
89
+ readonly id?: number;
90
+ };
91
+ /** The data of the places with data on `route`: kept where the key is the same, else started from what is known. */
92
+ export declare const holdingAt: <Route>(routes: DataRoutes<Route>, route: Route, before: Holding, known: Readonly<Record<string, Known>>, reload: number) => Holding;
93
+ /** The runs of the `place` Worker: one per place on `route` whose data is due. */
94
+ export declare const dueRuns: <Route>(routes: DataRoutes<Route>, route: Route, holding: Holding, reload: number) => ReadonlyArray<string>;
95
+ /** Each place's status as its page sees it: data that is due is loading, behind the data shown if there is any. */
96
+ export declare const shownOf: <Route>(routes: DataRoutes<Route>, route: Route, holding: Holding, reload: number) => Readonly<Record<string, Status<unknown>>>;
97
+ /** The data of each place drawn with its data there, by dotted name: what the Metadata is worked out from. */
98
+ export declare const readyOf: (shown: Readonly<Record<string, Status<unknown>>>) => Readonly<Record<string, unknown>>;
99
+ /**
100
+ * Loads the data of one place, for the run `run` names, through `Loading`:
101
+ * with none, it stays loading. Sends what it came to.
102
+ */
103
+ export declare const loadPlace: <Route>(routes: DataRoutes<Route>, run: string, self: {
104
+ readonly send: (message: PlaceMessage) => Effect.Effect<void>;
105
+ readonly get: Effect.Effect<{
106
+ readonly route: Route;
107
+ readonly holding: Holding;
108
+ readonly reload: number;
109
+ }>;
110
+ readonly now: Effect.Effect<number>;
111
+ }) => Effect.Effect<void, never, never>;
112
+ /** The data of the places once an answer for one of them came: unchanged if that place is gone. */
113
+ export declare const settle: (holding: Holding, message: Exclude<PlaceMessage, {
114
+ readonly _tag: 'PlaceRedirected' | 'RedirectedAway';
115
+ }>) => Holding;
116
+ export {};
117
+ //# sourceMappingURL=data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../src/routing/router/data.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,247 @@
1
+ import { Effect, Option, Schema } from 'effect';
2
+ import { Loading } from '../capability/index.js';
3
+ import { DataInfoSchema, dataInfoOf, placeInfoOf, statusSchema, } from '../page/index.js';
4
+ /*
5
+ * The data of every place on screen, held by the Router: one entry per
6
+ * place with data on the Route drawn, by dotted name. Each page is handed
7
+ * its own as `status`.
8
+ *
9
+ * 1. Start as a move lands, each new place from what is known: the data
10
+ * the move loaded before it landed, the server's, or the Data
11
+ * cache's copy, which it shows, and loads again once stale.
12
+ * Knowing nothing, Loading. A place whose key, its name and path
13
+ * params, did not change keeps what it has.
14
+ * 2. Load with the `place` Worker, one run per place whose data is due:
15
+ * while it is loading, after a reload, on a stale copy, or when
16
+ * its query changes. The data shown stays meanwhile; a loading
17
+ * screen stays at least 300 ms.
18
+ * 3. Settle each answer into its place: Ready, or Failed, keeping data
19
+ * shown when loading it again fails for the same query; a
20
+ * Redirect replaces the entry, or leaves the app for a URL outside
21
+ * it. An answer for a place no longer on screen is dropped.
22
+ */
23
+ /** How long a page's loading screen stays at least, so it never flashes. */
24
+ const MINIMUM = 300;
25
+ const forOf = (place) => JSON.stringify(place.params);
26
+ /** The data of the places with data, by dotted name, each with its own Schema. */
27
+ export const holdingSchema = (routes) => Schema.Struct(Object.fromEntries(routes.data.map(([name, data]) => [
28
+ name,
29
+ Schema.optionalKey(Schema.Struct({
30
+ key: Schema.String,
31
+ status: statusSchema(data),
32
+ reload: Schema.Number,
33
+ for: Schema.String,
34
+ })),
35
+ ])));
36
+ /** One answer for one place, with its data by its own Schema. */
37
+ const answerSchema = (routes) => (routes.data.length === 0
38
+ ? Schema.Never
39
+ : Schema.Union(routes.data.map(([name, data]) => Schema.Struct({ name: Schema.Literal(name), data }))));
40
+ /** The Router's Messages for the data of its places. */
41
+ export const placeMessages = (routes) => ({
42
+ PlaceLoaded: {
43
+ answer: answerSchema(routes),
44
+ key: Schema.String,
45
+ stale: Schema.Boolean,
46
+ info: DataInfoSchema,
47
+ reload: Schema.Number,
48
+ for: Schema.String,
49
+ },
50
+ PlaceFailed: {
51
+ name: Schema.String,
52
+ key: Schema.String,
53
+ reason: Schema.String,
54
+ notFound: Schema.Boolean,
55
+ reload: Schema.Number,
56
+ for: Schema.String,
57
+ },
58
+ PlaceRedirected: { route: Schema.Struct({ _tag: Schema.String }) },
59
+ RedirectedAway: {
60
+ href: Schema.String,
61
+ id: Schema.optionalKey(Schema.Number),
62
+ },
63
+ });
64
+ // 1. Start
65
+ /** The data of the places with data on `route`: kept where the key is the same, else started from what is known. */
66
+ export const holdingAt = (routes, route, before, known, reload) => {
67
+ const out = {};
68
+ for (const place of routes.places(route)) {
69
+ if (!place.input)
70
+ continue;
71
+ const kept = before[place.name];
72
+ if (kept && kept.key === place.key) {
73
+ out[place.name] = kept;
74
+ continue;
75
+ }
76
+ const from = known[place.name];
77
+ out[place.name] = {
78
+ key: place.key,
79
+ reload,
80
+ for: forOf(place),
81
+ status: from?._tag === 'Ready'
82
+ ? {
83
+ _tag: 'Ready',
84
+ data: from.data,
85
+ refreshing: from.stale,
86
+ info: dataInfoOf(place, from),
87
+ }
88
+ : from?._tag === 'Failed'
89
+ ? {
90
+ _tag: 'Failed',
91
+ reason: from.reason,
92
+ notFound: from.notFound,
93
+ info: placeInfoOf(place),
94
+ }
95
+ : { _tag: 'Loading', info: placeInfoOf(place) },
96
+ };
97
+ }
98
+ return out;
99
+ };
100
+ // 2. Load
101
+ /** Whether a place's data is due to load, and why: the run it is loaded for, as one key. */
102
+ const dueOf = (place, held, reload) => {
103
+ const asked = reload > held.reload || forOf(place) !== held.for;
104
+ const { status } = held;
105
+ if (status._tag === 'Loading' ||
106
+ (status._tag === 'Ready' && status.refreshing) ||
107
+ asked)
108
+ // The status is in the key, so a copy that came while it loaded, stale,
109
+ // is loaded again fresh: the run for a key it kept is not.
110
+ return `${place.name}|${held.key}|${reload}|${forOf(place)}|${status._tag}`;
111
+ return undefined;
112
+ };
113
+ /** The runs of the `place` Worker: one per place on `route` whose data is due. */
114
+ export const dueRuns = (routes, route, holding, reload) => routes.places(route).flatMap((place) => {
115
+ const held = holding[place.name];
116
+ const due = held && dueOf(place, held, reload);
117
+ return due ? [due] : [];
118
+ });
119
+ /** Each place's status as its page sees it: data that is due is loading, behind the data shown if there is any. */
120
+ export const shownOf = (routes, route, holding, reload) => {
121
+ const out = {};
122
+ for (const place of routes.places(route)) {
123
+ const held = holding[place.name];
124
+ if (!held)
125
+ continue;
126
+ const { status } = held;
127
+ out[place.name] =
128
+ dueOf(place, held, reload) === undefined
129
+ ? status
130
+ : status._tag === 'Ready'
131
+ ? { ...status, refreshing: true }
132
+ : { _tag: 'Loading', info: placeInfoOf(place) };
133
+ }
134
+ return out;
135
+ };
136
+ /** The data of each place drawn with its data there, by dotted name: what the Metadata is worked out from. */
137
+ export const readyOf = (shown) => Object.fromEntries(Object.entries(shown).flatMap(([name, status]) => status._tag === 'Ready' ? [[name, status.data]] : []));
138
+ /**
139
+ * Loads the data of one place, for the run `run` names, through `Loading`:
140
+ * with none, it stays loading. Sends what it came to.
141
+ */
142
+ export const loadPlace = (routes, run, self) => Effect.gen(function* () {
143
+ const found = yield* Effect.serviceOption(Loading);
144
+ if (Option.isNone(found))
145
+ return;
146
+ const name = run.slice(0, run.indexOf('|'));
147
+ const { route, holding, reload } = yield* self.get;
148
+ const place = routes.places(route).find((each) => each.name === name);
149
+ const held = holding[name];
150
+ if (!place || !held || dueOf(place, held, reload) !== run)
151
+ return;
152
+ const shown = shownOf(routes, route, holding, reload)[name];
153
+ const began = yield* self.now;
154
+ const answers = yield* found.value.load([{ name, params: place.params }],
155
+ // Data being loaded for a place that has none yet may already be on
156
+ // its way, or kept. Behind a stale copy it may be on its way, as a
157
+ // preload's: joined, never answered by the copy. A reload's is asked
158
+ // again, joining nothing.
159
+ reload > held.reload
160
+ ? { fresh: true }
161
+ : held.status._tag === 'Loading'
162
+ ? {}
163
+ : { again: true });
164
+ // When the answer came, before the loading screen's least time.
165
+ const arrived = yield* self.now;
166
+ if (shown?._tag === 'Loading')
167
+ yield* Effect.sleep(Math.max(0, began + MINIMUM - (yield* self.now)));
168
+ const answer = answers[name] ?? {
169
+ _tag: 'Broken',
170
+ reason: `no answer for ${name}`,
171
+ };
172
+ const stamp = { key: held.key, reload, for: forOf(place) };
173
+ const failed = (reason, notFound) => self.send({ _tag: 'PlaceFailed', name, reason, notFound, ...stamp });
174
+ switch (answer._tag) {
175
+ case 'Loaded': {
176
+ const schema = routes.data.find(([each]) => each === name)?.[1];
177
+ return yield* schema && !Schema.is(Schema.toType(schema))(answer.data)
178
+ ? failed(`the data of ${name} does not match its Schema`, false)
179
+ : self.send({
180
+ _tag: 'PlaceLoaded',
181
+ answer: { name, data: answer.data },
182
+ stale: answer.stale === true,
183
+ info: dataInfoOf(place, {
184
+ from: answer.cached ? 'cache' : 'browser',
185
+ at: answer.at ?? arrived,
186
+ took: answer.cached ? null : (answer.took ?? arrived - began),
187
+ }),
188
+ ...stamp,
189
+ });
190
+ }
191
+ case 'NotFound':
192
+ return yield* failed('Not found', true);
193
+ case 'Broken':
194
+ return yield* failed(answer.reason, false);
195
+ case 'Redirect':
196
+ return yield* self.send(answer.href === undefined
197
+ ? { _tag: 'PlaceRedirected', route: answer.route }
198
+ : { _tag: 'RedirectedAway', href: answer.href });
199
+ }
200
+ });
201
+ // 3. Settle
202
+ /** The data of the places once an answer for one of them came: unchanged if that place is gone. */
203
+ export const settle = (holding, message) => {
204
+ const name = message._tag === 'PlaceLoaded' ? message.answer.name : message.name;
205
+ const held = holding[name];
206
+ if (!held || held.key !== message.key)
207
+ return holding;
208
+ if (message._tag === 'PlaceLoaded')
209
+ return {
210
+ ...holding,
211
+ [name]: {
212
+ key: held.key,
213
+ status: {
214
+ _tag: 'Ready',
215
+ data: message.answer.data,
216
+ refreshing: message.stale,
217
+ info: message.info,
218
+ },
219
+ reload: message.reload,
220
+ for: message.for,
221
+ },
222
+ };
223
+ // Loading again behind data shown, for the same query, keeps it.
224
+ return {
225
+ ...holding,
226
+ [name]: held.status._tag === 'Ready' && message.for === held.for
227
+ ? {
228
+ ...held,
229
+ status: { ...held.status, refreshing: false },
230
+ reload: message.reload,
231
+ }
232
+ : {
233
+ key: held.key,
234
+ status: {
235
+ _tag: 'Failed',
236
+ reason: message.reason,
237
+ notFound: message.notFound,
238
+ info: {
239
+ render: held.status.info.render,
240
+ staleTime: held.status.info.staleTime,
241
+ },
242
+ },
243
+ reload: message.reload,
244
+ for: message.for,
245
+ },
246
+ };
247
+ };
@@ -0,0 +1,52 @@
1
+ import type { ChildrenOf, Metadata, Redirects, RouteOf, RoutesApi, Tree } from '../route/index.ts';
2
+ import type { AnyPageDefinition, Rootless } from '../page/index.ts';
3
+ import type { ScrollRestorationOptions } from '../scroll/index.ts';
4
+ /** When a link preloads the Route it goes to: on hover or focus, once it scrolls into view, or never. */
5
+ export type Preload = 'intent' | 'viewport' | false;
6
+ /** How the Router behaves in the browser: where it is mounted, how long a move waits, links, scroll. */
7
+ export type RouterOptions<C, D> = {
8
+ /** Paths that are never a place, each with the Route it is replaced by, from the params its path names. */
9
+ readonly redirect?: Redirects<C, D>;
10
+ /** The not-found Route's Metadata, over the root page's. */
11
+ readonly notFound?: Metadata;
12
+ /** Where the app is mounted, like `/app`: every link and URL is under it. */
13
+ readonly base?: string;
14
+ /** How long a move waits for its data, in milliseconds, before it lands and its pages show they are loading: 150 unless said. */
15
+ readonly delay?: number;
16
+ /** When a `Router.Link` preloads, unless it says: `'intent'` by default. */
17
+ readonly preload?: Preload;
18
+ /** How long, in milliseconds, an `'intent'` link waits before it preloads: 50 by default. */
19
+ readonly preloadDelay?: number;
20
+ /** How long, in milliseconds, a copy in the Data cache stays fresh, for every dynamic or client page that says none: 0 unless said. */
21
+ readonly staleTime?: number;
22
+ /** How many copies the Data cache keeps, the least recently used dropped first: 50 unless said. */
23
+ readonly cacheSize?: number;
24
+ } & ScrollRestorationOptions<RouteOf<C>>;
25
+ /** What the Router reads of its definition besides the Routes. */
26
+ export interface Rooted<G extends AnyPageDefinition> {
27
+ /** The root page's definition: its build is the Router's one Child. */
28
+ readonly root: G;
29
+ readonly options: {
30
+ readonly base: string;
31
+ readonly delay: number;
32
+ readonly preload: {
33
+ readonly mode: Preload;
34
+ readonly delay: number;
35
+ };
36
+ readonly cacheSize: number;
37
+ readonly scroll: ScrollRestorationOptions<never>;
38
+ };
39
+ }
40
+ /** The router of an app whose root page is `G`. */
41
+ export type RouterDefinition<G extends AnyPageDefinition> = Tree<ChildrenOf<G>, RouteOf<ChildrenOf<G>>, {}> & RoutesApi<ChildrenOf<G>> & Rooted<G>;
42
+ export type AnyRouterDefinition = RoutesApi<any> & Rooted<AnyPageDefinition>;
43
+ /**
44
+ * The router of the app whose root page is `root`, or the page a function
45
+ * gives, read when the router is first used: a page whose definition and
46
+ * build share a module may then import the router to move the app, as long
47
+ * as nothing reads it while the modules load.
48
+ */
49
+ export declare const define: <G extends AnyPageDefinition, const D extends Readonly<Record<string, (routes: Tree<ChildrenOf<G>, RouteOf<ChildrenOf<G>>, {}>, params: any) => RouteOf<ChildrenOf<G>>>> = {}>(root: (G & Rootless<G>) | (() => G & Rootless<G>), options?: RouterOptions<ChildrenOf<G>, D> & {
50
+ readonly redirect?: D & Redirects<ChildrenOf<G>, D>;
51
+ }) => RouterDefinition<G>;
52
+ //# sourceMappingURL=define.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../../../src/routing/router/define.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ import { routesOf } from '../route/index.js';
2
+ /** The root page's Metadata, under every place's. */
3
+ const rootMetadata = (root) => typeof root.metadata === 'function'
4
+ ? root.metadata({}, undefined)
5
+ : root.metadata;
6
+ /**
7
+ * The router of the app whose root page is `root`, or the page a function
8
+ * gives, read when the router is first used: a page whose definition and
9
+ * build share a module may then import the router to move the app, as long
10
+ * as nothing reads it while the modules load.
11
+ */
12
+ export const define = (root, options = {}) => {
13
+ const make = (page) => {
14
+ if (page.input !== undefined)
15
+ throw new Error('[@kestack/kabuki/routing] the root page has no data: a Loader needs a place in the URL');
16
+ const metadata = rootMetadata(page);
17
+ const { redirect, notFound, base, delay, preload, preloadDelay, staleTime, cacheSize, ...scroll } = options;
18
+ return Object.assign(routesOf(page.children, {
19
+ ...(redirect ? { redirect } : {}),
20
+ ...(notFound ? { notFound } : {}),
21
+ ...(metadata ? { metadata } : {}),
22
+ ...(staleTime === undefined ? {} : { staleTime }),
23
+ }), {
24
+ root: page,
25
+ options: {
26
+ base: (base ?? '').replace(/\/$/, ''),
27
+ delay: delay ?? 150,
28
+ preload: { mode: preload ?? 'intent', delay: preloadDelay ?? 50 },
29
+ cacheSize: cacheSize ?? 50,
30
+ scroll: scroll,
31
+ },
32
+ });
33
+ };
34
+ if (typeof root !== 'function')
35
+ return make(root);
36
+ // Read the root when the router is first used, so its pages may import it.
37
+ let made;
38
+ const router = () => (made ??= make(root()));
39
+ return new Proxy({}, {
40
+ get: (_, key) => Reflect.get(router(), key),
41
+ has: (_, key) => Reflect.has(router(), key),
42
+ ownKeys: () => Reflect.ownKeys(router()),
43
+ getOwnPropertyDescriptor: (_, key) => {
44
+ const found = Reflect.getOwnPropertyDescriptor(router(), key);
45
+ return found && { ...found, configurable: true };
46
+ },
47
+ });
48
+ };
@@ -0,0 +1,10 @@
1
+ import type { MouseEvent, ReactNode } from 'react';
2
+ import type { Drawing } from './context.tsx';
3
+ export declare function Frame({ drawing, attached, root, onClick, children, }: {
4
+ readonly drawing: Drawing;
5
+ readonly attached: boolean;
6
+ readonly root: object;
7
+ readonly onClick: (event: MouseEvent) => void;
8
+ readonly children: ReactNode;
9
+ }): import("react").JSX.Element;
10
+ //# sourceMappingURL=frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../../../src/routing/router/frame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAQ7C,wBAAgB,KAAK,CAAC,EACpB,OAAO,EACP,QAAQ,EACR,IAAI,EACJ,OAAO,EACP,QAAQ,GACT,EAAE;IACD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;CAC9B,+BASA"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { MetadataWriter, RouterContext } from './context.js';
3
+ /*
4
+ * What the Router draws around the app: the context links and hooks read,
5
+ * a catch for plain clicks on links, and, when an Adapter holds the address,
6
+ * the Metadata, worked out from the data of the pages drawn.
7
+ */
8
+ export function Frame({ drawing, attached, root, onClick, children, }) {
9
+ return (_jsxs(RouterContext, { value: drawing, children: [attached && _jsx(MetadataWriter, { metadata: drawing.metadata }), _jsx("div", { ...root, style: { display: 'contents' }, onClick: onClick, children: children })] }));
10
+ }