@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
@@ -7,9 +7,8 @@ type Given = {
7
7
  };
8
8
  export declare const create: (definition: ActorDefinition, id: string, key: string | undefined, { input, props }: Given) => Instance;
9
9
  /**
10
- * Enter the Instance's State: every Child the State writes is Invoked fresh.
11
- * A Child written at the top lasts through the Transition; then every Child
12
- * follows the parent's data.
10
+ * Enter the Instance's State: only the Children written at the top are
11
+ * kept, and following the parent's data Invokes the State's own fresh.
13
12
  */
14
13
  export declare const enter: (definition: ActorDefinition, instance: Instance) => Instance;
15
14
  export declare const follow: (definition: ActorDefinition, instance: Instance) => Instance;
@@ -1 +1 @@
1
- {"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../../src/core/snapshot/invoke.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAmB9C,6FAA6F;AAC7F,KAAK,KAAK,GAAG;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAMpE,eAAO,MAAM,MAAM,eACL,eAAe,MACvB,MAAM,OACL,MAAM,GAAG,SAAS,oBACL,KAAK,KACtB,QAkBF,CAAC;AAGF;;;;GAIG;AACH,eAAO,MAAM,KAAK,eACJ,eAAe,YACjB,QAAQ,KACjB,QA0BF,CAAC;AAGF,eAAO,MAAM,MAAM,eACL,eAAe,YACjB,QAAQ,KACjB,QAmCF,CAAC"}
1
+ {"version":3,"file":"invoke.d.ts","sourceRoot":"","sources":["../../../src/core/snapshot/invoke.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAsB9C,6FAA6F;AAC7F,KAAK,KAAK,GAAG;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAKpE,eAAO,MAAM,MAAM,eACL,eAAe,MACvB,MAAM,OACL,MAAM,GAAG,SAAS,oBACL,KAAK,KACtB,QAkBF,CAAC;AAGF;;;GAGG;AACH,eAAO,MAAM,KAAK,eACJ,eAAe,YACjB,QAAQ,KACjB,QAWF,CAAC;AAGF,eAAO,MAAM,MAAM,eACL,eAAe,YACjB,QAAQ,KACjB,QAsDF,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { Equal } from 'effect';
2
- import { childId, counterOf } from './tree.js';
2
+ import { childId, counterOf, isMany } from './tree.js';
3
3
  const SINGLE = { _tag: 'Single' };
4
4
  // 1. Create
5
5
  export const create = (definition, id, key, { input, props }) => {
@@ -23,65 +23,64 @@ export const create = (definition, id, key, { input, props }) => {
23
23
  };
24
24
  // 2. Enter
25
25
  /**
26
- * Enter the Instance's State: every Child the State writes is Invoked fresh.
27
- * A Child written at the top lasts through the Transition; then every Child
28
- * follows the parent's data.
26
+ * Enter the Instance's State: only the Children written at the top are
27
+ * kept, and following the parent's data Invokes the State's own fresh.
29
28
  */
30
29
  export const enter = (definition, instance) => {
31
- const slots = definition.children[instance.state._tag];
32
- if (!slots)
33
- return { ...instance, children: {} };
34
- const invocation = invocationOf(definition, instance);
35
- const invoked = { ...instance.invoked };
36
30
  const children = {};
37
- for (const [slot, { definition: child, many }] of Object.entries(slots)) {
31
+ for (const slot of Object.keys(definition.children[instance.state._tag] ?? {})) {
38
32
  const kept = definition.lasting.includes(slot)
39
33
  ? instance.children[slot]
40
34
  : undefined;
41
- children[slot] =
42
- kept ??
43
- (many
44
- ? keyedIn(invocation, slot).map((given) => invoke(child, instance.id, slot, given.key, given, invoked))
45
- : invoke(child, instance.id, slot, undefined, givenIn(invocation, slot), invoked));
35
+ if (kept)
36
+ children[slot] = kept;
46
37
  }
47
- return follow(definition, { ...instance, children, invoked });
38
+ return follow(definition, { ...instance, children });
48
39
  };
49
40
  // 3. Follow
50
41
  export const follow = (definition, instance) => {
51
42
  const slots = Object.entries(definition.children[instance.state._tag] ?? {});
52
43
  if (slots.length === 0)
53
44
  return instance;
54
- const invocation = invocationOf(definition, instance);
45
+ const data = {
46
+ model: instance.model,
47
+ state: instance.state,
48
+ props: instance.props,
49
+ };
55
50
  let invoked;
56
51
  let children;
57
- for (const [slot, { definition: child, many }] of slots) {
58
- if (!many) {
59
- const held = instance.children[slot];
60
- if (!held)
61
- continue;
62
- const handed = handedProps(child, held, givenIn(invocation, slot).props);
63
- if (handed !== held)
64
- (children ??= { ...instance.children })[slot] = handed;
65
- continue;
66
- }
67
- const before = (instance.children[slot] ?? []);
68
- const byKey = new Map(before.map((kept) => [kept.key, kept]));
69
- const wanted = keyedIn(invocation, slot);
70
- let changed = wanted.length !== before.length;
71
- const after = wanted.map((given, index) => {
72
- const kept = byKey.get(given.key);
52
+ for (const [slot, { definition: child, kind, keyed, keys, input, props },] of slots) {
53
+ const many = kind === 'each';
54
+ const held = instance.children[slot];
55
+ const before = (held === undefined ? [] : isMany(held) ? held : [held]);
56
+ const byKey = new Map(before.map((kept) => [kept.key ?? '', kept]));
57
+ const wanted = keysOf(slot, keys(data));
58
+ // `each` is always held, as a list even when empty.
59
+ let changed = wanted.length !== before.length || (many && held === undefined);
60
+ const after = wanted.map((key, index) => {
61
+ const kept = byKey.get(key);
73
62
  if (kept) {
74
- const handed = handedProps(child, kept, given.props);
75
- if (before[index] !== handed)
63
+ const next = handedProps(child, kept, props(data, key));
64
+ if (before[index] !== next)
76
65
  changed = true;
77
- return handed;
66
+ return next;
78
67
  }
79
68
  changed = true;
80
69
  invoked ??= { ...instance.invoked };
81
- return invoke(child, instance.id, slot, given.key, given, invoked);
70
+ return invoke(child, instance.id, slot, many || keyed ? key : undefined, {
71
+ input: input(data, key),
72
+ props: props(data, key),
73
+ }, invoked);
82
74
  });
83
- if (changed)
84
- (children ??= { ...instance.children })[slot] = after;
75
+ if (!changed)
76
+ continue;
77
+ children ??= { ...instance.children };
78
+ if (many)
79
+ children[slot] = after;
80
+ else if (after[0])
81
+ children[slot] = after[0];
82
+ else
83
+ delete children[slot];
85
84
  }
86
85
  return children
87
86
  ? { ...instance, children, invoked: invoked ?? instance.invoked }
@@ -101,19 +100,13 @@ const invoke = (definition, parent, slot, key, given, invoked) => {
101
100
  invoked[counter] = generation;
102
101
  return create(definition, childId(parent, slot, key, generation), key, given);
103
102
  };
104
- const invocationOf = (definition, instance) => definition.invoke[instance.state._tag]?.({
105
- model: instance.model,
106
- state: instance.state,
107
- props: instance.props,
108
- }) ?? {};
109
- const givenIn = (invocation, slot) => (invocation[slot] ?? {});
110
- const keyedIn = (invocation, slot) => {
111
- const keyed = (invocation[slot] ?? []);
103
+ /** The keys a Condition names, each once. */
104
+ const keysOf = (slot, keys) => {
112
105
  const seen = new Set();
113
- for (const { key } of keyed) {
106
+ for (const key of keys) {
114
107
  if (seen.has(key))
115
108
  throw new Error(`[@kestack/kabuki] ${slot}: key "${key}" is Invoked twice`);
116
109
  seen.add(key);
117
110
  }
118
- return keyed;
111
+ return keys;
119
112
  };
@@ -4,8 +4,8 @@ import { counterOf, isMany, slotToward } from './tree.js';
4
4
  * saved before. The only Message no Update sees. Before it is put in place,
5
5
  * every Instance is checked against its Actor: its ID, its Actor's name, its
6
6
  * State, its Model, its Props and its Children's slots, every one its State
7
- * Invokes and no other; one that does not
8
- * match is a reason, and the Restore is ignored.
7
+ * always holds and no other; one that does not match is a reason, and the
8
+ * Restore is ignored.
9
9
  */
10
10
  export const RESTORE = '@kestack/kabuki/Restore';
11
11
  export const isRestore = (message) => message._tag === RESTORE;
@@ -38,15 +38,16 @@ const check = (definition, instance, key) => {
38
38
  return error.message;
39
39
  }
40
40
  const slots = definition.children[tag] ?? {};
41
- for (const slot of Object.keys(slots))
42
- if (!(slot in instance.children))
41
+ for (const [slot, { kind }] of Object.entries(slots))
42
+ if (kind !== 'while' && !(slot in instance.children))
43
43
  return `${id} is in ${tag}, which Invokes "${slot}", but holds no such Child`;
44
44
  for (const [slot, held] of Object.entries(instance.children)) {
45
45
  const wanted = slots[slot];
46
46
  if (!wanted)
47
47
  return `${id} has a Child "${slot}" that ${tag} does not Invoke`;
48
- if (isMany(held) !== wanted.many)
49
- return `${id}'s Child "${slot}" is ${wanted.many ? '' : 'not '}keyed`;
48
+ const many = wanted.kind === 'each';
49
+ if (isMany(held) !== many)
50
+ return `${id}'s Child "${slot}" is ${many ? '' : 'not '}keyed`;
50
51
  for (const child of isMany(held) ? held : [held]) {
51
52
  if (slotToward(id, child.id) !== slot)
52
53
  return `${child.id} is not under ${id}'s "${slot}"`;
@@ -54,7 +55,7 @@ const check = (definition, instance, key) => {
54
55
  const counted = instance.invoked[counterOf(slot, child.key)] ?? 0;
55
56
  if (!(generation <= counted))
56
57
  return `${id} has Invoked "${slot}" ${counted} times, but holds ${child.id}`;
57
- const reason = check(wanted.definition, child, isMany(held) ? child.key : undefined);
58
+ const reason = check(wanted.definition, child, isMany(held) || wanted.keyed ? child.key : undefined);
58
59
  if (reason !== undefined)
59
60
  return reason;
60
61
  }
@@ -7,7 +7,7 @@ export declare const counterOf: (slot: string, key: string | undefined) => strin
7
7
  export declare const within: (ancestor: string, id: string) => boolean;
8
8
  /** The slot under `parent` on the way down to `id`, if `id` is below it. */
9
9
  export declare const slotToward: (parent: string, id: string) => string | undefined;
10
- /** Tells a keyed Child slot from a fixed one in an Instance's Children. */
10
+ /** Tells the list of an `each` Child from one Instance in an Instance's Children. */
11
11
  export declare const isMany: (held: Instance | ReadonlyArray<Instance>) => held is ReadonlyArray<Instance>;
12
12
  /** One Instance on the way down, with its Actor and the slot it sits in. */
13
13
  export interface Step {
@@ -1 +1 @@
1
- {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../../src/core/snapshot/tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAcxD,eAAO,MAAM,OAAO,WACV,MAAM,QACR,MAAM,OACP,MAAM,GAAG,SAAS,cACX,MAAM,KACjB,MAC0F,CAAC;AAE9F,+DAA+D;AAC/D,eAAO,MAAM,SAAS,SAAU,MAAM,OAAO,MAAM,GAAG,SAAS,KAAG,MACA,CAAC;AAEnE,8CAA8C;AAC9C,eAAO,MAAM,MAAM,aAAc,MAAM,MAAM,MAAM,KAAG,OACJ,CAAC;AAEnD,4EAA4E;AAC5E,eAAO,MAAM,UAAU,WAAY,MAAM,MAAM,MAAM,KAAG,MAAM,GAAG,SAKhE,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,MAAM,SACX,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,KACvC,IAAI,IAAI,aAAa,CAAC,QAAQ,CAAwB,CAAC;AAE1D,4EAA4E;AAC5E,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,uFAAuF;AACvF,eAAO,MAAM,MAAM,SACX,eAAe,YACX,QAAQ,MACd,MAAM,KACT,aAAa,CAAC,IAAI,CAAC,GAAG,SAexB,CAAC;AAEF,4HAA4H;AAC5H,eAAO,MAAM,OAAO,SAAU,aAAa,CAAC,IAAI,CAAC,SAAS,QAAQ,KAAG,QAa1D,CAAC;AAEZ,2DAA2D;AAC3D,eAAO,MAAM,UAAU,aACX,QAAQ,MACd,MAAM,KACT,QAAQ,GAAG,SAQb,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,OAAO,SACZ,eAAe,YACX,QAAQ,MACd,MAAM,KACT,eAAe,GAAG,SAC2B,CAAC"}
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../../src/core/snapshot/tree.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAcxD,eAAO,MAAM,OAAO,WACV,MAAM,QACR,MAAM,OACP,MAAM,GAAG,SAAS,cACX,MAAM,KACjB,MAC0F,CAAC;AAE9F,+DAA+D;AAC/D,eAAO,MAAM,SAAS,SAAU,MAAM,OAAO,MAAM,GAAG,SAAS,KAAG,MACA,CAAC;AAEnE,8CAA8C;AAC9C,eAAO,MAAM,MAAM,aAAc,MAAM,MAAM,MAAM,KAAG,OACJ,CAAC;AAEnD,4EAA4E;AAC5E,eAAO,MAAM,UAAU,WAAY,MAAM,MAAM,MAAM,KAAG,MAAM,GAAG,SAKhE,CAAC;AAEF,qFAAqF;AACrF,eAAO,MAAM,MAAM,SACX,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,KACvC,IAAI,IAAI,aAAa,CAAC,QAAQ,CAAwB,CAAC;AAE1D,4EAA4E;AAC5E,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,uFAAuF;AACvF,eAAO,MAAM,MAAM,SACX,eAAe,YACX,QAAQ,MACd,MAAM,KACT,aAAa,CAAC,IAAI,CAAC,GAAG,SAexB,CAAC;AAEF,4HAA4H;AAC5H,eAAO,MAAM,OAAO,SAAU,aAAa,CAAC,IAAI,CAAC,SAAS,QAAQ,KAAG,QAa1D,CAAC;AAEZ,2DAA2D;AAC3D,eAAO,MAAM,UAAU,aACX,QAAQ,MACd,MAAM,KACT,QAAQ,GAAG,SAQb,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,OAAO,SACZ,eAAe,YACX,QAAQ,MACd,MAAM,KACT,eAAe,GAAG,SAC2B,CAAC"}
@@ -22,7 +22,7 @@ export const slotToward = (parent, id) => {
22
22
  const end = rest.search(/[[#]/);
23
23
  return end < 0 ? undefined : rest.slice(0, end);
24
24
  };
25
- /** Tells a keyed Child slot from a fixed one in an Instance's Children. */
25
+ /** Tells the list of an `each` Child from one Instance in an Instance's Children. */
26
26
  export const isMany = (held) => Array.isArray(held);
27
27
  /** The Instances from the root down to `id`, with their Actors; none if it is gone. */
28
28
  export const pathTo = (root, snapshot, id) => {
@@ -3,6 +3,7 @@ import { Cause } from 'effect';
3
3
  import type { Layer } from 'effect';
4
4
  import { History } from '../core/index.ts';
5
5
  import type { AnyActor, Entry, Needs, SavedHistory, WorkerStatus } from '../core/index.ts';
6
+ import type { LazyView } from './lazy.tsx';
6
7
  import type { ViewOf } from './view.tsx';
7
8
  type Unmet<A, Provided> = [Exclude<Needs<A>, Provided>] extends [never] ? [] : [unmet: {
8
9
  readonly missingCapabilities: Exclude<Needs<A>, Provided>;
@@ -18,6 +19,19 @@ export interface AppOptions {
18
19
  readonly name?: string;
19
20
  /** A History saved before: the app is first drawn at its Head, and its Runtime carries on from there. */
20
21
  readonly saved?: SavedHistory;
22
+ /**
23
+ * The root's Input, read once, when the app first makes its History: not
24
+ * for a saved History, nor one a hot reload left. A function, so it can
25
+ * read what is only there at that moment, like the browser's URL.
26
+ */
27
+ readonly input?: () => unknown;
28
+ /**
29
+ * How many entries its History keeps on the Head's Branch; every one by
30
+ * default. `history.keep` changes it later, and the change is remembered
31
+ * in the browser under the app's name, so it outlasts a reload and wins
32
+ * over this.
33
+ */
34
+ readonly window?: number | null;
21
35
  }
22
36
  /** The running app, for code outside the tree: a timeline, a devtool. */
23
37
  export interface AppRuntime<A = AnyActor> {
@@ -39,11 +53,24 @@ export interface AppRuntime<A = AnyActor> {
39
53
  /** Why the last start, stop or clear failed, also logged; `null` once one succeeds. */
40
54
  readonly failure: Cause.Cause<unknown> | null;
41
55
  }
42
- export declare const toReact: <A extends AnyActor, Provided>(actor: A, view: ViewOf<A>, layer: Layer.Layer<Provided, never, never>, options?: AppOptions, ..._unmet: Unmet<A, Provided>) => (({ fallback }: {
43
- readonly fallback?: ReactNode;
44
- }) => import("react").JSX.Element) & {
56
+ export declare const toReact: <A extends AnyActor, Provided>(actor: A, view: ViewOf<A>, layer: Layer.Layer<Provided, never, never>, options?: AppOptions, ..._unmet: Unmet<A, Provided>) => (({ fallback, saved, onLazyView }: AppProps) => import("react").JSX.Element) & {
45
57
  displayName: string;
46
58
  useRuntime: () => AppRuntime<A>;
47
59
  };
60
+ /** What the App component takes. */
61
+ export interface AppProps {
62
+ /** Drawn while the History is replayed, or a lazy View loaded. */
63
+ readonly fallback?: ReactNode;
64
+ /**
65
+ * A History to draw, given when the App is drawn, so a server draws each
66
+ * request's own: drawn at its Head with no Runtime, as `options.saved` is.
67
+ * The first mount in a browser takes it as the app's History and starts
68
+ * the Runtime from it, unless an app of this name has one already, as
69
+ * after a hot reload. Read once: a later one is not drawn.
70
+ */
71
+ readonly saved?: SavedHistory;
72
+ /** Told of every lazy View this drawing draws, so a server can preload their code in the page. */
73
+ readonly onLazyView?: (view: LazyView<AnyActor>) => void;
74
+ }
48
75
  export {};
49
76
  //# sourceMappingURL=app.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/react/app.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,KAAK,EAA8B,MAAM,QAAQ,CAAC;AAC3D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,OAAO,EAAW,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EACV,QAAQ,EACR,KAAK,EACL,KAAK,EAEL,YAAY,EAEZ,YAAY,EACb,MAAM,kBAAkB,CAAC;AAI1B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAkBzC,KAAK,KAAK,CAAC,CAAC,EAAE,QAAQ,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACnE,EAAE,GACF,CAAC,KAAK,EAAE;IAAE,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;CAAE,CAAC,CAAC;AAE3E,yDAAyD;AACzD,KAAK,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAE5B,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,yGAAyG;IACzG,QAAQ,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC;CAC/B;AAED,yEAAyE;AACzE,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,QAAQ;IACtC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3C,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAC;IAC1B,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,aAAa,CAAC,YAAY,CAAC,CAAC;IACpE,uFAAuF;IACvF,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CAC/C;AAID,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS,QAAQ,EAAE,QAAQ,SAC3C,CAAC,QACF,MAAM,CAAC,CAAC,CAAC,SACR,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,YACjC,UAAU,aACR,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,qBAoMK;IAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE;;sBAWjD,UAAU,CAAC,CAAC,CAAC;CAqBhC,CAAC"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/react/app.tsx"],"names":[],"mappings":"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"}
package/dist/react/app.js CHANGED
@@ -4,6 +4,7 @@ import { Suspense, use, useEffect, useSyncExternalStore } from 'react';
4
4
  import { Cause, Effect, Exit, Fiber, Scope } from 'effect';
5
5
  import { History, Runtime } from '../core/index.js';
6
6
  import { makeDrawn } from './drawn.js';
7
+ import { LazyContext } from './lazy.js';
7
8
  import { registry } from './registry.js';
8
9
  import { AppContext } from './view.js';
9
10
  const NO_WORKERS = [];
@@ -12,14 +13,42 @@ export const toReact = (actor, view, layer, options = {}, ..._unmet) => {
12
13
  const name = options.name ?? actor.definition.name;
13
14
  const root = { id: actor.definition.name };
14
15
  const app = {};
15
- const own = () => options.saved ? History.load(actor, options.saved) : History.make(actor);
16
+ // 4. Keep
17
+ const windowKey = `@kestack/kabuki:${name}:window`;
18
+ /** The Window this app was left with, else the one it is given. */
19
+ const keptWindow = () => {
20
+ const stored = readStored(windowKey);
21
+ if (stored === 'all')
22
+ return { window: null };
23
+ const count = Number(stored);
24
+ if (stored !== undefined && Number.isInteger(count) && count >= 1)
25
+ return { window: count };
26
+ return options.window === undefined ? {} : { window: options.window };
27
+ };
28
+ let written;
29
+ const remember = (window) => {
30
+ if (written === undefined)
31
+ written = keptWindow().window ?? null;
32
+ if (window === written)
33
+ return;
34
+ written = window;
35
+ writeStored(windowKey, window === null ? 'all' : `${window}`);
36
+ };
37
+ const own = () => options.saved
38
+ ? History.load(actor, options.saved, keptWindow())
39
+ : History.make(actor, {
40
+ ...keptWindow(),
41
+ ...(options.input ? { input: options.input() } : {}),
42
+ });
16
43
  /** The History an app of this name holds or left, read with this code, else its own. */
17
44
  const adopt = () => {
18
45
  const left = registry.history(name);
19
- return left ? History.load(actor, left.save()) : own();
46
+ return left
47
+ ? History.load(actor, left.save(), { window: left.window() })
48
+ : own();
20
49
  };
21
- let history;
22
- let first;
50
+ /** The drawing of the app's own History: what the Runtime runs and `useRuntime` reads. */
51
+ let mine;
23
52
  let runtime;
24
53
  let shown = null;
25
54
  let past;
@@ -31,15 +60,46 @@ export const toReact = (actor, view, layer, options = {}, ..._unmet) => {
31
60
  for (const listener of listeners)
32
61
  listener();
33
62
  };
63
+ const subscribe = (listener) => {
64
+ listeners.add(listener);
65
+ return () => listeners.delete(listener);
66
+ };
67
+ /** One History drawn: the app's own, or one given when drawn, which only the app's own Runtime ever runs. */
68
+ const makeDraw = (history) => {
69
+ const draw = { history, first: undefined, drawn: undefined };
70
+ const live = () => shown === null ? (runtime?.snapshot() ?? draw.first?.snapshot) : past;
71
+ draw.drawn = makeDrawn(() => (draw === mine ? live() : draw.first?.snapshot), () => draw.first?.snapshot, (id, message) => {
72
+ if (draw === mine && shown === null)
73
+ runtime?.send(id, message, { kind: 'view', instance: id });
74
+ }, subscribe, () => draw === mine && shown === null);
75
+ return draw;
76
+ };
34
77
  let unwatch = () => { };
35
78
  const take = (next) => {
36
79
  unwatch();
37
- history = next;
38
- first = undefined;
39
- unwatch = next.subscribe(changed);
80
+ mine = next;
81
+ const { history } = next;
82
+ unwatch = history.subscribe(() => {
83
+ if (typeof shown === 'number' && !history.has(shown))
84
+ show(history.branch()[0]?.id ?? 'init');
85
+ remember(history.window());
86
+ changed();
87
+ });
40
88
  return next;
41
89
  };
42
- const historyOf = () => history ?? take(adopt());
90
+ const whose = () => mine;
91
+ const mineOf = () => mine ?? take(makeDraw(adopt()));
92
+ const historyOf = () => mineOf().history;
93
+ /** The drawing of a History given when drawn, one per saved History, so a draw that suspends finds it again. */
94
+ const given = new WeakMap();
95
+ const givenOf = (saved) => {
96
+ let draw = given.get(saved);
97
+ if (!draw)
98
+ given.set(saved, (draw = makeDraw(History.load(actor, saved, keptWindow()))));
99
+ return draw;
100
+ };
101
+ /** Until the app has a History of its own, a given one is drawn as it is. */
102
+ const takesGiven = (saved) => saved !== undefined && mounts === 0 && !registry.history(name);
43
103
  /** What came of a start, stop or clear: an interruption is a newer one taking over, not a failure. */
44
104
  const settled = (what) => (exit) => {
45
105
  if (Exit.isSuccess(exit)) {
@@ -56,52 +116,51 @@ export const toReact = (actor, view, layer, options = {}, ..._unmet) => {
56
116
  changed();
57
117
  };
58
118
  // 1. Draw
59
- /** Start replaying the History to its Head, once: what the app draws before its Runtime starts. */
60
- const firstDraw = () => {
61
- if (first)
62
- return first;
63
- const from = historyOf();
64
- const draw = { snapshot: undefined, replaying: undefined };
65
- draw.replaying =
66
- from.head() === null
67
- ? fulfilled((draw.snapshot = from.snapshotAt(null)))
119
+ /**
120
+ * Start replaying a History to its Head, once: what the app draws before
121
+ * its Runtime starts. Init, or one entry such as a Restore, is drawn at
122
+ * once; a longer Branch is replayed without blocking the page.
123
+ */
124
+ const firstDraw = (draw) => {
125
+ if (draw.first)
126
+ return draw.first;
127
+ const from = draw.history;
128
+ const head = from.head();
129
+ const first = { snapshot: undefined, replaying: undefined };
130
+ first.replaying =
131
+ head === null || from.branch(head).length <= 1
132
+ ? fulfilled((first.snapshot = from.snapshotAt(head)))
68
133
  : tracked(Effect.runPromise(from.seek()).then((snapshot) => {
69
- draw.snapshot = snapshot;
134
+ first.snapshot = snapshot;
70
135
  changed();
71
136
  return snapshot;
72
137
  }));
73
- return (first = draw);
138
+ return (draw.first = first);
74
139
  };
75
140
  /** Before the Runtime starts, wait for the first Snapshot: React shows the fallback until it is replayed. */
76
- const waitForFirstDraw = () => {
77
- const { snapshot, replaying } = firstDraw();
141
+ const waitForFirstDraw = (draw) => {
142
+ const { snapshot, replaying } = firstDraw(draw);
78
143
  if (!snapshot)
79
144
  use(replaying);
80
145
  };
81
- const snapshot = () => shown === null ? (runtime?.snapshot() ?? first?.snapshot) : past;
82
- const drawn = makeDrawn(snapshot, () => first?.snapshot, (id, message) => {
83
- if (shown === null)
84
- runtime?.send(id, message, { kind: 'view', instance: id });
85
- }, (listener) => {
86
- listeners.add(listener);
87
- return () => listeners.delete(listener);
88
- });
89
146
  // 2. Mount
90
147
  let mounts = 0;
91
148
  let close = () => { };
92
149
  let unsubscribe = () => { };
93
- const mount = () => {
150
+ const mount = (saved) => {
151
+ if (takesGiven(saved))
152
+ take(givenOf(saved));
94
153
  if (mounts++ === 0) {
95
154
  if (!registry.claim(name, app, historyOf())) {
96
155
  console.error(`[@kestack/kabuki] two apps named ${name} are mounted at once; they cannot share a History`);
97
- take(own());
156
+ take(makeDraw(own()));
98
157
  changed();
99
158
  }
100
159
  let open = true;
101
160
  close = () => {
102
161
  open = false;
103
162
  };
104
- firstDraw().replaying.then(() => {
163
+ firstDraw(mineOf()).replaying.then(() => {
105
164
  if (!open)
106
165
  return;
107
166
  close = host(historyOf(), layer, (exit) => {
@@ -158,19 +217,24 @@ export const toReact = (actor, view, layer, options = {}, ..._unmet) => {
158
217
  shown,
159
218
  failure,
160
219
  });
161
- const Drawn = () => {
162
- if (!runtime)
163
- waitForFirstDraw();
164
- return (_jsx(AppContext, { value: drawn, children: _jsx(RootView, { instance: root }) }));
220
+ const Drawn = ({ draw }) => {
221
+ if (!runtime || draw !== mine)
222
+ waitForFirstDraw(draw);
223
+ return (_jsx(AppContext, { value: draw.drawn, children: _jsx(RootView, { instance: root }) }));
165
224
  };
166
- const App = ({ fallback = null }) => {
167
- useEffect(mount, []);
168
- return (_jsx(Suspense, { fallback: fallback, children: _jsx(Drawn, {}) }));
225
+ const App = ({ fallback = null, saved, onLazyView }) => {
226
+ // The History given is read once, at the first mount, like `input`.
227
+ useEffect(() => mount(saved), []);
228
+ // Drawn again when the app takes another History of its own.
229
+ useSyncExternalStore(subscribe, whose, whose);
230
+ const draw = takesGiven(saved) ? givenOf(saved) : mineOf();
231
+ const drawn = (_jsx(Suspense, { fallback: fallback, children: _jsx(Drawn, { draw: draw }) }));
232
+ return onLazyView ? (_jsx(LazyContext, { value: onLazyView, children: drawn })) : (drawn);
169
233
  };
170
234
  return Object.assign(App, {
171
235
  displayName: name,
172
236
  useRuntime: () => {
173
- const { head, running, shown, failure } = useSyncExternalStore(drawn.subscribe, read, read);
237
+ const { head, running, shown, failure } = useSyncExternalStore(subscribe, read, read);
174
238
  return {
175
239
  history: historyOf(),
176
240
  branch: historyOf().branch(head),
@@ -212,3 +276,18 @@ const host = (history, layer, onSettled) => {
212
276
  void Effect.runPromise(Fiber.interrupt(starting).pipe(Effect.andThen(Scope.close(scope, Exit.void))));
213
277
  };
214
278
  };
279
+ /** A value kept in the browser, or nothing: on a server, or where storage is refused. */
280
+ const readStored = (key) => {
281
+ try {
282
+ return globalThis.localStorage?.getItem(key) ?? undefined;
283
+ }
284
+ catch {
285
+ return undefined;
286
+ }
287
+ };
288
+ const writeStored = (key, value) => {
289
+ try {
290
+ globalThis.localStorage?.setItem(key, value);
291
+ }
292
+ catch { }
293
+ };
@@ -1,4 +1,4 @@
1
1
  import type { Snapshot, Tagged } from '../core/index.ts';
2
2
  import type { DrawnApp } from './view.tsx';
3
- export declare const makeDrawn: (drawn: () => Snapshot | undefined, served: () => Snapshot | undefined, deliver: (id: string, message: Tagged) => void, subscribe: DrawnApp['subscribe']) => DrawnApp;
3
+ export declare const makeDrawn: (drawn: () => Snapshot | undefined, served: () => Snapshot | undefined, deliver: (id: string, message: Tagged) => void, subscribe: DrawnApp['subscribe'], live?: () => boolean) => DrawnApp;
4
4
  //# sourceMappingURL=drawn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"drawn.d.ts","sourceRoot":"","sources":["../../src/react/drawn.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAsB,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,KAAK,EAAY,QAAQ,EAAU,MAAM,YAAY,CAAC;AAW7D,eAAO,MAAM,SAAS,UACb,MAAM,QAAQ,GAAG,SAAS,UACzB,MAAM,QAAQ,GAAG,SAAS,WACzB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,aACnC,QAAQ,CAAC,WAAW,CAAC,KAC/B,QA0FF,CAAC"}
1
+ {"version":3,"file":"drawn.d.ts","sourceRoot":"","sources":["../../src/react/drawn.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAsB,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,KAAK,EAAY,QAAQ,EAAU,MAAM,YAAY,CAAC;AAW7D,eAAO,MAAM,SAAS,UACb,MAAM,QAAQ,GAAG,SAAS,UACzB,MAAM,QAAQ,GAAG,SAAS,WACzB,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,aACnC,QAAQ,CAAC,WAAW,CAAC,SAC1B,MAAM,OAAO,KAClB,QA2FF,CAAC"}
@@ -8,7 +8,7 @@ import { instanceAt, isMany } from '../core/index.js';
8
8
  * started cannot Send. What it keeps for an Instance is let go once the
9
9
  * Instance is in neither Snapshot.
10
10
  */
11
- export const makeDrawn = (drawn, served, deliver, subscribe) => {
11
+ export const makeDrawn = (drawn, served, deliver, subscribe, live = () => true) => {
12
12
  const handles = new Map();
13
13
  const sends = new Map();
14
14
  const cache = new Map();
@@ -74,6 +74,7 @@ export const makeDrawn = (drawn, served, deliver, subscribe) => {
74
74
  };
75
75
  return {
76
76
  subscribe,
77
+ live,
77
78
  instance: (id) => {
78
79
  prune();
79
80
  return propsIn(cache, drawn(), id);
@@ -1,7 +1,10 @@
1
1
  export { toReact } from './app.tsx';
2
2
  export { View } from './view.tsx';
3
3
  export { useChild, useChildren } from './child.ts';
4
+ export { useLive } from './live.ts';
4
5
  export type { Handle, Root, ViewOf, ViewProps } from './view.tsx';
5
6
  export type { ChildData } from './child.ts';
6
7
  export type { AppOptions, AppRuntime } from './app.tsx';
8
+ export { lazyOf } from './lazy.tsx';
9
+ export type { LazyView } from './lazy.tsx';
7
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAClE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAClE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC"}
@@ -1,3 +1,5 @@
1
1
  export { toReact } from './app.js';
2
2
  export { View } from './view.js';
3
3
  export { useChild, useChildren } from './child.js';
4
+ export { useLive } from './live.js';
5
+ export { lazyOf } from './lazy.js';
@@ -0,0 +1,30 @@
1
+ import type { AnyActor } from '../core/index.ts';
2
+ import type { ViewOf } from './view.tsx';
3
+ /** A View whose code is loaded when it is first drawn, or preloaded. */
4
+ export type LazyView<A> = ViewOf<A> & {
5
+ /** What the View is known by across server and browser, if it was given one: its module, for Start. */
6
+ readonly id: string | undefined;
7
+ /** Load its code now; resolves once it is loaded, at once if it already is. */
8
+ readonly preload: () => Promise<void>;
9
+ /** Whether its code is loaded, so drawing it does not suspend. */
10
+ readonly loaded: boolean;
11
+ };
12
+ export interface LazyOptions {
13
+ /** What the View is known by across server and browser: Start's Vite plugin gives its module's id. */
14
+ readonly id?: string;
15
+ }
16
+ /** Told of each lazy View a drawing draws; given by the App's `onLazyView`. */
17
+ export declare const LazyContext: import("react").Context<((view: LazyView<AnyActor>) => void) | undefined>;
18
+ /** The lazy View made for `actor`, if one was: to load its code before it is drawn. */
19
+ export declare const lazyOf: (actor: AnyActor) => LazyView<AnyActor> | undefined;
20
+ /**
21
+ * A View of `actor` loaded when first drawn: `load` imports a module whose
22
+ * default export is the View, made with `View.make`. The Actor is named here
23
+ * so the View can be found by it, and preloaded, before its code is there.
24
+ */
25
+ export declare const lazy: <A extends AnyActor>(actor: A, load: () => Promise<{
26
+ readonly default: ViewOf<A>;
27
+ }>, options?: LazyOptions) => LazyView<A>;
28
+ /** Preload every lazy View made with one of these ids; an id no lazy View was made with is skipped. */
29
+ export declare const preload: (ids: Iterable<string>) => Promise<void>;
30
+ //# sourceMappingURL=lazy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy.d.ts","sourceRoot":"","sources":["../../src/react/lazy.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAoBzC,wEAAwE;AACxE,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG;IACpC,uGAAuG;IACvG,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,kEAAkE;IAClE,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,sGAAsG;IACtG,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,+EAA+E;AAC/E,eAAO,MAAM,WAAW,kCACd,QAAQ,CAAC,QAAQ,CAAC,KAAK,IAAI,cACzB,CAAC;AAKb,uFAAuF;AACvF,eAAO,MAAM,MAAM,UAAW,QAAQ,KAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,SAC/B,CAAC;AAQhC;;;;GAIG;AACH,eAAO,MAAM,IAAI,GAAI,CAAC,SAAS,QAAQ,SAC9B,CAAC,QACF,MAAM,OAAO,CAAC;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC,YAC3C,WAAW,KACnB,QAAQ,CAAC,CAAC,CAkCZ,CAAC;AAEF,uGAAuG;AACvG,eAAO,MAAM,OAAO,QAAe,QAAQ,CAAC,MAAM,CAAC,KAAG,OAAO,CAAC,IAAI,CAQjE,CAAC"}