@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,56 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, lazy as reactLazy, use } from 'react';
3
+ /** Told of each lazy View a drawing draws; given by the App's `onLazyView`. */
4
+ export const LazyContext = createContext(undefined);
5
+ // 4. Find
6
+ const byActor = new WeakMap();
7
+ /** The lazy View made for `actor`, if one was: to load its code before it is drawn. */
8
+ export const lazyOf = (actor) => byActor.get(actor.definition);
9
+ const KEY = Symbol.for('@kestack/kabuki/lazy-views');
10
+ const byId = () => {
11
+ const global = globalThis;
12
+ return (global[KEY] ??= new Map());
13
+ };
14
+ /**
15
+ * A View of `actor` loaded when first drawn: `load` imports a module whose
16
+ * default export is the View, made with `View.make`. The Actor is named here
17
+ * so the View can be found by it, and preloaded, before its code is there.
18
+ */
19
+ export const lazy = (actor, load, options = {}) => {
20
+ // 1. Load
21
+ let view;
22
+ let loading;
23
+ const preload = () => (loading ??= load().then((module) => {
24
+ view = module.default;
25
+ }, (error) => {
26
+ loading = undefined;
27
+ throw error;
28
+ }));
29
+ const Loading = reactLazy(() => preload().then(() => ({ default: view })));
30
+ // 2. Draw
31
+ const Lazy = ((props) => {
32
+ // 3. Collect
33
+ use(LazyContext)?.(self);
34
+ const Loaded = view;
35
+ return Loaded ? _jsx(Loaded, { ...props }) : _jsx(Loading, { ...props });
36
+ });
37
+ const self = Object.defineProperties(Lazy, {
38
+ actor: { value: actor, enumerable: true },
39
+ id: { value: options.id, enumerable: true },
40
+ preload: { value: preload, enumerable: true },
41
+ loaded: { get: () => view !== undefined, enumerable: true },
42
+ displayName: { get: () => `Lazy(${actor.name}View)` },
43
+ });
44
+ if (options.id !== undefined)
45
+ byId().set(options.id, self);
46
+ byActor.set(actor.definition, self);
47
+ return self;
48
+ };
49
+ /** Preload every lazy View made with one of these ids; an id no lazy View was made with is skipped. */
50
+ export const preload = async (ids) => {
51
+ const views = byId();
52
+ await Promise.all([...ids].flatMap((id) => {
53
+ const view = views.get(id);
54
+ return view ? [view.preload()] : [];
55
+ }));
56
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Whether the app is drawn as it is now, not at a past Step, as Time Travel
3
+ * shows it: what an effect that touches the page around the app, like its
4
+ * scroll, reads first. Outside an app, always; on a server, never.
5
+ */
6
+ export declare const useLive: () => boolean;
7
+ //# sourceMappingURL=live.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../../src/react/live.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,eAAO,MAAM,OAAO,QAAO,OAO1B,CAAC"}
@@ -0,0 +1,13 @@
1
+ import { use, useSyncExternalStore } from 'react';
2
+ import { AppContext } from './view.js';
3
+ const never = () => false;
4
+ const noop = () => () => { };
5
+ /**
6
+ * Whether the app is drawn as it is now, not at a past Step, as Time Travel
7
+ * shows it: what an effect that touches the page around the app, like its
8
+ * scroll, reads first. Outside an app, always; on a server, never.
9
+ */
10
+ export const useLive = () => {
11
+ const drawn = use(AppContext);
12
+ return useSyncExternalStore(drawn?.subscribe ?? noop, drawn?.live ?? (() => true), never);
13
+ };
@@ -1,14 +1,22 @@
1
1
  import type { CSSProperties, ReactNode, Ref } from 'react';
2
- import type { AnyActor, Many, Only, Types } from '../core/index.ts';
2
+ import type { AnyActor, Only, Types } from '../core/index.ts';
3
+ import { lazy, preload } from './lazy.tsx';
3
4
  /** One Instance in the tree, to draw: its ID and, for a keyed Child, its key. Never changes. */
4
5
  export interface Handle<A> {
5
6
  readonly id: string;
6
7
  readonly key?: string;
7
8
  readonly actor?: A;
8
9
  }
9
- type HandleOf<S> = S extends Many<infer A> ? ReadonlyArray<Handle<A> & {
10
+ /** A Child as its View finds it: a list of `each`, one that may be absent `while` its test is false, else one always there. */
11
+ type HandleOf<C> = C extends {
12
+ readonly actor: infer A;
13
+ } ? C extends {
14
+ readonly each: Function;
15
+ } ? ReadonlyArray<Handle<A> & {
10
16
  readonly key: string;
11
- }> : Handle<S>;
17
+ }> : C extends {
18
+ readonly while: Function;
19
+ } ? Handle<A> | undefined : Handle<A> : never;
12
20
  type KidsIn<A, T> = T extends keyof Types<A>['children'] ? Types<A>['children'][T] : {};
13
21
  /**
14
22
  * What a parent may hand the root element of a View: the `ref` and `style`
@@ -48,6 +56,8 @@ export interface DrawnApp {
48
56
  readonly instance: (id: string) => Drawable | undefined;
49
57
  /** The Instance as first drawn, before any Runtime: what a server draws and the client hydrates against. */
50
58
  readonly served: (id: string) => Drawable | undefined;
59
+ /** Whether it draws the app as it is now, not a past Step: what may touch the page around it. */
60
+ readonly live: () => boolean;
51
61
  }
