sygnal 7.0.0 → 7.2.0-preview.0

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 (343) hide show
  1. package/CHANGELOG.md +632 -0
  2. package/dist/agent-ui.cjs.js +592 -0
  3. package/dist/agent-ui.cjs.js.map +1 -0
  4. package/dist/agent-ui.esm.js +582 -0
  5. package/dist/agent-ui.esm.js.map +1 -0
  6. package/dist/api.cjs.js +63 -0
  7. package/dist/api.cjs.js.map +1 -0
  8. package/dist/api.esm.js +2 -0
  9. package/dist/api.esm.js.map +1 -0
  10. package/dist/astro/index.cjs.js +968 -4
  11. package/dist/astro/index.cjs.js.map +1 -1
  12. package/dist/astro/index.mjs +968 -4
  13. package/dist/astro/index.mjs.map +1 -1
  14. package/dist/astro/server.cjs.js +73 -45
  15. package/dist/astro/server.cjs.js.map +1 -1
  16. package/dist/astro/server.mjs +73 -45
  17. package/dist/astro/server.mjs.map +1 -1
  18. package/dist/canvas.cjs.js +2919 -0
  19. package/dist/canvas.cjs.js.map +1 -0
  20. package/dist/canvas.esm.js +2866 -0
  21. package/dist/canvas.esm.js.map +1 -0
  22. package/dist/devtools.cjs.js +38 -6
  23. package/dist/devtools.cjs.js.map +1 -1
  24. package/dist/devtools.esm.js +38 -6
  25. package/dist/devtools.esm.js.map +1 -1
  26. package/dist/diagnostics.cjs.js +224 -20
  27. package/dist/diagnostics.cjs.js.map +1 -1
  28. package/dist/diagnostics.esm.js +224 -20
  29. package/dist/diagnostics.esm.js.map +1 -1
  30. package/dist/guide/accessibility.md +1 -1
  31. package/dist/guide/ai-compose.md +150 -0
  32. package/dist/guide/behaviors.md +2 -1
  33. package/dist/guide/collaborative-state.md +265 -0
  34. package/dist/guide/element-commands.md +36 -2
  35. package/dist/guide/forms-reference.md +5 -5
  36. package/dist/guide/forms.md +3 -2
  37. package/dist/guide/http.md +28 -1
  38. package/dist/guide/islands.md +240 -0
  39. package/dist/guide/recipes/code-editor.md +1 -1
  40. package/dist/guide/renderers/agent-ui.md +138 -0
  41. package/dist/guide/renderers/custom-targets.md +126 -0
  42. package/dist/guide/renderers/overview.md +120 -0
  43. package/dist/guide/renderers/react-native.md +128 -0
  44. package/dist/guide/renderers/terminal.md +306 -0
  45. package/dist/guide/renderers/three.md +332 -0
  46. package/dist/guide/renderers/write-once.md +137 -0
  47. package/dist/guide/resources.md +2 -0
  48. package/dist/guide/sandboxed-components.md +223 -0
  49. package/dist/guide/timers.md +32 -5
  50. package/dist/guide/typed-apis.md +442 -0
  51. package/dist/guide/ui/accordion.md +11 -0
  52. package/dist/guide/ui/combobox.md +112 -8
  53. package/dist/guide/ui/dialog.md +96 -20
  54. package/dist/guide/ui/menu.md +57 -5
  55. package/dist/guide/ui/overview.md +8 -4
  56. package/dist/guide/ui/popover.md +69 -9
  57. package/dist/guide/ui/select.md +54 -4
  58. package/dist/guide/ui/tabs.md +1 -1
  59. package/dist/guide/ui/tooltip.md +7 -5
  60. package/dist/guide/virtual-collections.md +3 -0
  61. package/dist/guide/widgets.md +90 -1
  62. package/dist/host.cjs.js +2181 -0
  63. package/dist/host.cjs.js.map +1 -0
  64. package/dist/host.esm.js +2146 -0
  65. package/dist/host.esm.js.map +1 -0
  66. package/dist/index.cjs.js +3948 -586
  67. package/dist/index.cjs.js.map +1 -1
  68. package/dist/index.d.ts +584 -44
  69. package/dist/index.esm.js +3918 -586
  70. package/dist/index.esm.js.map +1 -1
  71. package/dist/primitives.cjs.js +143 -0
  72. package/dist/primitives.cjs.js.map +1 -0
  73. package/dist/primitives.esm.js +133 -0
  74. package/dist/primitives.esm.js.map +1 -0
  75. package/dist/react-native.cjs.js +248 -0
  76. package/dist/react-native.cjs.js.map +1 -0
  77. package/dist/react-native.esm.js +244 -0
  78. package/dist/react-native.esm.js.map +1 -0
  79. package/dist/replay.cjs.js +39 -0
  80. package/dist/replay.cjs.js.map +1 -0
  81. package/dist/replay.esm.js +2 -0
  82. package/dist/replay.esm.js.map +1 -0
  83. package/dist/sandbox-build.cjs.js +140 -0
  84. package/dist/sandbox-build.cjs.js.map +1 -0
  85. package/dist/sandbox-build.esm.js +137 -0
  86. package/dist/sandbox-build.esm.js.map +1 -0
  87. package/dist/sandbox-kernel.esm.js +212 -0
  88. package/dist/sandbox-kernel.esm.js.map +1 -0
  89. package/dist/sandbox-worker.cjs.js +857 -0
  90. package/dist/sandbox-worker.cjs.js.map +1 -0
  91. package/dist/sandbox-worker.esm.js +854 -0
  92. package/dist/sandbox-worker.esm.js.map +1 -0
  93. package/dist/sandbox.cjs.js +1425 -0
  94. package/dist/sandbox.cjs.js.map +1 -0
  95. package/dist/sandbox.esm.js +1406 -0
  96. package/dist/sandbox.esm.js.map +1 -0
  97. package/dist/sygnal.min.js +1 -1
  98. package/dist/sygnal.min.js.map +1 -1
  99. package/dist/terminal.cjs.js +1314 -0
  100. package/dist/terminal.cjs.js.map +1 -0
  101. package/dist/terminal.esm.js +1304 -0
  102. package/dist/terminal.esm.js.map +1 -0
  103. package/dist/three.cjs.js +1111 -0
  104. package/dist/three.cjs.js.map +1 -0
  105. package/dist/three.esm.js +1079 -0
  106. package/dist/three.esm.js.map +1 -0
  107. package/dist/ui-combobox.cjs.js +256 -18
  108. package/dist/ui-combobox.cjs.js.map +1 -1
  109. package/dist/ui-combobox.esm.js +256 -18
  110. package/dist/ui-combobox.esm.js.map +1 -1
  111. package/dist/ui-menu.cjs.js +150 -16
  112. package/dist/ui-menu.cjs.js.map +1 -1
  113. package/dist/ui-menu.esm.js +151 -17
  114. package/dist/ui-menu.esm.js.map +1 -1
  115. package/dist/ui-select.cjs.js +95 -9
  116. package/dist/ui-select.cjs.js.map +1 -1
  117. package/dist/ui-select.esm.js +95 -9
  118. package/dist/ui-select.esm.js.map +1 -1
  119. package/dist/ui.cjs.js +679 -173
  120. package/dist/ui.cjs.js.map +1 -1
  121. package/dist/ui.esm.js +679 -175
  122. package/dist/ui.esm.js.map +1 -1
  123. package/dist/vike/config/package.json +1 -1
  124. package/dist/vite/plugin.cjs.js +968 -4
  125. package/dist/vite/plugin.cjs.js.map +1 -1
  126. package/dist/vite/plugin.mjs +968 -4
  127. package/dist/vite/plugin.mjs.map +1 -1
  128. package/dist/worker-page.cjs.js +669 -0
  129. package/dist/worker-page.cjs.js.map +1 -0
  130. package/dist/worker-page.esm.js +665 -0
  131. package/dist/worker-page.esm.js.map +1 -0
  132. package/dist/worker.cjs.js +377 -0
  133. package/dist/worker.cjs.js.map +1 -0
  134. package/dist/worker.esm.js +374 -0
  135. package/dist/worker.esm.js.map +1 -0
  136. package/dist/yjs-testing.cjs.js +98 -0
  137. package/dist/yjs-testing.cjs.js.map +1 -0
  138. package/dist/yjs-testing.esm.js +77 -0
  139. package/dist/yjs-testing.esm.js.map +1 -0
  140. package/dist/yjs.cjs.js +923 -0
  141. package/dist/yjs.cjs.js.map +1 -0
  142. package/dist/yjs.esm.js +900 -0
  143. package/dist/yjs.esm.js.map +1 -0
  144. package/dist/zag.cjs.js +11 -1
  145. package/dist/zag.cjs.js.map +1 -1
  146. package/dist/zag.esm.js +11 -1
  147. package/dist/zag.esm.js.map +1 -1
  148. package/llms/ai.md +65 -0
  149. package/llms/collaboration.md +5 -0
  150. package/llms/graphics.md +10 -0
  151. package/llms/renderers.md +7 -0
  152. package/llms/replay.md +5 -0
  153. package/llms/sandbox.md +5 -0
  154. package/llms/typed-apis.md +5 -0
  155. package/llms/ui.md +10 -0
  156. package/llms-full.txt +444 -0
  157. package/llms.txt +37 -35
  158. package/package.json +139 -3
  159. package/src/agent-ui/mcp.ts +152 -0
  160. package/src/agent-ui/snapshot.ts +212 -0
  161. package/src/agent-ui.d.ts +106 -0
  162. package/src/agent-ui.ts +249 -0
  163. package/src/ai.d.ts +95 -2
  164. package/src/api.d.ts +303 -0
  165. package/src/api.ts +9 -0
  166. package/src/canvas/Canvas.ts +585 -0
  167. package/src/canvas/backend.ts +627 -0
  168. package/src/canvas/geometry.ts +187 -0
  169. package/src/canvas/gesture.ts +23 -0
  170. package/src/canvas/image.ts +197 -0
  171. package/src/canvas/kit.ts +130 -0
  172. package/src/canvas/paint.ts +260 -0
  173. package/src/canvas/pointer.ts +25 -0
  174. package/src/canvas/snap.ts +68 -0
  175. package/src/canvas/svg.ts +99 -0
  176. package/src/canvas.d.ts +373 -0
  177. package/src/canvas.ts +46 -0
  178. package/src/core/actions.ts +14 -3
  179. package/src/core/cell.ts +12 -4
  180. package/src/core/debug.ts +8 -3
  181. package/src/core/hooks.ts +16 -2
  182. package/src/core/hosts/collection.ts +1 -1
  183. package/src/core/hosts/switchable.ts +26 -8
  184. package/src/core/instance.ts +98 -45
  185. package/src/core/markers/portal.ts +4 -0
  186. package/src/core/registry.ts +4 -0
  187. package/src/core/runtime.ts +34 -25
  188. package/src/core/statics.ts +5 -5
  189. package/src/cycle/dom/EventDelegator.ts +37 -33
  190. package/src/cycle/dom/MainDOMSource.ts +16 -14
  191. package/src/cycle/dom/enrichEventStream.ts +13 -9
  192. package/src/cycle/dom/fromEvent.ts +6 -10
  193. package/src/cycle/dom/index.ts +11 -7
  194. package/src/cycle/dom/isolate.ts +13 -6
  195. package/src/cycle/dom/makeDOMDriver.ts +6 -2
  196. package/src/cycle/dom/utils.ts +9 -2
  197. package/src/cycle/run/adapt.ts +4 -12
  198. package/src/devtools.d.ts +9 -0
  199. package/src/devtools.ts +2 -1
  200. package/src/extra/ai/compose/catalog.ts +2 -1
  201. package/src/extra/ai/compose/decisions.ts +1 -2
  202. package/src/extra/ai/compose/generated.ts +234 -0
  203. package/src/extra/ai/compose/index.ts +6 -2
  204. package/src/extra/ai/compose/layout.ts +3 -1
  205. package/src/extra/ai/compose/llm.ts +7 -3
  206. package/src/extra/ai/compose/types.ts +2 -2
  207. package/src/extra/ai/transports/shared.ts +3 -1
  208. package/src/extra/api/contract.ts +485 -0
  209. package/src/extra/api/index.ts +26 -0
  210. package/src/extra/behaviors.ts +3 -3
  211. package/src/extra/controlled.ts +44 -0
  212. package/src/extra/copyAsTest.ts +22 -0
  213. package/src/extra/devtools.ts +5 -3
  214. package/src/extra/devtoolsActions.ts +8 -0
  215. package/src/extra/devtoolsNext.ts +1 -1
  216. package/src/extra/diagnostics/checks/actionLog.ts +8 -3
  217. package/src/extra/diagnostics/checks/behaviors.ts +25 -0
  218. package/src/extra/diagnostics/checks/elementCommands.ts +17 -5
  219. package/src/extra/diagnostics/checks/index.ts +1 -1
  220. package/src/extra/diagnostics/checks/next.ts +78 -4
  221. package/src/extra/diagnostics/checks/props.ts +6 -1
  222. package/src/extra/diagnostics/checks/replies.ts +11 -7
  223. package/src/extra/diagnostics/checks/timers.ts +8 -2
  224. package/src/extra/diagnostics/checks/virtual.ts +11 -0
  225. package/src/extra/diagnostics/codes.ts +51 -1
  226. package/src/extra/elementCommands.ts +20 -4
  227. package/src/extra/fetchDriver.ts +4 -1
  228. package/src/extra/fetchEventSource.ts +6 -2
  229. package/src/extra/field.ts +29 -0
  230. package/src/extra/form.ts +42 -16
  231. package/src/extra/frame.ts +54 -0
  232. package/src/extra/keymap.ts +41 -0
  233. package/src/extra/pager.ts +2 -0
  234. package/src/extra/persist.ts +2 -1
  235. package/src/extra/pointer.ts +262 -0
  236. package/src/extra/processForm.ts +34 -1
  237. package/src/extra/replay/address.ts +49 -0
  238. package/src/extra/replay/entropy.ts +99 -0
  239. package/src/extra/replay/format.ts +263 -0
  240. package/src/extra/replay/index.ts +8 -0
  241. package/src/extra/replay/inert.ts +22 -0
  242. package/src/extra/replay/recorder.ts +247 -0
  243. package/src/extra/replay/replayer.ts +234 -0
  244. package/src/extra/replay/report.ts +126 -0
  245. package/src/extra/replay/schema.ts +52 -0
  246. package/src/extra/replay/version.ts +3 -0
  247. package/src/extra/router.ts +7 -3
  248. package/src/extra/selection.ts +2 -0
  249. package/src/extra/socketDriver.ts +51 -6
  250. package/src/extra/ssr.ts +128 -42
  251. package/src/extra/testing.ts +295 -39
  252. package/src/extra/timers.ts +96 -7
  253. package/src/extra/undo.ts +2 -0
  254. package/src/extra/virtual.ts +25 -2
  255. package/src/extra/widget.ts +106 -17
  256. package/src/host/actions.ts +383 -0
  257. package/src/host/delegation.ts +101 -0
  258. package/src/host/dom.ts +553 -0
  259. package/src/host/driver.ts +185 -0
  260. package/src/host/selector.ts +119 -0
  261. package/src/host/style.ts +51 -0
  262. package/src/host.d.ts +298 -0
  263. package/src/host.ts +46 -0
  264. package/src/index.d.ts +302 -38
  265. package/src/index.ts +15 -3
  266. package/src/pragma/blocks.ts +211 -0
  267. package/src/pragma/index.ts +44 -10
  268. package/src/primitives.d.ts +54 -0
  269. package/src/primitives.ts +136 -0
  270. package/src/react-native.d.ts +57 -0
  271. package/src/react-native.ts +285 -0
  272. package/src/react-peers.d.ts +21 -3
  273. package/src/replay.d.ts +185 -0
  274. package/src/replay.ts +6 -0
  275. package/src/sandbox/build.ts +119 -0
  276. package/src/sandbox/create.ts +187 -0
  277. package/src/sandbox/engine.ts +267 -0
  278. package/src/sandbox/generated.ts +195 -0
  279. package/src/sandbox/kernel.ts +180 -0
  280. package/src/sandbox/mirror.ts +269 -0
  281. package/src/sandbox/protocol.ts +128 -0
  282. package/src/sandbox/quickjs.ts +74 -0
  283. package/src/sandbox/ssr.ts +41 -0
  284. package/src/sandbox/widget.ts +60 -0
  285. package/src/sandbox/worker-entry.ts +59 -0
  286. package/src/sandbox/worker.ts +137 -0
  287. package/src/sandbox-build.d.ts +12 -0
  288. package/src/sandbox-build.ts +6 -0
  289. package/src/sandbox.d.ts +299 -0
  290. package/src/sandbox.ts +25 -0
  291. package/src/shared.ts +12 -0
  292. package/src/terminal/paint.ts +712 -0
  293. package/src/terminal.d.ts +167 -0
  294. package/src/terminal.ts +530 -0
  295. package/src/three/assets.ts +170 -0
  296. package/src/three/host.ts +36 -0
  297. package/src/three/scene.ts +217 -0
  298. package/src/three/target.ts +252 -0
  299. package/src/three.d.ts +221 -0
  300. package/src/three.ts +30 -0
  301. package/src/ui/accordion.ts +21 -7
  302. package/src/ui/dialog.ts +192 -90
  303. package/src/ui/disclosure.ts +2 -0
  304. package/src/ui/dom.ts +30 -0
  305. package/src/ui/layers.ts +132 -0
  306. package/src/ui/popover.ts +120 -21
  307. package/src/ui/shared.ts +98 -12
  308. package/src/ui/stack.ts +18 -0
  309. package/src/ui/tabs.ts +2 -0
  310. package/src/ui/toaster.ts +16 -15
  311. package/src/ui/tooltip.ts +24 -8
  312. package/src/ui/tooltips.ts +26 -14
  313. package/src/ui/zag/combobox.ts +180 -13
  314. package/src/ui/zag/menu.ts +97 -17
  315. package/src/ui/zag/select.ts +15 -4
  316. package/src/ui/zag/shared.ts +60 -8
  317. package/src/ui-combobox.d.ts +16 -1
  318. package/src/ui-menu.d.ts +22 -7
  319. package/src/ui-zag-types.d.ts +13 -5
  320. package/src/ui.d.ts +62 -7
  321. package/src/ui.ts +2 -2
  322. package/src/vite/api.ts +186 -0
  323. package/src/vite/compile.ts +280 -0
  324. package/src/vite/plugin.d.ts +34 -0
  325. package/src/vite/plugin.ts +78 -4
  326. package/src/vite/sandbox.ts +16 -0
  327. package/src/worker/page.ts +396 -0
  328. package/src/worker/protocol.ts +48 -0
  329. package/src/worker/target.ts +282 -0
  330. package/src/worker-page.d.ts +42 -0
  331. package/src/worker-page.ts +12 -0
  332. package/src/worker.d.ts +65 -0
  333. package/src/worker.ts +16 -0
  334. package/src/yjs/driver.ts +253 -0
  335. package/src/yjs/mapping.ts +389 -0
  336. package/src/yjs/shared.ts +138 -0
  337. package/src/yjs/testing.ts +91 -0
  338. package/src/yjs-testing.d.ts +34 -0
  339. package/src/yjs-testing.ts +7 -0
  340. package/src/yjs.d.ts +109 -0
  341. package/src/yjs.ts +18 -0
  342. package/src/zag.d.ts +2 -0
  343. package/src/zag.ts +8 -1