52
62
  export declare const AppContext: import("react").Context<DrawnApp | undefined>;
53
63
  /**
@@ -55,16 +65,20 @@ export declare const AppContext: import("react").Context<DrawnApp | undefined>;
55
65
  * `ref` and `style` to animate it by. Data from the parent is its Props, which
56
66
  * the parent Actor hands it, so Update and Workers see it too.
57
67
  */
58
- export type ViewOf<A> = (props: {
68
+ export type ViewOf<A> = ((props: {
59
69
  readonly instance: Handle<A>;
60
70
  readonly ref?: Ref<Element> | undefined;
61
71
  readonly style?: CSSProperties | undefined;
62
- }) => ReactNode;
72
+ }) => ReactNode) & {
73
+ /** The Actor it draws. */
74
+ readonly actor: A;
75
+ };
63
76
  /**
64
77
  * How one Actor is drawn: one function of its Instance. The result re-renders
65
78
  * only when its own Instance's Model, State, Props or set of Children changes,
66
79
  * or the parent hands it a new `ref` or `style`, never because a descendant
67
- * did.
80
+ * did. A new Instance in the same place, as a Child with a new key, is drawn
81
+ * afresh: no React state carries over from the one before.
68
82
  *
69
83
  * Once the Instance stops, the View keeps drawing its last data, so an exit
70
84
  * animation still has something to show.
@@ -73,8 +87,14 @@ export type ViewOf<A> = (props: {
73
87
  * the View a new `style` object on every render, with the same values.
74
88
  */
75
89
  declare const make: <A extends AnyActor>(actor: A, draw: (self: ViewProps<A>) => ReactNode) => ViewOf<A>;
90
+ /**
91
+ * `make` draws an Actor; `lazy` loads a View made with `make` when it is
92
+ * first drawn; `preload` loads the lazy Views made with these ids.
93
+ */
76
94
  export declare const View: {
77
95
  make: typeof make;
96
+ lazy: typeof lazy;
97
+ preload: typeof preload;
78
98
  };
79
99
  export {};
80
100
  //# sourceMappingURL=view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/react/view.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../../src/react/view.tsx"],"names":[],"mappings":"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"}
@@ -1,10 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  import { createContext, memo, use, useRef, useSyncExternalStore } from 'react';
3
+ import { lazy, preload } from './lazy.js';
2
4
  export const AppContext = createContext(undefined);
3
5
  /**
4
6
  * How one Actor is drawn: one function of its Instance. The result re-renders
5
7
  * only when its own Instance's Model, State, Props or set of Children changes,
6
8
  * or the parent hands it a new `ref` or `style`, never because a descendant
7
- * did.
9
+ * did. A new Instance in the same place, as a Child with a new key, is drawn
10
+ * afresh: no React state carries over from the one before.
8
11
  *
9
12
  * Once the Instance stops, the View keeps drawing its last data, so an exit
10
13
  * animation still has something to show.
@@ -13,7 +16,7 @@ export const AppContext = createContext(undefined);
13
16
  * the View a new `style` object on every render, with the same values.
14
17
  */
15
18
  const make = (actor, draw) => {
16
- const View = memo(({ instance, ref, style, }) => {
19
+ const Drawn = ({ instance, ref, style }) => {
17
20
  const drawn = use(AppContext);
18
21
  if (!drawn)
19
22
  throw new Error(`[@kestack/kabuki] ${actor.name}View is drawn outside its app`);
@@ -31,8 +34,13 @@ const make = (actor, draw) => {
31
34
  ...self,
32
35
  root: { ref: ref, style },
33
36
  });
34
- }, same);
35
- View.displayName = `${actor.name}View`;
37
+ };
38
+ const View = memo((given) => (_jsx(Drawn, { ...given }, given.instance.id)), same);
39
+ // Read when shown, so a name given after the View is made still counts.
40
+ Object.defineProperties(View, {
41
+ actor: { value: actor, enumerable: true },
42
+ displayName: { get: () => `${actor.name}View`, configurable: true },
43
+ });
36
44
  return View;
37
45
  };
38
46
  /** What the parent hands is the same when each is the same value, and `style` when each of its values is. */
@@ -44,4 +52,8 @@ const same = (a, b) => {
44
52
  (key === 'style' && sameValues(a[key], b[key])));
45
53
  };
46
54
  const sameValues = (a, b) => a !== undefined && b !== undefined && same(a, b);