@@ -0,0 +1,374 @@
1
+ import { xs } from 'sygnal';
2
+ import { HostDocument, nodeId, makeHostDriver, HostEvent } from 'sygnal/host';
3
+
4
+ /** a scope's key: its scope names (src/cycle/dom/isolate's {type, scope} list) */
5
+ const keys = new WeakMap();
6
+ const scopeKey = (ns) => {
7
+ if (!ns || !ns.length)
8
+ return '';
9
+ let k = keys.get(ns);
10
+ if (k === undefined)
11
+ keys.set(ns, k = ns.map(s => s.scope).join('\u0001'));
12
+ return k;
13
+ };
14
+
15
+ /**
16
+ * sygnal/worker's wire format (research: dev-plans/research/web-worker §2), shared by the worker
17
+ * target (src/worker/target.ts) and the page bridge (src/worker/page.ts). Messages are plain,
18
+ * structured-cloneable objects; node ids are sygnal/host nodeId()s (the host body is the mount).
19
+ *
20
+ * worker → page
21
+ * {root} the host body's id (the page maps it to its mount element)
22
+ * {ops, ack, listen?, prevent?} one per flush: flat op tuples; the last page event handled
23
+ * before they were made; the event types intents listen for and
24
+ * the intents' declared preventDefaults, each when it changed
25
+ * {drv, sink} a value for the page driver `drv`'s sink
26
+ * {sub, drv, method?, args} / {unsub} open / close a page driver source subscription
27
+ * page → worker
28
+ * {ev: [PageEvent]} events, in dispatch order (a field value the page rebased
29
+ * crosses as an input event, inputType 'insertReplacementText')
30
+ * {ping: true} answer with a flush, even an empty one (a held submit waits for its ack)
31
+ * {emit, v} a value from a page driver source subscription
32
+ */
33
+ const OP = {
34
+ EL: 0, // [0, id, tag]
35
+ TEXT: 1, // [1, id, data]
36
+ KIDS: 2, // [2, parentId, childIds]
37
+ ATTR: 3, // [3, id, name, value | null]
38
+ PROP: 4, // [4, id, 'value' | 'checked' | 'selected', value]
39
+ DATA: 5, // [5, id, data]
40
+ STYLE: 6, // [6, id, key, value | null]
41
+ FOCUS: 7, // [7, id | 0]
42
+ FREE: 8, // [8, ids]
43
+ SCOPE: 9, // [9, id, scopeKey | null]: an isolation scope's root element (declared preventDefaults are scoped)
44
+ };
45
+ /** an op's tuple width, by opcode */
46
+ const WIDTH = [3, 3, 3, 4, 4, 3, 4, 2, 2, 3];
47
+
48
+ /**
49
+ * sygnal/worker: the worker half (research: dev-plans/research/web-worker). The app renders into a
50
+ * sygnal/host tree inside a Web Worker; this module turns the tree's changes into one message of
51
+ * patch ops per flush (src/worker/protocol.ts), takes the page's events back, and stands in for
52
+ * drivers that have to live on the page.
53
+ *
54
+ * const target = createWorkerTarget(self)
55
+ * run(App, { DOM: target.driver, STORE: target.remote('STORE', ['get']), ROUTER: target.remote('ROUTER') })
56
+ *
57
+ * The diff reads the host tree's change tracking: every mutation moves `revision` on the node and
58
+ * its ancestors (`attrRevision` for its own attributes, classes, style), and the walk skips every
59
+ * subtree whose revision didn't move.
60
+ */
61
+ function createWorkerTarget(port, options = {}) {
62
+ const FRAME = options.frame ?? 16;
63
+ const doc = new HostDocument();
64
+ const recs = new WeakMap();
65
+ const byId = new Map();
66
+ const bodyId = nodeId(doc.body);
67
+ byId.set(bodyId, doc.body);
68
+ recs.set(doc.body, { rev: -1, arev: -1, attrs: new Map(), style: {}, kids: [] });
69
+ port.postMessage({ root: bodyId });
70
+ let ops = [];
71
+ let ack = 0;
72
+ let pong = false;
73
+ let lastActive = null;
74
+ let lastListen = '';
75
+ let lastPrevent = '';
76
+ let warned = false;
77
+ const detached = new Set();
78
+ const stats = { messages: 0, ops: 0, events: 0 };
79
+ // ---- tree → ops ---------------------------------------------------------------------------
80
+ const scopeOf = (el) => el.__isolate ? scopeKey(el.__isolate) : null;
81
+ /** a node the page hasn't seen: create it, its state and its subtree (pre-order) */
82
+ function create(n) {
83
+ const id = nodeId(n);
84
+ byId.set(id, n);
85
+ detached.delete(n);
86
+ if (n.nodeType === 3) {
87
+ const data = n.data;
88
+ ops.push(OP.TEXT, id, data);
89
+ recs.set(n, { rev: n.revision, arev: n.attrRevision, data });
90
+ return id;
91
+ }
92
+ const el = n;
93
+ ops.push(OP.EL, id, el.localName);
94
+ for (const [k, v] of el.attributes)
95
+ ops.push(OP.ATTR, id, k, v);
96
+ const style = { ...el.style };
97
+ for (const k in style)
98
+ ops.push(OP.STYLE, id, k, style[k]);
99
+ if (el.value !== '')
100
+ ops.push(OP.PROP, id, 'value', el.value);
101
+ if (el.checked)
102
+ ops.push(OP.PROP, id, 'checked', true);
103
+ if (el.selected)
104
+ ops.push(OP.PROP, id, 'selected', true);
105
+ const scope = scopeOf(el);
106
+ if (scope !== null)
107
+ ops.push(OP.SCOPE, id, scope);
108
+ const kids = el.childNodes.map(create);
109
+ if (kids.length)
110
+ ops.push(OP.KIDS, id, kids);
111
+ recs.set(el, { rev: el.revision, arev: el.attrRevision, attrs: new Map(el.attributes), style, value: el.value, checked: el.checked, selected: el.selected, kids, scope });
112
+ return id;
113
+ }
114
+ /** a node the page has: send what moved since (only subtrees whose revision moved) */
115
+ function update(n) {
116
+ const r = recs.get(n);
117
+ if (r.rev === n.revision)
118
+ return;
119
+ r.rev = n.revision;
120
+ const id = nodeId(n);
121
+ if (n.nodeType === 3) {
122
+ const data = n.data;
123
+ if (data !== r.data) {
124
+ ops.push(OP.DATA, id, data);
125
+ r.data = data;
126
+ }
127
+ return;
128
+ }
129
+ const el = n;
130
+ if (r.arev !== el.attrRevision) {
131
+ r.arev = el.attrRevision;
132
+ const old = r.attrs;
133
+ for (const [k, v] of el.attributes)
134
+ if (old.get(k) !== v)
135
+ ops.push(OP.ATTR, id, k, v);
136
+ for (const k of old.keys())
137
+ if (!el.attributes.has(k))
138
+ ops.push(OP.ATTR, id, k, null);
139
+ r.attrs = new Map(el.attributes);
140
+ const s = { ...el.style }, os = r.style;
141
+ for (const k in s)
142
+ if (os[k] !== s[k])
143
+ ops.push(OP.STYLE, id, k, s[k]);
144
+ for (const k in os)
145
+ if (!(k in s))
146
+ ops.push(OP.STYLE, id, k, null);
147
+ r.style = s;
148
+ }
149
+ if (el.value !== r.value) {
150
+ ops.push(OP.PROP, id, 'value', el.value);
151
+ r.value = el.value;
152
+ }
153
+ if (el.checked !== r.checked) {
154
+ ops.push(OP.PROP, id, 'checked', el.checked);
155
+ r.checked = el.checked;
156
+ }
157
+ if (el.selected !== r.selected) {
158
+ ops.push(OP.PROP, id, 'selected', el.selected);
159
+ r.selected = el.selected;
160
+ }
161
+ const scope = scopeOf(el);
162
+ if (scope !== (r.scope ?? null)) {
163
+ ops.push(OP.SCOPE, id, scope);
164
+ r.scope = scope;
165
+ }
166
+ const old = r.kids;
167
+ const kids = new Array(el.childNodes.length);
168
+ let same = old.length === kids.length;
169
+ for (let i = 0; i < kids.length; i++) {
170
+ const c = el.childNodes[i];
171
+ // a known node (also one that moved here from a parent visited earlier this flush)
172
+ if (recs.has(c)) {
173
+ detached.delete(c);
174
+ kids[i] = nodeId(c);
175
+ update(c);
176
+ }
177
+ else
178
+ kids[i] = create(c);
179
+ if (same && kids[i] !== old[i])
180
+ same = false;
181
+ }
182
+ if (!same) {
183
+ ops.push(OP.KIDS, id, kids);
184
+ const now = new Set(kids);
185
+ for (const k of old)
186
+ if (!now.has(k))
187
+ detached.add(byId.get(k));
188
+ r.kids = kids;
189
+ }
190
+ }
191
+ /** the intents' declared preventDefaults (a predicate can't cross: the page never sees it) */
192
+ function prevents() {
193
+ const out = [];
194
+ for (const d of doc.delegates) {
195
+ const pd = d.preventDefault;
196
+ if (!pd)
197
+ continue;
198
+ if (typeof pd == 'function') {
199
+ if (!warned) {
200
+ warned = true;
201
+ console.warn(`sygnal/worker: events('${d.type}', { preventDefault }) with a function runs in the worker, after the page's default action; use true or a pattern of event fields ({ key: 'Tab' })`);
202
+ }
203
+ continue;
204
+ }
205
+ out.push([d.type, d.selector || '', !!d.global, d.scope || '', pd === true ? true : pd]);
206
+ }
207
+ return out;
208
+ }
209
+ function flush() {
210
+ scheduled = false;
211
+ update(doc.body);
212
+ // nodes that left a parent and didn't come back anywhere this flush: the page drops them
213
+ if (detached.size) {
214
+ const free = [];
215
+ const drop = (n) => {
216
+ byId.delete(nodeId(n));
217
+ recs.delete(n);
218
+ if (n.nodeType === 1)
219
+ for (const c of n.childNodes)
220
+ drop(c);
221
+ };
222
+ for (const n of detached)
223
+ if (!n.isConnected) {
224
+ free.push(nodeId(n));
225
+ drop(n);
226
+ }
227
+ detached.clear();
228
+ if (free.length)
229
+ ops.push(OP.FREE, free);
230
+ }
231
+ if (doc.activeElement !== lastActive) {
232
+ lastActive = doc.activeElement;
233
+ ops.push(OP.FOCUS, lastActive ? nodeId(lastActive) : 0);
234
+ }
235
+ const msg = { ops, ack };
236
+ const types = [...new Set([...doc.delegates].map((d) => d.type))].sort().join(' ');
237
+ if (types !== lastListen) {
238
+ lastListen = types;
239
+ msg.listen = types.split(' ').filter(Boolean);
240
+ }
241
+ const pd = prevents(), pdKey = JSON.stringify(pd);
242
+ if (pdKey !== lastPrevent) {
243
+ lastPrevent = pdKey;
244
+ msg.prevent = pd;
245
+ }
246
+ if (!ops.length && !msg.listen && !msg.prevent && !pong)
247
+ return;
248
+ pong = false;
249
+ for (let i = 0; i < ops.length; i += WIDTH[ops[i]])
250
+ stats.ops++;
251
+ stats.messages++;
252
+ ops = [];
253
+ lastPost = performance.now();
254
+ port.postMessage(msg);
255
+ }
256
+ // one message per flush: Sygnal's flush (and an EFFECT's writes, autoFocus) run in microtasks
257
+ // and frames; a macrotask after the change collects all of them. Busy (a message went out less
258
+ // than a frame ago), wait for the rest of that frame: 10k <Transition> timers firing one by one
259
+ // made 1,160 messages of 3 ops each (research §2.1)
260
+ let scheduled = false;
261
+ let lastPost = -Infinity;
262
+ const mc = new MessageChannel();
263
+ mc.port1.onmessage = () => { if (scheduled)
264
+ flush(); };
265
+ const schedule = () => {
266
+ if (scheduled)
267
+ return;
268
+ scheduled = true;
269
+ const wait = lastPost + FRAME - performance.now();
270
+ wait > 0 ? setTimeout(() => { if (scheduled)
271
+ flush(); }, wait) : mc.port2.postMessage(0);
272
+ };
273
+ doc.onChange = schedule;
274
+ // ---- page events → host events ------------------------------------------------------------
275
+ function receiveEvent(e) {
276
+ // changes the app made before this event go out first, stamped with the previous ack: the
277
+ // page's form state below would otherwise overwrite a write not yet sent (an EFFECT's clear
278
+ // of a field the user is already typing into again), and the page could never rebase it
279
+ if (scheduled)
280
+ flush();
281
+ ack = e.seq;
282
+ stats.events++;
283
+ const el = byId.get(e.id);
284
+ if (!el || el.nodeType !== 1)
285
+ return;
286
+ const r = recs.get(el);
287
+ // the page's state first, recorded as already sent: no echo back to the page
288
+ const st = e.state;
289
+ if (st) {
290
+ if ('value' in st) {
291
+ el.value = st.value;
292
+ if (r)
293
+ r.value = el.value;
294
+ }
295
+ if ('checked' in st) {
296
+ el.checked = !!st.checked;
297
+ if (r)
298
+ r.checked = el.checked;
299
+ }
300
+ // (the host tree has no layout: a scroll position is the page's, readable as on the DOM)
301
+ if ('scrollTop' in st)
302
+ Object.assign(el, { scrollTop: st.scrollTop, scrollLeft: st.scrollLeft });
303
+ }
304
+ // focus moved on the page: the host follows without its own focus/blur events (the page's
305
+ // come next, as events), and the page isn't told to focus what it focused
306
+ if (e.type === 'focus' || e.type === 'focusin') {
307
+ doc.activeElement = lastActive = el;
308
+ }
309
+ if ((e.type === 'blur' || e.type === 'focusout') && doc.activeElement === el) {
310
+ doc.activeElement = lastActive = null;
311
+ }
312
+ // the page already ran the default action (typed, toggled, submitted): a plain event, never
313
+ // activate() / pressKey(), which would do it again
314
+ const bubbles = !/^(focus|blur|mouseenter|mouseleave|pointerenter|pointerleave|scroll|load|error)$/.test(e.type);
315
+ el.dispatchEvent(new HostEvent(e.type, { ...e.init, bubbles }));
316
+ }
317
+ // ---- drivers on the page ------------------------------------------------------------------
318
+ let nextSub = 0;
319
+ const subs = new Map();
320
+ /** a stream the page feeds: the page subscribes to `drv` (its source, or source[method](...args)) */
321
+ const pageStream = (drv, method, args = []) => {
322
+ let sid = 0;
323
+ return xs.create({
324
+ start(l) { sid = ++nextSub; subs.set(sid, l); port.postMessage({ sub: sid, drv, method, args }); },
325
+ stop() { subs.delete(sid); port.postMessage({ unsub: sid }); },
326
+ });
327
+ };
328
+ /**
329
+ * A driver that stays on the page (DOM-only browser APIs, localStorage, the router). The sink's
330
+ * values are posted to the page's driver of the same name (startBridge's `drivers`); the source
331
+ * is a stream (no `methods`) or an object whose methods return streams (`methods: ['get']`).
332
+ * Arguments and values must be structured-cloneable.
333
+ */
334
+ function remote(drv, methods) {
335
+ return (sink$) => {
336
+ sink$.addListener({ next: (v) => port.postMessage({ drv, sink: v }), error: () => { }, complete: () => { } });
337
+ if (!methods)
338
+ return pageStream(drv);
339
+ const src = {};
340
+ for (const m of methods)
341
+ src[m] = (...args) => pageStream(drv, m, args);
342
+ return src;
343
+ };
344
+ }
345
+ port.addEventListener('message', (m) => {
346
+ const d = m.data;
347
+ if (!d || typeof d != 'object')
348
+ return;
349
+ if (d.ev)
350
+ for (const e of d.ev)
351
+ receiveEvent(e);
352
+ else if (d.ping) {
353
+ pong = true;
354
+ schedule();
355
+ }
356
+ else if (d.emit)
357
+ subs.get(d.emit)?.next(d.v);
358
+ });
359
+ port.start?.();
360
+ return {
361
+ /** the DOM driver: pass it as `DOM` to run() */
362
+ driver: makeHostDriver(doc, options.driver),
363
+ remote,
364
+ /** send the pending changes now (they otherwise go out after the current task) */
365
+ flush: () => { if (scheduled)
366
+ flush(); },
367
+ /** the host tree the app renders into */
368
+ doc,
369
+ stats,
370
+ };
371
+ }
372
+
373
+ export { OP, createWorkerTarget };
374
+ //# sourceMappingURL=worker.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.esm.js","sources":["../src/host/delegation.ts","../src/worker/protocol.ts","../src/worker/target.ts"],"sourcesContent":["/**\n * sygnal/host: event delegation indexed by event type and isolation scope (research:\n * dev-plans/research/sandbox §6.2). The document keeps its delegates in a DelegateSet: a Set (the\n * public `doc.delegates`) that also indexes them by type. An event walks from its target up once;\n * at each element only the host driver's delegates of that element's scope are tested, so a\n * click in a 1,000-row Collection no longer runs every row's selector at every level.\n *\n * A delegate the host driver made carries `scope` (its isolation scope's key), `at(el, sr)` (does\n * it accept `el`, whose scope root is `sr`) and `deliver(e, el)`. Any other delegate (a target's\n * own) is called as before, with the event. Delivery keeps the order the delegates were added in.\n */\nimport type {HostDelegate, HostElement, HostEvent} from './dom'\n\n/** a scope's key: its scope names (src/cycle/dom/isolate's {type, scope} list) */\nconst keys = new WeakMap<object, string>()\nexport const scopeKey = (ns: {scope: string}[] | undefined): string => {\n if (!ns || !ns.length) return ''\n let k = keys.get(ns)\n if (k === undefined) keys.set(ns, k = ns.map(s => s.scope).join('\\u0001'))\n return k\n}\n/** the element carrying an element's innermost scope (null: the root component's own) */\nconst scopeRoot = (el: HostElement | null): HostElement | null => {\n for (let e = el; e; e = e.parentNode) if (e.__isolate) return e\n return null\n}\n\nexport type PreventDefault = boolean | Record<string, unknown> | ((e: any) => boolean)\n\n/** a pattern's fields all equal the event's (nested objects compared the same way), as src/cycle/dom/fromEvent's */\nexport function matchPattern(pattern: Record<string, unknown>, obj: any): boolean {\n for (const k in pattern) {\n const p = pattern[k], v = obj?.[k]\n if (p && typeof p == 'object' && v && typeof v == 'object' ? !matchPattern(p as Record<string, unknown>, v) : p !== v) return false\n }\n return true\n}\n/** the DOM driver's preventDefault option: true, a pattern of event fields, or a predicate */\nexport function preventIf(e: {preventDefault(): void}, pd: PreventDefault | undefined) {\n if (pd === true || (typeof pd == 'function' ? pd(e) : pd && typeof pd == 'object' && matchPattern(pd, e))) e.preventDefault()\n}\n\ntype Indexed = HostDelegate & {scope: string, at(el: HostElement, sr: HostElement | null): boolean, deliver(e: HostEvent, el: HostElement): void}\ntype Entry = {order: Map<HostDelegate, number>, plain: HostDelegate[], global: Indexed[], byScope: Map<string, Indexed[]>}\n\nconst indexed = (d: HostDelegate): d is Indexed => typeof (d as any).deliver == 'function' && typeof (d as any).at == 'function'\n\nexport class DelegateSet extends Set<HostDelegate> {\n private index?: Map<string, Entry | null>\n add(d: HostDelegate) {\n if (!this.has(d)) { super.add(d); this.index?.delete(d.type) }\n return this\n }\n delete(d: HostDelegate) {\n const r = super.delete(d)\n if (r) this.index?.delete(d.type)\n return r\n }\n clear() { super.clear(); this.index?.clear() }\n\n private entry(type: string): Entry | null {\n const index = this.index ??= new Map()\n let e = index.get(type)\n if (e !== undefined) return e\n e = null\n let i = 0\n for (const d of this) {\n if (d.type !== type) continue\n e ??= {order: new Map(), plain: [], global: [], byScope: new Map()}\n e.order.set(d, i++)\n if (!indexed(d)) e.plain.push(d)\n else if (d.global) e.global.push(d)\n else {\n const list = e.byScope.get(d.scope)\n list ? list.push(d) : e.byScope.set(d.scope, [d])\n }\n }\n index.set(type, e)\n return e\n }\n\n /** the delegates' part of a dispatch: one walk up from the target, then delivery in order */\n dispatch(ev: HostEvent) {\n const e = this.entry(ev.type)\n if (!e) return\n const hits: [number, HostDelegate, HostElement | null][] = []\n for (const d of e.plain) hits.push([e.order.get(d)!, d, null])\n const done = new Set<HostDelegate>()\n let sr = scopeRoot(ev.target)\n for (let el: HostElement | null = ev.target; el; el = el.parentNode) {\n for (const d of e.global) if (!done.has(d) && d.at(el, sr)) { done.add(d); hits.push([e.order.get(d)!, d, el]) }\n const scoped = e.byScope.get(sr ? scopeKey(sr.__isolate) : '')\n if (scoped) for (const d of scoped) if (!done.has(d) && d.at(el, sr)) { done.add(d); hits.push([e.order.get(d)!, d, el]) }\n if (!ev.bubbles) break\n // leaving a scope root: the next scope out\n if (el === sr) sr = scopeRoot(el.parentNode)\n }\n if (hits.length > 1) hits.sort((a, b) => a[0] - b[0])\n for (const [, d, el] of hits) el ? (d as Indexed).deliver(ev, el) : d(ev)\n }\n}\n","/**\n * sygnal/worker's wire format (research: dev-plans/research/web-worker §2), shared by the worker\n * target (src/worker/target.ts) and the page bridge (src/worker/page.ts). Messages are plain,\n * structured-cloneable objects; node ids are sygnal/host nodeId()s (the host body is the mount).\n *\n * worker → page\n * {root} the host body's id (the page maps it to its mount element)\n * {ops, ack, listen?, prevent?} one per flush: flat op tuples; the last page event handled\n * before they were made; the event types intents listen for and\n * the intents' declared preventDefaults, each when it changed\n * {drv, sink} a value for the page driver `drv`'s sink\n * {sub, drv, method?, args} / {unsub} open / close a page driver source subscription\n * page → worker\n * {ev: [PageEvent]} events, in dispatch order (a field value the page rebased\n * crosses as an input event, inputType 'insertReplacementText')\n * {ping: true} answer with a flush, even an empty one (a held submit waits for its ack)\n * {emit, v} a value from a page driver source subscription\n */\nexport const OP = {\n EL: 0, // [0, id, tag]\n TEXT: 1, // [1, id, data]\n KIDS: 2, // [2, parentId, childIds]\n ATTR: 3, // [3, id, name, value | null]\n PROP: 4, // [4, id, 'value' | 'checked' | 'selected', value]\n DATA: 5, // [5, id, data]\n STYLE: 6, // [6, id, key, value | null]\n FOCUS: 7, // [7, id | 0]\n FREE: 8, // [8, ids]\n SCOPE: 9, // [9, id, scopeKey | null]: an isolation scope's root element (declared preventDefaults are scoped)\n} as const\n\n/** an op's tuple width, by opcode */\nexport const WIDTH = [3, 3, 3, 4, 4, 3, 4, 2, 2, 3]\n\n/** a page event as it crosses: no DOM objects */\nexport interface PageEvent {\n seq: number\n type: string\n /** the nearest bridged element's id (the event's target, or its ancestor the page created) */\n id: number\n /** the event's plain fields (key, modifiers, pointer coordinates, deltas, inputType, data, …) */\n init: Record<string, any>\n /** the target's state on the page when it fired: a field's value / checked, a scroll position */\n state?: {value?: string, checked?: boolean, scrollTop?: number, scrollLeft?: number}\n}\n\n/** an intent's declared preventDefault: [type, selector, global, scope key, true | pattern] */\nexport type Prevent = [type: string, selector: string, global: boolean, scope: string, pattern: true | Record<string, unknown>]\n","/**\n * sygnal/worker: the worker half (research: dev-plans/research/web-worker). The app renders into a\n * sygnal/host tree inside a Web Worker; this module turns the tree's changes into one message of\n * patch ops per flush (src/worker/protocol.ts), takes the page's events back, and stands in for\n * drivers that have to live on the page.\n *\n * const target = createWorkerTarget(self)\n * run(App, { DOM: target.driver, STORE: target.remote('STORE', ['get']), ROUTER: target.remote('ROUTER') })\n *\n * The diff reads the host tree's change tracking: every mutation moves `revision` on the node and\n * its ancestors (`attrRevision` for its own attributes, classes, style), and the walk skips every\n * subtree whose revision didn't move.\n */\nimport {xs} from '../index'\nimport {HostDocument, HostEvent, makeHostDriver, nodeId} from '../host'\nimport type {HostElement, HostNode, HostDriverOptions} from '../host'\nimport {scopeKey} from '../host/delegation'\nimport {OP, WIDTH} from './protocol'\nimport type {PageEvent, Prevent} from './protocol'\n\ntype Rec = {rev: number, arev: number, attrs?: Map<string, string>, style?: Record<string, any>, value?: string, checked?: boolean, selected?: boolean, kids?: number[], data?: string, scope?: string | null}\n\n/** the worker side of a channel: `self` in a dedicated worker, or a MessagePort */\nexport interface WorkerPort {\n postMessage(message: any): void\n addEventListener(type: 'message', listener: (e: MessageEvent) => void): void\n start?(): void\n}\n\nexport interface WorkerTargetOptions {\n /** the shortest gap between two messages while the app is busy, in ms (default 16: a frame) */\n frame?: number\n /** the host driver's options (extra snabbdom modules, onPatch, onError) */\n driver?: HostDriverOptions\n}\n\nexport interface WorkerTargetStats {\n messages: number\n ops: number\n events: number\n}\n\nexport function createWorkerTarget(port: WorkerPort, options: WorkerTargetOptions = {}) {\n const FRAME = options.frame ?? 16\n const doc = new HostDocument()\n const recs = new WeakMap<HostNode, Rec>()\n const byId = new Map<number, HostNode>()\n const bodyId = nodeId(doc.body)\n byId.set(bodyId, doc.body)\n recs.set(doc.body, {rev: -1, arev: -1, attrs: new Map(), style: {}, kids: []})\n port.postMessage({root: bodyId})\n\n let ops: any[] = []\n let ack = 0\n let pong = false\n let lastActive: HostElement | null = null\n let lastListen = ''\n let lastPrevent = ''\n let warned = false\n const detached = new Set<HostNode>()\n const stats: WorkerTargetStats = {messages: 0, ops: 0, events: 0}\n\n // ---- tree → ops ---------------------------------------------------------------------------\n\n const scopeOf = (el: HostElement) => el.__isolate ? scopeKey(el.__isolate) : null\n\n /** a node the page hasn't seen: create it, its state and its subtree (pre-order) */\n function create(n: HostNode): number {\n const id = nodeId(n)\n byId.set(id, n)\n detached.delete(n)\n if (n.nodeType === 3) {\n const data = (n as any).data\n ops.push(OP.TEXT, id, data)\n recs.set(n, {rev: n.revision, arev: n.attrRevision, data})\n return id\n }\n const el = n as HostElement\n ops.push(OP.EL, id, el.localName)\n for (const [k, v] of el.attributes) ops.push(OP.ATTR, id, k, v)\n const style = {...el.style}\n for (const k in style) ops.push(OP.STYLE, id, k, style[k])\n if (el.value !== '') ops.push(OP.PROP, id, 'value', el.value)\n if (el.checked) ops.push(OP.PROP, id, 'checked', true)\n if (el.selected) ops.push(OP.PROP, id, 'selected', true)\n const scope = scopeOf(el)\n if (scope !== null) ops.push(OP.SCOPE, id, scope)\n const kids = el.childNodes.map(create)\n if (kids.length) ops.push(OP.KIDS, id, kids)\n recs.set(el, {rev: el.revision, arev: el.attrRevision, attrs: new Map(el.attributes), style, value: el.value, checked: el.checked, selected: el.selected, kids, scope})\n return id\n }\n\n /** a node the page has: send what moved since (only subtrees whose revision moved) */\n function update(n: HostNode) {\n const r = recs.get(n)!\n if (r.rev === n.revision) return\n r.rev = n.revision\n const id = nodeId(n)\n if (n.nodeType === 3) {\n const data = (n as any).data\n if (data !== r.data) { ops.push(OP.DATA, id, data); r.data = data }\n return\n }\n const el = n as HostElement\n if (r.arev !== el.attrRevision) {\n r.arev = el.attrRevision\n const old = r.attrs!\n for (const [k, v] of el.attributes) if (old.get(k) !== v) ops.push(OP.ATTR, id, k, v)\n for (const k of old.keys()) if (!el.attributes.has(k)) ops.push(OP.ATTR, id, k, null)\n r.attrs = new Map(el.attributes)\n const s = {...el.style}, os = r.style!\n for (const k in s) if (os[k] !== s[k]) ops.push(OP.STYLE, id, k, s[k])\n for (const k in os) if (!(k in s)) ops.push(OP.STYLE, id, k, null)\n r.style = s\n }\n if (el.value !== r.value) { ops.push(OP.PROP, id, 'value', el.value); r.value = el.value }\n if (el.checked !== r.checked) { ops.push(OP.PROP, id, 'checked', el.checked); r.checked = el.checked }\n if (el.selected !== r.selected) { ops.push(OP.PROP, id, 'selected', el.selected); r.selected = el.selected }\n const scope = scopeOf(el)\n if (scope !== (r.scope ?? null)) { ops.push(OP.SCOPE, id, scope); r.scope = scope }\n const old = r.kids!\n const kids: number[] = new Array(el.childNodes.length)\n let same = old.length === kids.length\n for (let i = 0; i < kids.length; i++) {\n const c = el.childNodes[i]\n // a known node (also one that moved here from a parent visited earlier this flush)\n if (recs.has(c)) { detached.delete(c); kids[i] = nodeId(c); update(c) }\n else kids[i] = create(c)\n if (same && kids[i] !== old[i]) same = false\n }\n if (!same) {\n ops.push(OP.KIDS, id, kids)\n const now = new Set(kids)\n for (const k of old) if (!now.has(k)) detached.add(byId.get(k)!)\n r.kids = kids\n }\n }\n\n /** the intents' declared preventDefaults (a predicate can't cross: the page never sees it) */\n function prevents(): Prevent[] {\n const out: Prevent[] = []\n for (const d of doc.delegates) {\n const pd = d.preventDefault\n if (!pd) continue\n if (typeof pd == 'function') {\n if (!warned) { warned = true; console.warn(`sygnal/worker: events('${d.type}', { preventDefault }) with a function runs in the worker, after the page's default action; use true or a pattern of event fields ({ key: 'Tab' })`) }\n continue\n }\n out.push([d.type, d.selector || '', !!d.global, d.scope || '', pd === true ? true : pd])\n }\n return out\n }\n\n function flush() {\n scheduled = false\n update(doc.body)\n // nodes that left a parent and didn't come back anywhere this flush: the page drops them\n if (detached.size) {\n const free: number[] = []\n const drop = (n: HostNode) => {\n byId.delete(nodeId(n)); recs.delete(n)\n if (n.nodeType === 1) for (const c of (n as HostElement).childNodes) drop(c)\n }\n for (const n of detached) if (!n.isConnected) { free.push(nodeId(n)); drop(n) }\n detached.clear()\n if (free.length) ops.push(OP.FREE, free)\n }\n if (doc.activeElement !== lastActive) {\n lastActive = doc.activeElement\n ops.push(OP.FOCUS, lastActive ? nodeId(lastActive) : 0)\n }\n const msg: any = {ops, ack}\n const types = [...new Set([...doc.delegates].map((d) => d.type))].sort().join(' ')\n if (types !== lastListen) { lastListen = types; msg.listen = types.split(' ').filter(Boolean) }\n const pd = prevents(), pdKey = JSON.stringify(pd)\n if (pdKey !== lastPrevent) { lastPrevent = pdKey; msg.prevent = pd }\n if (!ops.length && !msg.listen && !msg.prevent && !pong) return\n pong = false\n for (let i = 0; i < ops.length; i += WIDTH[ops[i]]) stats.ops++\n stats.messages++\n ops = []\n lastPost = performance.now()\n port.postMessage(msg)\n }\n\n // one message per flush: Sygnal's flush (and an EFFECT's writes, autoFocus) run in microtasks\n // and frames; a macrotask after the change collects all of them. Busy (a message went out less\n // than a frame ago), wait for the rest of that frame: 10k <Transition> timers firing one by one\n // made 1,160 messages of 3 ops each (research §2.1)\n let scheduled = false\n let lastPost = -Infinity\n const mc = new MessageChannel()\n mc.port1.onmessage = () => { if (scheduled) flush() }\n const schedule = () => {\n if (scheduled) return\n scheduled = true\n const wait = lastPost + FRAME - performance.now()\n wait > 0 ? setTimeout(() => { if (scheduled) flush() }, wait) : mc.port2.postMessage(0)\n }\n doc.onChange = schedule\n\n // ---- page events → host events ------------------------------------------------------------\n\n function receiveEvent(e: PageEvent) {\n // changes the app made before this event go out first, stamped with the previous ack: the\n // page's form state below would otherwise overwrite a write not yet sent (an EFFECT's clear\n // of a field the user is already typing into again), and the page could never rebase it\n if (scheduled) flush()\n ack = e.seq\n stats.events++\n const el = byId.get(e.id) as HostElement | undefined\n if (!el || el.nodeType !== 1) return\n const r = recs.get(el)\n // the page's state first, recorded as already sent: no echo back to the page\n const st = e.state\n if (st) {\n if ('value' in st) { el.value = st.value; if (r) r.value = el.value }\n if ('checked' in st) { el.checked = !!st.checked; if (r) r.checked = el.checked }\n // (the host tree has no layout: a scroll position is the page's, readable as on the DOM)\n if ('scrollTop' in st) Object.assign(el, {scrollTop: st.scrollTop, scrollLeft: st.scrollLeft})\n }\n // focus moved on the page: the host follows without its own focus/blur events (the page's\n // come next, as events), and the page isn't told to focus what it focused\n if (e.type === 'focus' || e.type === 'focusin') { doc.activeElement = lastActive = el }\n if ((e.type === 'blur' || e.type === 'focusout') && doc.activeElement === el) { doc.activeElement = lastActive = null }\n // the page already ran the default action (typed, toggled, submitted): a plain event, never\n // activate() / pressKey(), which would do it again\n const bubbles = !/^(focus|blur|mouseenter|mouseleave|pointerenter|pointerleave|scroll|load|error)$/.test(e.type)\n el.dispatchEvent(new HostEvent(e.type, {...e.init, bubbles}))\n }\n\n // ---- drivers on the page ------------------------------------------------------------------\n\n let nextSub = 0\n const subs = new Map<number, any>()\n\n /** a stream the page feeds: the page subscribes to `drv` (its source, or source[method](...args)) */\n const pageStream = (drv: string, method?: string, args: any[] = []) => {\n let sid = 0\n return xs.create({\n start(l: any) { sid = ++nextSub; subs.set(sid, l); port.postMessage({sub: sid, drv, method, args}) },\n stop() { subs.delete(sid); port.postMessage({unsub: sid}) },\n })\n }\n\n /**\n * A driver that stays on the page (DOM-only browser APIs, localStorage, the router). The sink's\n * values are posted to the page's driver of the same name (startBridge's `drivers`); the source\n * is a stream (no `methods`) or an object whose methods return streams (`methods: ['get']`).\n * Arguments and values must be structured-cloneable.\n */\n function remote(drv: string, methods?: string[]) {\n return (sink$: any) => {\n sink$.addListener({next: (v: any) => port.postMessage({drv, sink: v}), error: () => {}, complete: () => {}})\n if (!methods) return pageStream(drv)\n const src: any = {}\n for (const m of methods) src[m] = (...args: any[]) => pageStream(drv, m, args)\n return src\n }\n }\n\n port.addEventListener('message', (m: MessageEvent) => {\n const d = m.data\n if (!d || typeof d != 'object') return\n if (d.ev) for (const e of d.ev) receiveEvent(e)\n else if (d.ping) { pong = true; schedule() }\n else if (d.emit) subs.get(d.emit)?.next(d.v)\n })\n port.start?.()\n\n return {\n /** the DOM driver: pass it as `DOM` to run() */\n driver: makeHostDriver(doc, options.driver),\n remote,\n /** send the pending changes now (they otherwise go out after the current task) */\n flush: () => { if (scheduled) flush() },\n /** the host tree the app renders into */\n doc,\n stats,\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ 'use strict';
2
+
3
+ var Y = require('yjs');
4
+ var awareness = require('y-protocols/awareness');
5
+
6
+ function _interopNamespaceDefault(e) {
7
+ var n = Object.create(null);
8
+ if (e) {
9
+ Object.keys(e).forEach(function (k) {
10
+ if (k !== 'default') {
11
+ var d = Object.getOwnPropertyDescriptor(e, k);
12
+ Object.defineProperty(n, k, d.get ? d : {
13
+ enumerable: true,
14
+ get: function () { return e[k]; }
15
+ });
16
+ }
17
+ });
18
+ }
19
+ n.default = e;
20
+ return Object.freeze(n);
21
+ }
22
+
23
+ var Y__namespace = /*#__PURE__*/_interopNamespaceDefault(Y);
24
+
25
+ /*
26
+ * 'sygnal/yjs/testing': createNetwork(), an in-memory Yjs "network" for tests and demos (no server).
27
+ * Each peer's document updates and awareness updates are relayed to the other online peers as
28
+ * encoded bytes, as y-websocket would; offline peers miss them and catch up on reconnect with the
29
+ * same state-vector exchange y-websocket's sync steps 1 and 2 do. `bytes` / `messages` count what
30
+ * crossed it. A peer is a provider for makeYjsDriver({ doc, provider: peer }): its `awareness`,
31
+ * `synced` (a peer syncs with the online peers when it joins), `connect()` / `disconnect()`.
32
+ */
33
+ const NET = {};
34
+ const createNetwork = () => {
35
+ const all = [];
36
+ // sync steps 1 and 2 both ways, plus the current awareness states
37
+ const sync = (a, b) => {
38
+ for (const [x, y] of [[a, b], [b, a]]) {
39
+ const u = Y__namespace.encodeStateAsUpdate(x.doc, Y__namespace.encodeStateVector(y.doc));
40
+ net.bytes += u.length;
41
+ net.messages++;
42
+ Y__namespace.applyUpdate(y.doc, u, NET);
43
+ awareness.applyAwarenessUpdate(y.awareness, awareness.encodeAwarenessUpdate(x.awareness, [...x.awareness.getStates().keys()]), NET);
44
+ }
45
+ };
46
+ const net = {
47
+ bytes: 0, messages: 0,
48
+ get peers() { return all.map((p) => p.peer); },
49
+ join(doc = new Y__namespace.Doc()) {
50
+ const awareness$1 = new awareness.Awareness(doc);
51
+ const p = { doc, awareness: awareness$1, online: true };
52
+ const relay = () => all.filter((q) => q !== p && q.online);
53
+ const onUpdate = (u, origin) => {
54
+ if (origin === NET || !p.online)
55
+ return;
56
+ for (const q of relay()) {
57
+ net.bytes += u.length;
58
+ net.messages++;
59
+ Y__namespace.applyUpdate(q.doc, u, NET);
60
+ }
61
+ };
62
+ const onAwareness = ({ added, updated, removed }, origin) => {
63
+ if (origin === NET || !p.online)
64
+ return;
65
+ const u = awareness.encodeAwarenessUpdate(awareness$1, [...added, ...updated, ...removed]);
66
+ for (const q of relay())
67
+ awareness.applyAwarenessUpdate(q.awareness, u, NET);
68
+ };
69
+ doc.on('update', onUpdate);
70
+ awareness$1.on('update', onAwareness);
71
+ const on = () => { p.online = true; for (const q of relay())
72
+ sync(p, q); };
73
+ const off = () => { p.online = false; };
74
+ p.peer = {
75
+ doc, awareness: awareness$1, synced: true,
76
+ get isOnline() { return p.online; },
77
+ offline: off, online: on, connect: on, disconnect: off,
78
+ leave() {
79
+ awareness$1.setLocalState(null);
80
+ off();
81
+ doc.off('update', onUpdate);
82
+ awareness$1.off('update', onAwareness);
83
+ // its 'outdated' timer would keep a process alive
84
+ awareness$1.destroy();
85
+ all.splice(all.indexOf(p), 1);
86
+ },
87
+ };
88
+ all.push(p);
89
+ for (const q of relay())
90
+ sync(p, q);
91
+ return p.peer;
92
+ },
93
+ };
94
+ return net;
95
+ };
96
+
97
+ exports.createNetwork = createNetwork;
98
+ //# sourceMappingURL=yjs-testing.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yjs-testing.cjs.js","sources":["../src/yjs/testing.ts"],"sourcesContent":["/*\n * 'sygnal/yjs/testing': createNetwork(), an in-memory Yjs \"network\" for tests and demos (no server).\n * Each peer's document updates and awareness updates are relayed to the other online peers as\n * encoded bytes, as y-websocket would; offline peers miss them and catch up on reconnect with the\n * same state-vector exchange y-websocket's sync steps 1 and 2 do. `bytes` / `messages` count what\n * crossed it. A peer is a provider for makeYjsDriver({ doc, provider: peer }): its `awareness`,\n * `synced` (a peer syncs with the online peers when it joins), `connect()` / `disconnect()`.\n */\nimport * as Y from 'yjs'\nimport {Awareness, encodeAwarenessUpdate, applyAwarenessUpdate} from 'y-protocols/awareness'\n\nconst NET = {}\n\nexport interface Peer {\n doc: Y.Doc\n awareness: Awareness\n /** true: the peer has synced with the online peers (at join) */\n synced: boolean\n readonly isOnline: boolean\n /** stop relaying (local changes stay in the document) */\n offline(): void\n /** relay again, after a state-vector sync with every online peer */\n online(): void\n /** provider aliases of online() / offline(): makeYjsDriver's `{ connect: boolean }` command */\n connect(): void\n disconnect(): void\n /** leave the network for good (its awareness state is removed from the others) */\n leave(): void\n}\n\nexport interface Network {\n /** a new peer (a new Y.Doc unless one is given), synced with the online peers */\n join(doc?: Y.Doc): Peer\n readonly peers: Peer[]\n /** bytes and messages relayed so far (document updates and syncs) */\n bytes: number\n messages: number\n}\n\nexport const createNetwork = (): Network => {\n const all: {doc: Y.Doc; awareness: Awareness; online: boolean; peer: Peer}[] = []\n // sync steps 1 and 2 both ways, plus the current awareness states\n const sync = (a: any, b: any) => {\n for (const [x, y] of [[a, b], [b, a]]) {\n const u = Y.encodeStateAsUpdate(x.doc, Y.encodeStateVector(y.doc))\n net.bytes += u.length; net.messages++\n Y.applyUpdate(y.doc, u, NET)\n applyAwarenessUpdate(y.awareness, encodeAwarenessUpdate(x.awareness, [...x.awareness.getStates().keys()]), NET)\n }\n }\n const net: Network = {\n bytes: 0, messages: 0,\n get peers() { return all.map((p) => p.peer) },\n join(doc = new Y.Doc()) {\n const awareness = new Awareness(doc)\n const p: any = {doc, awareness, online: true}\n const relay = () => all.filter((q) => q !== p && q.online)\n const onUpdate = (u: Uint8Array, origin: any) => {\n if (origin === NET || !p.online) return\n for (const q of relay()) { net.bytes += u.length; net.messages++; Y.applyUpdate(q.doc, u, NET) }\n }\n const onAwareness = ({added, updated, removed}: any, origin: any) => {\n if (origin === NET || !p.online) return\n const u = encodeAwarenessUpdate(awareness, [...added, ...updated, ...removed])\n for (const q of relay()) applyAwarenessUpdate(q.awareness, u, NET)\n }\n doc.on('update', onUpdate)\n awareness.on('update', onAwareness)\n const on = () => { p.online = true; for (const q of relay()) sync(p, q) }\n const off = () => { p.online = false }\n p.peer = {\n doc, awareness, synced: true,\n get isOnline() { return p.online },\n offline: off, online: on, connect: on, disconnect: off,\n leave() {\n awareness.setLocalState(null)\n off()\n doc.off('update', onUpdate)\n awareness.off('update', onAwareness)\n // its 'outdated' timer would keep a process alive\n awareness.destroy()\n all.splice(all.indexOf(p), 1)\n },\n } as Peer\n all.push(p)\n for (const q of relay()) sync(p, q)\n return p.peer\n },\n }\n return net\n}\n"],"names":["Y","applyAwarenessUpdate","encodeAwarenessUpdate","awareness","Awareness"],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import * as Y from 'yjs';
2
+ import { Awareness, applyAwarenessUpdate, encodeAwarenessUpdate } from 'y-protocols/awareness';
3
+
4
+ /*
5
+ * 'sygnal/yjs/testing': createNetwork(), an in-memory Yjs "network" for tests and demos (no server).
6
+ * Each peer's document updates and awareness updates are relayed to the other online peers as
7
+ * encoded bytes, as y-websocket would; offline peers miss them and catch up on reconnect with the
8
+ * same state-vector exchange y-websocket's sync steps 1 and 2 do. `bytes` / `messages` count what
9
+ * crossed it. A peer is a provider for makeYjsDriver({ doc, provider: peer }): its `awareness`,
10
+ * `synced` (a peer syncs with the online peers when it joins), `connect()` / `disconnect()`.
11
+ */
12
+ const NET = {};
13
+ const createNetwork = () => {
14
+ const all = [];
15
+ // sync steps 1 and 2 both ways, plus the current awareness states
16
+ const sync = (a, b) => {
17
+ for (const [x, y] of [[a, b], [b, a]]) {
18
+ const u = Y.encodeStateAsUpdate(x.doc, Y.encodeStateVector(y.doc));
19
+ net.bytes += u.length;
20
+ net.messages++;
21
+ Y.applyUpdate(y.doc, u, NET);
22
+ applyAwarenessUpdate(y.awareness, encodeAwarenessUpdate(x.awareness, [...x.awareness.getStates().keys()]), NET);
23
+ }
24
+ };
25
+ const net = {
26
+ bytes: 0, messages: 0,
27
+ get peers() { return all.map((p) => p.peer); },
28
+ join(doc = new Y.Doc()) {
29
+ const awareness = new Awareness(doc);
30
+ const p = { doc, awareness, online: true };
31
+ const relay = () => all.filter((q) => q !== p && q.online);
32
+ const onUpdate = (u, origin) => {
33
+ if (origin === NET || !p.online)
34
+ return;
35
+ for (const q of relay()) {
36
+ net.bytes += u.length;
37
+ net.messages++;
38
+ Y.applyUpdate(q.doc, u, NET);
39
+ }
40
+ };
41
+ const onAwareness = ({ added, updated, removed }, origin) => {
42
+ if (origin === NET || !p.online)
43
+ return;
44
+ const u = encodeAwarenessUpdate(awareness, [...added, ...updated, ...removed]);
45
+ for (const q of relay())
46
+ applyAwarenessUpdate(q.awareness, u, NET);
47
+ };
48
+ doc.on('update', onUpdate);
49
+ awareness.on('update', onAwareness);
50
+ const on = () => { p.online = true; for (const q of relay())
51
+ sync(p, q); };
52
+ const off = () => { p.online = false; };
53
+ p.peer = {
54
+ doc, awareness, synced: true,
55
+ get isOnline() { return p.online; },
56
+ offline: off, online: on, connect: on, disconnect: off,
57
+ leave() {
58
+ awareness.setLocalState(null);
59
+ off();
60
+ doc.off('update', onUpdate);
61
+ awareness.off('update', onAwareness);
62
+ // its 'outdated' timer would keep a process alive
63
+ awareness.destroy();
64
+ all.splice(all.indexOf(p), 1);
65
+ },
66
+ };
67
+ all.push(p);
68
+ for (const q of relay())
69
+ sync(p, q);
70
+ return p.peer;
71
+ },
72
+ };
73
+ return net;
74
+ };
75
+
76
+ export { createNetwork };
77
+ //# sourceMappingURL=yjs-testing.esm.js.map