47
- export const View = { make };
55
+ /**
56
+ * `make` draws an Actor; `lazy` loads a View made with `make` when it is
57
+ * first drawn; `preload` loads the lazy Views made with these ids.
58
+ */
59
+ export const View = { make, lazy, preload };
@@ -0,0 +1,38 @@
1
+ import { Context } from 'effect';
2
+ /**
3
+ * Where an app's Address lives, besides memory: the browser's own history or
4
+ * TanStack Router's. Every URL here is a whole path with its query, base
5
+ * included. An entry's key is what the router stamps on each history entry it
6
+ * writes, so Back and Forward land on an entry it knows. With no Adapter in
7
+ * the Layer, the app routes in memory and never touches a URL.
8
+ */
9
+ export interface AdapterShape {
10
+ /** The URL shown now: path and query. */
11
+ readonly url: () => string;
12
+ /** The element on the page the URL names now, after its `#`, decoded; none if it names none. */
13
+ readonly hash?: () => string;
14
+ /** The key the router stamped on the entry shown now, if it wrote it. */
15
+ readonly key: () => string | null;
16
+ /** Hear every change of the URL, whoever made it; gives back how to stop. */
17
+ readonly listen: (changed: () => void) => () => void;
18
+ readonly push: (url: string, key: string) => void;
19
+ readonly replace: (url: string, key: string) => void;
20
+ /** Move through the history by `by` entries: -1 is Back. */
21
+ readonly go: (by: number) => void;
22
+ /**
23
+ * Go to a URL outside the app's base, or another site: hand it to whoever
24
+ * owns it. `replace` takes the place of the entry shown, as a Redirect does.
25
+ */
26
+ readonly leave: (url: string, options?: {
27
+ readonly replace?: boolean;
28
+ }) => void;
29
+ }
30
+ declare const Adapter_base: Context.ServiceClass<Adapter, "@kestack/kabuki/routing/Adapter", AdapterShape>;
31
+ export declare class Adapter extends Adapter_base {
32
+ }
33
+ /** The key the router keeps in a history entry's state. */
34
+ export declare const KEY = "@kestack/kabuki/routing";
35
+ /** The key in a history entry's state, if the router wrote one. */
36
+ export declare const keyIn: (state: unknown) => string | null;
37
+ export {};
38
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/routing/adapter/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AASjC;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,gGAAgG;IAChG,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,MAAM,CAAC;IAC7B,yEAAyE;IACzE,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IACrD,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CACd,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,KACrC,IAAI,CAAC;CACX;;AAED,qBAAa,OAAQ,SAAQ,YAE5B;CAAG;AAEJ,2DAA2D;AAC3D,eAAO,MAAM,GAAG,4BAA4B,CAAC;AAE7C,mEAAmE;AACnE,eAAO,MAAM,KAAK,UAAW,OAAO,KAAG,MAAM,GAAG,IAG/C,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { Context } from 'effect';
2
+ export class Adapter extends Context.Service()('@kestack/kabuki/routing/Adapter') {
3
+ }
4
+ /** The key the router keeps in a history entry's state. */
5
+ export const KEY = '@kestack/kabuki/routing';
6
+ /** The key in a history entry's state, if the router wrote one. */
7
+ export const keyIn = (state) => {
8
+ const found = state?.[KEY];
9
+ return typeof found === 'string' ? found : null;
10
+ };
@@ -0,0 +1,8 @@
1
+ import { Layer } from 'effect';
2
+ import { Adapter } from '../index.ts';
3
+ /**
4
+ * The browser's own history, for an app that owns the address: the whole
5
+ * site, or everything under its base.
6
+ */
7
+ export declare const browser: () => Layer.Layer<Adapter, never, never>;
8
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../../src/routing/adapter/browser/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AAIlD;;;GAGG;AACH,eAAO,MAAM,OAAO,0CA2BhB,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { Layer } from 'effect';
2
+ import { Adapter, KEY, keyIn } from '../index.js';
3
+ const CHANGED = '@kestack/kabuki/routing:changed';
4
+ /**
5
+ * The browser's own history, for an app that owns the address: the whole
6
+ * site, or everything under its base.
7
+ */
8
+ export const browser = () => Layer.sync(Adapter, () => {
9
+ const tell = () => dispatchEvent(new Event(CHANGED));
10
+ return {
11
+ url: () => `${location.pathname}${location.search}`,
12
+ hash: () => decodeURIComponent(location.hash.slice(1)),
13
+ key: () => keyIn(history.state),
14
+ listen: (changed) => {
15
+ addEventListener('popstate', changed);
16
+ addEventListener(CHANGED, changed);
17
+ return () => {
18
+ removeEventListener('popstate', changed);
19
+ removeEventListener(CHANGED, changed);
20
+ };
21
+ },
22
+ push: (url, key) => {
23
+ history.pushState({ [KEY]: key }, '', url);
24
+ tell();
25
+ },
26
+ replace: (url, key) => {
27
+ history.replaceState({ [KEY]: key }, '', url);
28
+ tell();
29
+ },
30
+ go: (by) => history.go(by),
31
+ leave: (url, options) => options?.replace ? location.replace(url) : location.assign(url),
32
+ };
33
+ });
@@ -0,0 +1,2 @@
1
+ export { browser } from './browser.ts';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/routing/adapter/browser/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ export { browser } from './browser.js';
@@ -0,0 +1,3 @@
1
+ export { Adapter, KEY, keyIn } from './adapter.ts';
2
+ export type { AdapterShape } from './adapter.ts';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routing/adapter/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ export { Adapter, KEY, keyIn } from './adapter.js';
@@ -0,0 +1,3 @@
1
+ export { tanstack } from './tanstack.ts';
2
+ export type { TanStackHistory } from './tanstack.ts';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/routing/adapter/tanstack/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1 @@
1
+ export { tanstack } from './tanstack.js';
@@ -0,0 +1,27 @@
1
+ import { Layer } from 'effect';
2
+ import { Adapter } from '../index.ts';
3
+ /** The part of a TanStack Router history the Adapter needs: `router.history`. */
4
+ export interface TanStackHistory {
5
+ readonly location: {
6
+ readonly pathname: string;
7
+ readonly search: string;
8
+ readonly hash?: string;
9
+ readonly state: unknown;
10
+ };
11
+ readonly subscribe: (listener: () => void) => () => void;
12
+ readonly push: (path: string, state?: any) => void;
13
+ readonly replace: (path: string, state?: any) => void;
14
+ readonly go: (index: number) => void;
15
+ }
16
+ /**
17
+ * TanStack Router's history, for an app on a TanStack Start catch-all route:
18
+ * TanStack hands everything under the app's base to it, and gets back every
19
+ * URL outside it. Every move goes through TanStack's history, so TanStack
20
+ * keeps its own count of where Back goes and matches the same route again
21
+ * without remounting. By default it finds the router TanStack keeps on the
22
+ * page; `history` hands one over instead.
23
+ */
24
+ export declare const tanstack: (options?: {
25
+ readonly history?: () => TanStackHistory;
26
+ }) => Layer.Layer<Adapter, never, never>;
27
+ //# sourceMappingURL=tanstack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tanstack.d.ts","sourceRoot":"","sources":["../../../../src/routing/adapter/tanstack/tanstack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAc,MAAM,aAAa,CAAC;AAElD,iFAAiF;AACjF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,QAAQ,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;IACzD,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACnD,QAAQ,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACtD,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAcD;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,aAAc;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,eAAe,CAAC;CAC1C,uCAsBG,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { Layer } from 'effect';
2
+ import { Adapter, KEY, keyIn } from '../index.js';
3
+ /** The router TanStack keeps on `window` in a browser. */
4
+ const fromWindow = () => {
5
+ const router = globalThis.__TSR_ROUTER__;
6
+ if (!router)
7
+ throw new Error('[@kestack/kabuki/routing] no TanStack Router on this page: pass `history` to tanstack()');
8
+ return router.history;
9
+ };
10
+ /**
11
+ * TanStack Router's history, for an app on a TanStack Start catch-all route:
12
+ * TanStack hands everything under the app's base to it, and gets back every
13
+ * URL outside it. Every move goes through TanStack's history, so TanStack
14
+ * keeps its own count of where Back goes and matches the same route again
15
+ * without remounting. By default it finds the router TanStack keeps on the
16
+ * page; `history` hands one over instead.
17
+ */
18
+ export const tanstack = (options) => Layer.sync(Adapter, () => {
19
+ const history = (options?.history ?? fromWindow)();
20
+ return {
21
+ url: () => `${history.location.pathname}${history.location.search}`,
22
+ hash: () => decodeURIComponent((history.location.hash ?? '').replace(/^#/, '')),
23
+ key: () => keyIn(history.location.state),
24
+ listen: (changed) => history.subscribe(changed),
25
+ push: (url, key) => history.push(url, { [KEY]: key }),
26
+ replace: (url, key) => history.replace(url, { [KEY]: key }),
27
+ go: (by) => history.go(by),
28
+ // TanStack's history holds this site's URLs alone: another site is
29
+ // the browser's to go to.
30
+ leave: (url, options) => {
31
+ if (new URL(url, location.href).origin !== location.origin)
32
+ return options?.replace
33
+ ? location.replace(url)
34
+ : location.assign(url);
35
+ return options?.replace ? history.replace(url) : history.push(url);
36
+ },
37
+ };
38
+ });
@@ -0,0 +1,12 @@
1
+ import { Layer } from 'effect';
2
+ import { Loading } from './loading.ts';
3
+ import type { RoutesWithPlaces } from './loading.ts';
4
+ /**
5
+ * A `Loading` that keeps what `inner` loads, for the places of `routes`:
6
+ * `Router.cached(Routes, loading(Routes))`. `size` copies at most: the
7
+ * router's `cacheSize` unless said, 50 unless it says.
8
+ */
9
+ export declare const cached: <E, R>(routes: RoutesWithPlaces, inner: Layer.Layer<Loading, E, R>, options?: {
10
+ readonly size?: number;
11
+ }) => Layer.Layer<Loading, E, R>;
12
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/routing/capability/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,KAAK,EAAU,MAAM,QAAQ,CAAC;AAEvE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,EAAU,gBAAgB,EAAU,MAAM,cAAc,CAAC;AAmDrE;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,CAAC,UACjB,gBAAgB,SACjB,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,YACvB;IAAE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KACnC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAoLE,CAAC"}
@@ -0,0 +1,170 @@
1
+ import { Cause, Clock, Deferred, Effect, Layer, Stream } from 'effect';
2
+ import { Loading } from './loading.js';
3
+ /*
4
+ * The Data cache: the last data each place loaded, one copy per name and
5
+ * what its Loader is handed, newest wins. It sits in front of another
6
+ * `Loading` and lives as long as the Layer, so in a browser, as long as the
7
+ * tab; a server has none.
8
+ *
9
+ * 1. Keep every place loaded, up to `size` copies, each with when it
10
+ * arrived; the least recently used goes first, never one for its
11
+ * age. A failure is never kept.
12
+ * 2. Answer from a copy unless asked `again` or `fresh`: `stale` once it
13
+ * is older than its place's Stale time; a static place's never
14
+ * is.
15
+ * 3. Join a place already being loaded: a click during a preload waits
16
+ * for the same answer rather than asking again, and so does the
17
+ * load behind a stale copy. A fresh ask, a reload's, joins
18
+ * nothing, and an answer is kept only if nothing asked later is,
19
+ * and the copies were not dropped since.
20
+ * 4. Batch the places asked for in the same moment into one ask of the
21
+ * Loading behind, so many places loading at once are one
22
+ * request, each answered as it comes. The load runs on its own:
23
+ * a place that stops waiting does not stop it, and its answer is
24
+ * still kept.
25
+ * 5. Seed with the data a server drew the page with, so a page first
26
+ * seen from the server is kept as one loaded in the browser is,
27
+ * arrived when the server loaded it.
28
+ *
29
+ * A preload loads only the places with no fresh copy.
30
+ */
31
+ /** Asks of one batch, split so no ask names a place twice: answers come back by name. */
32
+ const rounds = (asks) => {
33
+ const out = [];
34
+ for (const ask of asks) {
35
+ const round = out.find((each) => each.every((other) => other.place.name !== ask.place.name));
36
+ if (round)
37
+ round.push(ask);
38
+ else
39
+ out.push([ask]);
40
+ }
41
+ return out;
42
+ };
43
+ const reasonOf = (cause) => {
44
+ const error = Cause.squash(cause);
45
+ return error instanceof Error ? error.message : String(error);
46
+ };
47
+ /**
48
+ * A `Loading` that keeps what `inner` loads, for the places of `routes`:
49
+ * `Router.cached(Routes, loading(Routes))`. `size` copies at most: the
50
+ * router's `cacheSize` unless said, 50 unless it says.
51
+ */
52
+ export const cached = (routes, inner, options) => Layer.effect(Loading, Effect.gen(function* () {
53
+ const source = yield* Loading;
54
+ const size = options?.size ?? routes.options?.cacheSize ?? 50;
55
+ const staleTimes = new Map(routes.every.map((place) => [place.name, place.staleTime]));
56
+ // 1. Keep
57
+ const copies = new Map();
58
+ /** Whether a copy of the place is younger than its Stale time. */
59
+ const freshOf = (place, copy, now) => now - copy.at < (staleTimes.get(place.name) ?? 0);
60
+ const flying = new Map();
61
+ let batch = [];
62
+ let asked = 0;
63
+ /** The last ask before the copies were dropped: no answer to one asked by then is kept. */
64
+ let forgotten = 0;
65
+ const keyOf = (place) => JSON.stringify([place.name, place.params]);
66
+ const keep = (key, copy) => {
67
+ copies.delete(key);
68
+ copies.set(key, copy);
69
+ for (const oldest of copies.keys()) {
70
+ if (copies.size <= size)
71
+ break;
72
+ copies.delete(oldest);
73
+ }
74
+ };
75
+ // 4. Batch
76
+ /** Settles one ask with what its place came to, and keeps its data, unless a newer ask's is kept or the copies were dropped since. */
77
+ const settle = (ask, outcome) => Effect.flatMap(Clock.currentTimeMillis, (at) => {
78
+ const { key, answer, seq, since } = ask;
79
+ if (flying.get(key) === ask)
80
+ flying.delete(key);
81
+ if (outcome._tag === 'Loaded' &&
82
+ seq > forgotten &&
83
+ seq > (copies.get(key)?.seq ?? 0))
84
+ keep(key, { data: outcome.data, seq, at });
85
+ return Deferred.succeed(answer, { ...outcome, at, took: at - since });
86
+ });
87
+ /** Asks the Loading behind for a batch, each settled as it comes if it can say so, and any left unanswered broken. */
88
+ const ask = (asks) => {
89
+ const places = asks.map(({ place }) => place);
90
+ const byName = new Map(asks.map((each) => [each.place.name, each]));
91
+ const answered = source.each
92
+ ? Stream.runForEach(source.each(places), ([name, outcome]) => {
93
+ const found = byName.get(name);
94
+ if (!found)
95
+ return Effect.void;
96
+ byName.delete(name);
97
+ return settle(found, outcome);
98
+ })
99
+ : Effect.flatMap(source.load(places), (answers) => Effect.forEach(asks, (each) => {
100
+ const outcome = answers[each.place.name];
101
+ if (!outcome)
102
+ return Effect.void;
103
+ byName.delete(each.place.name);
104
+ return settle(each, outcome);
105
+ }));
106
+ const left = (reason) => Effect.forEach([...byName.values()], (each) => settle(each, { _tag: 'Broken', reason: reason(each.place.name) }));
107
+ return answered.pipe(Effect.andThen(left((name) => `no answer for ${name}`)), Effect.catchCause((cause) => left(() => reasonOf(cause))));
108
+ };
109
+ const flush = Effect.sleep(0).pipe(Effect.andThen(Effect.suspend(() => {
110
+ const taken = batch;
111
+ batch = [];
112
+ return Effect.forEach(rounds(taken), ask, {
113
+ concurrency: 'unbounded',
114
+ });
115
+ })));
116
+ /**
117
+ * The place's answer, being loaded: joined if it already is, else
118
+ * added to the batch. A fresh ask joins nothing: what is on its way
119
+ * was asked before it.
120
+ */
121
+ const loading = (place, fresh = false) => Effect.flatMap(Clock.currentTimeMillis, (since) => {
122
+ const key = keyOf(place);
123
+ // 3. Join
124
+ const going = flying.get(key);
125
+ if (going && !fresh)
126
+ return Effect.succeed(going.answer);
127
+ const answer = Deferred.makeUnsafe();
128
+ const ask = { key, place, answer, seq: (asked += 1), since };
129
+ flying.set(key, ask);
130
+ batch.push(ask);
131
+ return batch.length === 1
132
+ ? Effect.as(Effect.forkDetach(flush), answer)
133
+ : Effect.succeed(answer);
134
+ });
135
+ // 2. Answer
136
+ const one = (place, fresh, again) => Effect.flatMap(Clock.currentTimeMillis, (now) => {
137
+ const key = keyOf(place);
138
+ const copy = copies.get(key);
139
+ if (!fresh && !again && copy) {
140
+ keep(key, copy);
141
+ return Effect.succeed({
142
+ _tag: 'Loaded',
143
+ data: copy.data,
144
+ stale: !freshOf(place, copy, now),
145
+ at: copy.at,
146
+ cached: true,
147
+ });
148
+ }
149
+ return Effect.flatMap(loading(place, fresh), Deferred.await);
150
+ });
151
+ return {
152
+ load: (places, options) => Effect.forEach(places, (place) => Effect.map(one(place, options?.fresh === true, options?.again === true), (answer) => [place.name, answer]), { concurrency: 'unbounded' }).pipe(Effect.map((answers) => Object.fromEntries(answers))),
153
+ preload: (places) => Effect.flatMap(Clock.currentTimeMillis, (now) => Effect.forEach(places.filter((place) => {
154
+ const copy = copies.get(keyOf(place));
155
+ return !copy || !freshOf(place, copy, now);
156
+ }), (place) => loading(place), { discard: true })),
157
+ // 5. Seed
158
+ keep: (place, data, arrived) => Effect.flatMap(Clock.currentTimeMillis, (now) => Effect.sync(() => {
159
+ const key = keyOf(place);
160
+ if (!copies.has(key))
161
+ keep(key, { data, seq: (asked += 1), at: arrived ?? now });
162
+ })),
163
+ // What is on its way was asked before: it is neither joined nor kept.
164
+ forget: Effect.sync(() => {
165
+ copies.clear();
166
+ flying.clear();
167
+ forgotten = asked;
168
+ }),
169
+ };
170
+ })).pipe(Layer.provide(inner));
@@ -0,0 +1,6 @@
1
+ export { Loading, memory, outcomeOf } from './loading.ts';
2
+ export type { Answer, RoutesWithPlaces, Wanted } from './loading.ts';
3
+ export { cached } from './cache.ts';
4
+ export { Navigation, back, backTo, forward, moveOptions, preload, push, reload, replace, } from './navigation.ts';
5
+ export type { AnyRoute, HashScrollOptions, MoveOptions } from './navigation.ts';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routing/capability/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC1D,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EACL,UAAU,EACV,IAAI,EACJ,MAAM,EACN,OAAO,EACP,WAAW,EACX,OAAO,EACP,IAAI,EACJ,MAAM,EACN,OAAO,GACR,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,QAAQ,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { Loading, memory, outcomeOf } from './loading.js';
2
+ export { cached } from './cache.js';
3
+ export { Navigation, back, backTo, forward, moveOptions, preload, push, reload, replace, } from './navigation.js';