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
@@ -21,7 +21,7 @@ import type {App} from './runtime'
21
21
  import {tearDown, INST, SEED} from './teardown'
22
22
  import {CoreDef, defOf, isObj} from './define'
23
23
  import {Cell, CalcCell, calcCell, keyCell, lensCell, localCell, pathOf} from './cell'
24
- import {hosts, posts, pres, resolvers} from './registry'
24
+ import {ext, hosts, posts, pres, resolvers} from './registry'
25
25
  import {handle} from './actions'
26
26
  import {viewOf} from './view'
27
27
  import {makeCommandSource} from '../extra/command'
@@ -31,13 +31,22 @@ import {sameData} from '../cycle/dom/utils'
31
31
 
32
32
  // v7: a known binding target joins key/id/path in the lifetime identity. Fresh equivalent
33
33
  // byId helpers retain identity; opaque lenses need an explicit key for a new target.
34
- function bindingChanged(a: any, b: any): boolean {
34
+ export function bindingChanged(a: any, b: any): boolean {
35
35
  if (a === b) return false
36
36
  const x = a?.[BINDING], y = b?.[BINDING]
37
37
  return x && y ? !x.every((v: any, i: number) => Object.is(v, y[i]))
38
38
  : !!(x || y || typeof a == 'string' || typeof b == 'string' || a === undefined || b === undefined)
39
39
  }
40
40
 
41
+ /** a binding whose target is known (a key, a byId selection): its deletion ends the child */
42
+ export const known = (b: any) => typeof b == 'string' || b?.[BINDING]
43
+ /** F-3: `owner` watches child (or Switchable page) `k`'s target: notify() disposes it when deleted */
44
+ export function watch(owner: Inst, k: any) {
45
+ k.bs = bindingState(k)
46
+ ;(owner.bound ||= new Set()).add(k)
47
+ owner.app.watchers.add(owner)
48
+ }
49
+
41
50
  function bindingState(k: any): any {
42
51
  const c = k.base || k.cell || k.arr
43
52
  return c && (c.has?.() ?? c.get() !== undefined) ? (k.cell || k.arr).get() : undefined
@@ -118,6 +127,8 @@ let left = 0
118
127
  export const sameTree = (a: any, b: any): boolean => (left = SAME_MAX, same(a, b))
119
128
  function same(a: any, b: any): boolean {
120
129
  if (a === b) return true
130
+ // CV-3: a compiled block (its inside is its hole values)
131
+ if (b?.$b) return !!a?.$b && ext.sb!(a, b)
121
132
  if (--left < 0 || !a || !b || a.sel !== b.sel || a.key !== b.key || a.text !== b.text || b.data?.hook || !sameData(a.data, b.data)) return false
122
133
  const x = a.children, y = b.children
123
134
  if (x === y) return true
@@ -129,8 +140,23 @@ function same(a: any, b: any): boolean {
129
140
  const EMPTY_CTX: any = Object.freeze({})
130
141
  /** a view read every key (spread, Object.keys): any context change re-renders it */
131
142
  const ALL = '\u0000all'
143
+ // D168 read-tracking: one handler for every view; each render's Proxy targets a holder of that
144
+ // render's context (c) and the instance's key set (k), so a read after the view returned (a
145
+ // closure the view made) still sees its own render's context
146
+ const track = (h: any, k: any) => (typeof k == 'string' && h.k.add(k), k)
147
+ const TRACK: ProxyHandler<any> = {
148
+ get: (h, k) => h.c[track(h, k)],
149
+ has: (h, k) => track(h, k) in h.c,
150
+ ownKeys: (h) => (h.k.add(ALL), Reflect.ownKeys(h.c)),
151
+ getOwnPropertyDescriptor: (h, k) => Reflect.getOwnPropertyDescriptor(h.c, k),
152
+ }
132
153
 
133
154
  let IDS = 0
155
+ // shared defaults (one per module, not per instance): the first render's "last state", no
156
+ // slots, no tag children (reconcile() makes an instance its own Map on its first child)
157
+ const UNSET = {}
158
+ const NOSLOTS: Record<string, any[]> = Object.freeze({}) as any
159
+ const NOKIDS = new Map<string, any>()
134
160
 
135
161
  /**
136
162
  * a child that failed to instantiate: renders the owner's error fallback (SYG408). It keeps its
@@ -144,6 +170,31 @@ class Failed {
144
170
  dispose() {}
145
171
  }
146
172
 
173
+ /**
174
+ * The intent's sources: a Proxy whose own properties are the source names, in the table's order
175
+ * (spread, Object.keys, `in`, destructuring as on a plain object), each made when read; one
176
+ * handler for every instance (a property defined with Object.defineProperty per instance was
177
+ * ~1 µs of its creation). Its target holds the instance and what the intent adds
178
+ */
179
+ const table = (t: any) => t[INST].app.srcDesc(!!t[INST].parent)
180
+ const tab = (t: any, k: any): any => typeof k == 'string' && table(t)[k]
181
+ const SRC: ProxyHandler<any> = {
182
+ get: (t, k) => { const g = tab(t, k); return g ? g(t[INST]) : t[k] },
183
+ has: (t, k) => !!tab(t, k) || k in t,
184
+ ownKeys: (t) => Object.keys(table(t)).concat(Reflect.ownKeys(t) as any),
185
+ getOwnPropertyDescriptor: (t, k) => { const g = tab(t, k); return g ? {value: g(t[INST]), enumerable: true, configurable: true} : Reflect.getOwnPropertyDescriptor(t, k) },
186
+ // a source is read-only, as the getter-only property it was: a write fails (a TypeError in strict code)
187
+ set: (t, k, v) => !tab(t, k) && ((t[k] = v), true),
188
+ }
189
+
190
+ /** an intent stream's listener: its action (an object, no closures per stream) */
191
+ class Listener {
192
+ constructor(public i: Inst, public t: string) {}
193
+ next(d: any) { const i = this.i; i.dying ? handle(i, this.t, d, 'intent') : i.app.dispatch(i, this.t, d, 'intent') }
194
+ // (no code of its own yet: R4's diagnostics may add one)
195
+ error(e: any) { const i = this.i; console.error(`[Sygnal] ${i.def.name}: intent stream '${this.t}' errored; it stops emitting`, e); i.app.appError(i, e, 'intent', this.t) }
196
+ }
197
+
147
198
  export class Inst {
148
199
  readonly id = IDS++
149
200
  disposed = false
@@ -159,41 +210,47 @@ export class Inst {
159
210
  children!: any[]
160
211
  /** the children as the parent passed them (slots included) */
161
212
  raw!: any[]
162
- slots: Record<string, any[]> = {}
163
- kids = new Map<string, any>()
213
+ slots = NOSLOTS
214
+ kids = NOKIDS
164
215
  /** the children with a string / byId binding: the presence scan's (notify) */
165
216
  bound?: Set<any>
166
217
  uidBase: string
218
+ // (the fields most instances set are initialized here, so they are the object's own slots;
219
+ // the ones few instances use are left out until used)
167
220
  /** the parent's view of this instance's output (last vnode / READY it injected) */
168
221
  last: any; lr = true
169
222
  outv: any
170
223
  // the last render's inputs: state, props, children, context, the context keys its view read
171
- ls: any = {}; lp: any; lc: any; lctx: any; keys: Set<string> | null = null; tmpl: any; forced = true
224
+ ls: any = UNSET; lp: any; lc: any; lctx: any; keys: Set<string> | null = null; tmpl: any; forced = true
172
225
  postSels: string[] | null = null
173
226
  // context memo: the app's write version it was computed at, the value
174
227
  cver = -1; cv: any
175
228
  subs: any[] = []
176
- srcs: Record<string, any> | null = null
177
- fb: Record<string, any> | null = null
178
- ac: AbortController | null = null
229
+ /** a Collection item's key; the isolate memo (runtime scopeValue) */
230
+ k: any; $iso: any
231
+ /** canvas-three F2: a template node's last injected copy, by the node (its children array, `.r` the copy) */
232
+ ic?: WeakMap<any, any>
233
+ srcs?: Record<string, any>
234
+ fb?: Record<string, any>
235
+ ac?: AbortController | null
179
236
  disp$: any
180
237
  st$: any; stLast: any; stateSrc: any
181
238
  props$: any; children$: any
182
239
  /** CHILD.select listeners (the children's PARENT values) */
183
- hub: Set<(e: any) => void> | null = null
240
+ hub?: Set<(e: any) => void> | null
184
241
  wd: any; so: any; el: any
185
242
  iv: any
186
243
  cmds: any
187
244
  /** the app's render epoch it last rendered at (G-311) */
188
245
  ep = 0
189
246
  /** pending next() timers (G-300) */
190
- timers: Set<any> | null = null
247
+ timers?: Set<any> | null
191
248
  // statics.ts: the [sink, static] pairs it declares, the last values sent, the state / shown
192
249
  // flag they were computed from; its reply streams
193
- st: Array<[string, string]> | null = null
194
- sv: Record<string, any> | null = null
195
- sS: any; sH: any
196
- rep: any[] | null = null
250
+ st?: Array<[string, string]> | null
251
+ sv?: Record<string, any> | null
252
+ sS: any; sH: any; sC: any
253
+ rep?: any[] | null
197
254
 
198
255
  constructor(
199
256
  public app: App, public def: CoreDef, public parent: Inst | null, public base: Cell,
@@ -204,7 +261,8 @@ export class Inst {
204
261
  this.setChildren(children)
205
262
  this.uidBase = uidBase
206
263
  this.ready = !def.ready
207
- this.cell = def.calculated || def.idle ? calcCell(base, def) : {get: () => base.get(), set: (v) => base.set(v), raw: () => base.get()}
264
+ // (no calculated fields: the base cell itself)
265
+ this.cell = def.calculated || def.idle ? calcCell(base, def) : base as CalcCell
208
266
  const H = app.hooks
209
267
  // the initial state, synchronously (INITIALIZE at construction, D165): a root (or the state an
210
268
  // HMR swap keeps), an isolated child's local state. D174: an isolated child bound to a slice
@@ -226,7 +284,7 @@ export class Inst {
226
284
  if (reset || (!has && def.model)) app.dispatch(this, SEED, {v: init, r: reset, b}, 'built-in')
227
285
  else if (has) H.onStateSeed?.(viewOf(this), b.get(), init)
228
286
  }
229
- } else if (def.idle && isObj(this.cell.raw()) && !parent) this.cell.set(this.cell.raw())
287
+ } else if (def.idle && isObj(this.rawState()) && !parent) this.cell.set(this.rawState())
230
288
  H.onCreate?.(viewOf(this))
231
289
  dbg(this, () => 'Instantiated')
232
290
  if (def.handlers.has('INITIALIZE')) app.dispatch(this, 'INITIALIZE', init, 'built-in')
@@ -245,6 +303,8 @@ export class Inst {
245
303
  }
246
304
 
247
305
  get name() { return this.def.name }
306
+ /** the state with calculated fields only as stored (STATE.stream) */
307
+ rawState() { return this.cell === this.base ? this.base.get() : this.cell.raw() }
248
308
 
249
309
  // ---------------------------------------------------------------- sources (the stream edge)
250
310
  subscribe() {
@@ -264,11 +324,7 @@ export class Inst {
264
324
  const s = actions[type]
265
325
  if (s == null) continue
266
326
  if (typeof s.subscribe != 'function') fail('SYG603', this, `intent entry '${type}' is not a stream`, 'Return { ACTION: stream$ }')
267
- this.subs.push(s.subscribe({
268
- next: (d: any) => this.dying ? handle(this, type, d, 'intent') : app.dispatch(this, type, d, 'intent'),
269
- // (no code of its own yet: R4's diagnostics may add one)
270
- error: (e: any) => { console.error(`[Sygnal] ${def.name}: intent stream '${type}' errored; it stops emitting`, e); app.appError(this, e, 'intent', type) },
271
- }))
327
+ this.subs.push(s.subscribe(new Listener(this, type)))
272
328
  }
273
329
  } catch (e) {
274
330
  // a failed instance keeps no subscription (its parent renders the error fallback)
@@ -280,10 +336,7 @@ export class Inst {
280
336
  }
281
337
 
282
338
  sources() {
283
- if (this.so) return this.so
284
- const so = this.so = Object.defineProperties({}, this.app.srcDesc(!!this.parent))
285
- Object.defineProperty(so, INST, {value: this})
286
- return so
339
+ return this.so ||= new Proxy({[INST]: this}, SRC)
287
340
  }
288
341
  /** a driver source isolated to this instance (the parent's, isolated to this scope; cached) */
289
342
  src(n: string): any {
@@ -297,7 +350,7 @@ export class Inst {
297
350
  stateSource() {
298
351
  if (this.stateSrc) return this.stateSrc
299
352
  this.st$ = xs.createWithMemory()
300
- const v = this.stLast = this.cell.raw()
353
+ const v = this.stLast = this.rawState()
301
354
  if (v !== undefined) this.st$.shamefullySendNext(v)
302
355
  this.app.watchers.add(this)
303
356
  return (this.stateSrc = new StateSource(this.st$, 'STATE', this.dispose$(), 1))
@@ -321,7 +374,7 @@ export class Inst {
321
374
  }
322
375
  }
323
376
  if (this.st$) {
324
- const v = this.cell.raw()
377
+ const v = this.rawState()
325
378
  if (v !== this.stLast && v !== undefined) { this.stLast = v; this.st$.shamefullySendNext(v) }
326
379
  }
327
380
  }
@@ -342,9 +395,8 @@ export class Inst {
342
395
  setChildren(children: any[]) {
343
396
  // G-294: the raw children (before the slots are split out) decide whether the view re-runs
344
397
  this.raw = children
345
- const [kids, slots] = children.length ? extractSlots(children) : [children, {}]
346
- this.children = kids
347
- this.slots = slots
398
+ if (children.length) [this.children, this.slots] = extractSlots(children)
399
+ else this.children = children, this.slots = NOSLOTS
348
400
  }
349
401
  setProps(props: Record<string, any>, children: any[]) {
350
402
  this.props = props
@@ -458,10 +510,11 @@ export class Inst {
458
510
  if (v !== k.last || k.ready !== k.lr) { k.last = v; k.lr = k.ready; kidsDirty = true }
459
511
  }
460
512
  if (!viewDirty && !kidsDirty) return this.outv
461
- if (same && !kidsDirty) { this.app.hooks.onRender?.(viewOf(this), this.tmpl); return this.outv }
513
+ // CV-3: hooks (dev checks, devtools, testing) see a compiled block's vnodes (ext.xb)
514
+ if (same && !kidsDirty) { this.app.hooks.onRender?.(viewOf(this), ext.xb ? ext.xb(this.tmpl) : this.tmpl); return this.outv }
462
515
  let v = this.kids.size ? this.inject(this.tmpl, 'r') : this.tmpl
463
516
  if (this.postSels) for (const s of this.postSels) v = posts[s](v, this)
464
- this.app.hooks.onRender?.(viewOf(this), v)
517
+ this.app.hooks.onRender?.(viewOf(this), ext.xb ? ext.xb(v) : v)
465
518
  return (this.outv = this.parent ? this.app.scopeValue(this.parent, 'DOM', v, this) : v)
466
519
  }
467
520
 
@@ -478,12 +531,10 @@ export class Inst {
478
531
  const def = this.def
479
532
  let keys: Set<string> | null = null, context = ctx
480
533
  if (ctx !== EMPTY_CTX) {
481
- const ks = keys = new Set<string>()
482
- context = new Proxy(ctx, {
483
- get: (t, k) => (typeof k == 'string' && ks.add(k), t[k as any]),
484
- has: (t, k) => (typeof k == 'string' && ks.add(k), k in t),
485
- ownKeys: (t) => (ks.add(ALL), Reflect.ownKeys(t)),
486
- })
534
+ // (the last render's set, read by ctxChanged before this call)
535
+ keys = this.keys || new Set<string>()
536
+ keys.clear()
537
+ context = new Proxy({c: ctx, k: keys}, TRACK)
487
538
  }
488
539
  try {
489
540
  this.tmpl = def.view({...this.props, state, children: this.children, slots: this.slots, context, uid: this.uid})
@@ -536,12 +587,8 @@ export class Inst {
536
587
  const binding = host && sel != 'switchable' ? 'from' : 'state'
537
588
  if (k && (k.disposed || (view && k.def && k.def.view !== view) || bindingChanged(k.props?.[binding], props[binding]))) { k.dispose(); this.kids.delete(id); k = undefined }
538
589
  if (k) k.setProps(props, children, n, id)
539
- else this.kids.set(id, k = host ? this.host(host, props, children, id, n) : this.child(view, props, children, id, !!data.props?.resetState))
540
- if (typeof props[binding] == 'string' || props[binding]?.[BINDING]) {
541
- k.bs = bindingState(k)
542
- ;(this.bound ||= new Set()).add(k)
543
- this.app.watchers.add(this)
544
- }
590
+ else (this.kids === NOKIDS ? this.kids = new Map() : this.kids).set(id, k = host ? this.host(host, props, children, id, n) : this.child(view, props, children, id, !!data.props?.resetState))
591
+ if (known(props[binding])) watch(this, k)
545
592
  return
546
593
  }
547
594
  if (posts[sel] && !(ps ||= []).includes(sel)) ps.push(sel)
@@ -604,7 +651,13 @@ export class Inst {
604
651
  const v = this.inject(c[i], path + '.' + i)
605
652
  if (v !== c[i]) (out ||= c.slice())[i] = v
606
653
  }
607
- return out ? {...n, children: out} : n
654
+ if (!out) return n
655
+ // canvas-three F2: the same children as this node's last injection: its last copy, so snabbdom
656
+ // skips an element (and its hooks: a widget's postpatch) above components that didn't change
657
+ const p = (this.ic ||= new WeakMap()).get(n)
658
+ if (p && p.length == out.length && p.every((x: any, i: number) => x === out[i])) return p.r
659
+ this.ic.set(n, out)
660
+ return out.r = {...n, children: out}
608
661
  }
609
662
 
610
663
  // ---------------------------------------------------------------- actions
@@ -33,6 +33,9 @@ function mount(st: PortalState, target: string): boolean {
33
33
  container.appendChild(anchor)
34
34
  st.pv = st.v.data._portalVnode = (patch ||= snabbdomInit(defaultModules))(anchor, box(st.kids))
35
35
  st.c = st.v.data._portalContainer = container
36
+ // ui-sweep R4: the placeholder names its content, so ELEMENT commands and sygnal/ui parts find
37
+ // elements the component renders into the portal (extra/elementCommands.ts, ui/shared.ts)
38
+ if (st.v.elm) st.v.elm.__portal = st.pv.elm
36
39
  return true
37
40
  }
38
41
 
@@ -74,6 +77,7 @@ export function portalPlaceholder(target: string, children: any[]): any {
74
77
  if (!st.pv) return
75
78
  st.pv = newVnode.data._portalVnode = patch(st.pv, box(st.kids))
76
79
  newVnode.data._portalContainer = st.c
80
+ if (newVnode.elm) newVnode.elm.__portal = st.pv.elm
77
81
  },
78
82
  destroy: (vnode: any) => {
79
83
  const st: PortalState | undefined = vnode.data?._p
@@ -27,3 +27,7 @@ export const posts: Record<string, (vnode: any, owner: any) => any> = {}
27
27
  export const resolvers: Array<(view: ComponentFn, owner: any) => ComponentFn | undefined> = []
28
28
  /** definition time, every app: (src, view) -> src (behaviors, undo, selection, pager, persist, resources) */
29
29
  export const defHooks: Array<(src: DefSource, view: ComponentFn) => DefSource | void> = []
30
+ /** CV-3 (compiled blocks, src/pragma/blocks.ts, registered by the first block): xb expands blocks
31
+ * into plain vnodes for a DOM driver that doesn't patch them (not `DOMDriver.blocks`); sb, the
32
+ * same-output check for two blocks */
33
+ export const ext: {xb?: (v: any) => any; sb?: (a: any, b: any) => boolean} = {}
@@ -21,14 +21,16 @@ import xs from '../extra/xstreamCompat'
21
21
  import {makeDOMDriver} from '../cycle/dom/index'
22
22
  import eventBusDriver from '../extra/eventDriver'
23
23
  import logDriver from '../extra/logDriver'
24
- import {NOT_SINK, isAbort} from '../shared'
24
+ import {NOT_SINK, isAbort, delivering} from '../shared'
25
25
  import {error as logError, callHook} from '../extra/diagnostics/legacy'
26
26
  import type {ComponentFn, DefSource, Hooks, RuntimeAPI, ActionCause} from './hooks'
27
27
  import {CoreDef, defOf, rootDef} from './define'
28
28
  import {rootCell} from './cell'
29
29
  import {Inst} from './instance'
30
+ import {seen} from './actions'
30
31
  import {viewOf} from './view'
31
- import {stopQueued, INST, SET, SEED} from './teardown'
32
+ import {ext} from './registry'
33
+ import {stopQueued, SET, SEED} from './teardown'
32
34
  import {scanSources} from './statics'
33
35
  // the public ClientOnly is a separate bundle (sygnal/vike/ClientOnly) with no core of its own, so
34
36
  // its marker handler registers with the core (the other markers register from their modules)
@@ -56,6 +58,8 @@ export interface StartOptions {
56
58
  __state?: any
57
59
  /** internal: an HMR swap: the instances its first flush makes get no BOOTSTRAP (as today's `__hmr`) */
58
60
  __swap?: boolean
61
+ /** internal: the handlers' now() / random() / uuid() (renderComponent's fixed clock, seeded random, ids) */
62
+ __entropy?: {now?: () => number; random?: () => number; uuid?: () => string}
59
63
  /** internal: renderComponent's root (test intent, initial state, name) */
60
64
  __override?: Partial<DefSource> & {name?: string; testActions?: string[]}
61
65
  }
@@ -81,6 +85,8 @@ export class App {
81
85
  last: any
82
86
  /** P46-P: a view ran but kept its last vnode (the flush still emits, as when it changed) */
83
87
  ran = false
88
+ /** CV-3: the DOM driver patches compiled blocks itself (makeDOMDriver's DOMDriver.blocks) */
89
+ bd = false
84
90
  vdomL: any
85
91
  vdom$: any
86
92
  sources: Record<string, any> = {}
@@ -149,6 +155,8 @@ export class App {
149
155
  /** `src`: for a reply, the source that delivered it (the action log's replySink) */
150
156
  dispatch(inst: Inst, type: any, data: any, cause: ActionCause = 'intent', src?: string) {
151
157
  if (this.disposed) return
158
+ // 7.2: at enqueue, with the DOM event being delivered (a string type: not setState / a seed)
159
+ typeof type == 'string' && this.hooks.onDispatch?.(viewOf(inst), type, data, cause, delivering)
152
160
  this.queue.push(inst, type, data, cause, src)
153
161
  if (!this.draining && !this.rendering) this.drain()
154
162
  }
@@ -165,8 +173,9 @@ export class App {
165
173
  if (q[i + 1] === SET) {
166
174
  const f = q[i + 2], c = inst.cell
167
175
  const v = typeof f == 'function' ? f(c.get()) : f
168
- // (ABORT: no change, as from a reducer)
169
- if (v !== c.get() && !isAbort(v)) c.set(v)
176
+ // (ABORT: no change, as from a reducer). 7.2: seen by onAction as a 'setState' action
177
+ // with the new state (false: dropped)
178
+ if (v !== c.get() && !isAbort(v) && seen(inst, 'setState', v, 'setState')) c.set(v)
170
179
  } else if (q[i + 1] === SEED) {
171
180
  // G-309: decided when it is applied: a parent write queued before it keeps the slice (D174)
172
181
  const d = q[i + 2], b = d.b
@@ -283,8 +292,9 @@ export class App {
283
292
  if (v !== this.last || this.ran) {
284
293
  this.ran = false
285
294
  this.last = v
286
- this.hooks.onPatch?.(v)
287
- this.vdomL?.next(v ?? null)
295
+ this.hooks.onPatch?.(ext.xb ? ext.xb(v) : v)
296
+ // CV-3: compiled blocks are patched only by the browser DOM driver; any other gets their vnodes
297
+ this.vdomL?.next(ext.xb && !this.bd ? ext.xb(v ?? null) : v ?? null)
288
298
  }
289
299
  if (this.dq.length) this.stopLater()
290
300
  } finally {
@@ -350,7 +360,7 @@ export class App {
350
360
  scopeValue(parent: Inst, _: string, v: any, inst: Inst) {
351
361
  const d = parent.dom
352
362
  if (!d || inst.scope === undefined) return v
353
- if (typeof d.isolateValue == 'function') return d.isolateValue(v, inst.scope)
363
+ if (typeof d.isolateValue == 'function') return d.isolateValue(v, inst.scope, inst)
354
364
  let r: any
355
365
  this.pipe(inst, '\u0000DOM', v, (s$) => d.isolateSink(s$, inst.scope), (x) => { r = x })
356
366
  return r
@@ -376,22 +386,21 @@ export class App {
376
386
  }
377
387
 
378
388
  // ---------------------------------------------------------------- intent sources
379
- /** the getters of an instance's intent sources (one descriptor map per app and kind) */
380
- srcDesc(child: boolean) {
389
+ /** an instance's intent sources by name, each a getter of its instance (one table per app and kind; Inst SRC) */
390
+ srcDesc(child: boolean): Record<string, (i: Inst) => any> {
381
391
  const k = +child
382
392
  if (this.desc[k]) return this.desc[k]
383
- const d: any = {}
384
- const get = (f: (i: Inst) => any) => ({get(this: any) { return f(this[INST]) }, enumerable: true, configurable: true})
385
- for (const n in this.sources) if (n !== 'DOM') d[n] = get(i => i.src(n))
386
- d.DOM = get(i => i.dom && (i.wd ||= wrapDOM(i.dom)))
387
- d.STATE = get(i => i.stateSource())
388
- d.CHILD = get(i => i.childSource())
389
- d.dispose$ = get(i => i.dispose$())
393
+ const d: any = Object.create(null)
394
+ for (const n in this.sources) if (n !== 'DOM') d[n] = (i: Inst) => i.src(n)
395
+ d.DOM = (i: Inst) => i.dom && (i.wd ||= wrapDOM(i.dom))
396
+ d.STATE = (i: Inst) => i.stateSource()
397
+ d.CHILD = (i: Inst) => i.childSource()
398
+ d.dispose$ = (i: Inst) => i.dispose$()
390
399
  if (child) {
391
- d.props$ = get(i => i.props$ || seeded(i, 'props$', i.props))
392
- d.children$ = get(i => i.children$ || seeded(i, 'children$', i.children))
400
+ d.props$ = (i: Inst) => i.props$ || seeded(i, 'props$', i.props)
401
+ d.children$ = (i: Inst) => i.children$ || seeded(i, 'children$', i.children)
393
402
  // a Command passed as a prop (createCommand): its messages, as today's `commands$`
394
- d.commands$ = get(i => i.commands())
403
+ d.commands$ = (i: Inst) => i.commands()
395
404
  }
396
405
  return (this.desc[k] = d)
397
406
  }
@@ -437,9 +446,8 @@ export class App {
437
446
  : k == 'wrapHandler' ? (i: any, t: any, s: any, f: any) => { const x = b(i, t, s, f) || f; return a(i, t, s, x) || x }
438
447
  : k == 'wrapSources' ? (i: any, s: any) => { const x = b(i, s) || s; return a(i, x) || x }
439
448
  : k == 'transformDef' ? (src: any, v: any) => { const x = b(src, v) || src; return a(x, v) || x }
440
- // onElementCommand: false (not run) from either layer
441
- : k == 'onElementCommand' ? (i: any, c: any) => { const x = b(i, c); return a(i, c) === false || x === false ? false : undefined }
442
- : (...args: any[]) => { b(...args); a(...args) }
449
+ // false from either layer: onElementCommand (not run), onAction (dropped)
450
+ : (...args: any[]) => { const x = b(...args); return a(...args) === false || x === false ? false : undefined }
443
451
  }
444
452
  }
445
453
  // D168's safety net (R4): a view context tracking skipped, offered for a re-run check
@@ -460,9 +468,9 @@ export class App {
460
468
  }
461
469
  }
462
470
 
463
- function wrapDOM(dom: any) {
464
- return new Proxy(dom, {get: (t, p, r) => (typeof p == 'symbol' || p in t ? Reflect.get(t, p, r) : (sel: any) => t.select(sel).events(p))})
465
- }
471
+ // (one handler for every instance's DOM source)
472
+ const DOMH: ProxyHandler<any> = {get: (t, p, r) => (typeof p == 'symbol' || p in t ? Reflect.get(t, p, r) : (sel: any, o?: any) => t.select(sel).events(p, o))}
473
+ const wrapDOM = (dom: any) => new Proxy(dom, DOMH)
466
474
  /** props$ / children$: a memory stream with the current value, fed by setProps */
467
475
  function seeded(i: any, k: string, v: any) {
468
476
  const s = xs.createWithMemory()
@@ -529,6 +537,7 @@ export function start(Root: ComponentFn, drivers: Record<string, any> = {}, opts
529
537
  }
530
538
  // the Cycle run loop, reduced: a proxy sink per driver, the driver's source, then the app's
531
539
  // streams imitated (values reach a driver synchronously; the DOM gets one vnode per flush)
540
+ app.bd = !!all.DOM?.blocks
532
541
  for (const n in all) {
533
542
  const p = app.proxies[n] = xs.create()
534
543
  const src = app.sources[n] = all[n](p, n)
@@ -76,17 +76,17 @@ const shownOf = (inst: Inst) => {
76
76
  /** recompute the instance's declarations when its state or shown flag changed; send the changed ones */
77
77
  export function checkStatics(inst: Inst) {
78
78
  if (inst.disposed) return
79
- const s = inst.cell.get(), shown = shownOf(inst)
79
+ const s = inst.cell.get(), shown = shownOf(inst), c = inst.context()
80
80
  // no state yet: nothing declared (as the state stream, which skips undefined)
81
- if (s === undefined || (s === inst.sS && shown === inst.sH)) return
82
- inst.sS = s; inst.sH = shown
81
+ if (s === undefined || (s === inst.sS && shown === inst.sH && c === inst.sC)) return
82
+ inst.sS = s; inst.sH = shown; inst.sC = c
83
83
  const view = inst.def.view, sv = inst.sv!
84
84
  for (const [n, k] of inst.st!) {
85
85
  const f = view[k]
86
86
  let v: any = f
87
87
  try {
88
- if (typeof f == 'function') v = f(s)
89
- else if (f && typeof f == 'object') { v = {}; for (const r in f) v[r] = f[r](s) }
88
+ if (typeof f == 'function') v = f(s, c)
89
+ else if (f && typeof f == 'object') { v = {}; for (const r in f) v[r] = f[r](s, c) }
90
90
  } catch (e) {
91
91
  // as today: nothing sent; the next value is sent even when equal to the last one
92
92
  inst.app.caught(inst, 'SYG216', `${k} threw; nothing sent`, e, 'declaration')
@@ -7,6 +7,7 @@ import {EventsFnOptions} from './DOMSource';
7
7
  import {Scope} from './isolate';
8
8
  import SymbolTree from './SymbolTree';
9
9
  import PriorityQueue from './PriorityQueue';
10
+ import {deliver} from '../../shared';
10
11
  import {
11
12
  fromEvent,
12
13
  preventDefaultConditional,
@@ -382,46 +383,49 @@ export class EventDelegator {
382
383
  passive: boolean,
383
384
  bubbles = true
384
385
  ): void {
385
- const cycleEvent = this.patchEvent(event);
386
- const target = event.target as Element;
387
- const rootElement = this.isolateModule.getRootElement(target);
388
-
389
- if (bubbles) {
390
- const namespace = this.isolateModule.getNamespace(target);
391
- if (!namespace) {
392
- return;
393
- }
394
- const listeners = this.getVirtualListeners(eventType, namespace);
395
- const phase = (useCapture: boolean) => this.bubble(
396
- eventType,
397
- target,
398
- rootElement,
399
- cycleEvent,
400
- listeners,
401
- namespace,
402
- namespace.length - 1,
403
- useCapture,
404
- passive
405
- );
406
- phase(true);
407
- phase(false);
408
- } else {
409
- const phase = (useCapture: boolean) => {
410
- this.putNonBubblingListener(eventType, target, useCapture, passive);
411
- this.doBubbleStep(
386
+ // 7.2: the event is recorded while it is delivered (the core's onDispatch hook)
387
+ deliver(event, () => {
388
+ const cycleEvent = this.patchEvent(event);
389
+ const target = event.target as Element;
390
+ const rootElement = this.isolateModule.getRootElement(target);
391
+
392
+ if (bubbles) {
393
+ const namespace = this.isolateModule.getNamespace(target);
394
+ if (!namespace) {
395
+ return;
396
+ }
397
+ const listeners = this.getVirtualListeners(eventType, namespace);
398
+ const phase = (useCapture: boolean) => this.bubble(
412
399
  eventType,
413
400
  target,
414
401
  rootElement,
415
402
  cycleEvent,
416
- this.virtualNonBubblingListener,
403
+ listeners,
404
+ namespace,
405
+ namespace.length - 1,
417
406
  useCapture,
418
407
  passive
419
408
  );
420
- };
421
- phase(true);
422
- phase(false);
423
- event.stopPropagation();
424
- }
409
+ phase(true);
410
+ phase(false);
411
+ } else {
412
+ const phase = (useCapture: boolean) => {
413
+ this.putNonBubblingListener(eventType, target, useCapture, passive);
414
+ this.doBubbleStep(
415
+ eventType,
416
+ target,
417
+ rootElement,
418
+ cycleEvent,
419
+ this.virtualNonBubblingListener,
420
+ useCapture,
421
+ passive
422
+ );
423
+ };
424
+ phase(true);
425
+ phase(false);
426
+ event.stopPropagation();
427
+ }
428
+ });
425
429
  }
426
430
 
427
431
  private bubble(
@@ -28,19 +28,24 @@ export class MainDOMSource {
28
28
  private _eventDelegator: EventDelegator,
29
29
  private _name: string
30
30
  ) {
31
- this.isolateSource = (source, scope) =>
32
- new MainDOMSource(
33
- source._rootElement$,
34
- source._sanitation$,
35
- source._namespace.concat(getScopeObj(scope)),
36
- source._isolateModule,
37
- source._eventDelegator,
38
- source._name
39
- );
40
- this.isolateSink = makeIsolateSink(this._namespace) as any;
41
- this.isolateValue = makeIsolateValue(this._namespace);
42
31
  }
43
32
 
33
+ // (methods and lazily made functions, not closures per source: one source per instance)
34
+ public isolateSource(source: MainDOMSource, scope: string): MainDOMSource {
35
+ return new MainDOMSource(
36
+ source._rootElement$,
37
+ source._sanitation$,
38
+ source._namespace.concat(getScopeObj(scope)),
39
+ source._isolateModule,
40
+ source._eventDelegator,
41
+ source._name
42
+ );
43
+ }
44
+ private _is?: IsolateSink<VNode>;
45
+ private _iv?: (node: any, scope: string, memo?: any) => any;
46
+ get isolateSink(): IsolateSink<VNode> { return this._is ||= makeIsolateSink(this._namespace) as any }
47
+ get isolateValue(): (node: any, scope: string, memo?: any) => any { return this._iv ||= makeIsolateValue(this._namespace) }
48
+
44
49
  private _elements(): Stream<Array<Element>> {
45
50
  if (this._namespace.length === 0) {
46
51
  return this._rootElement$.map(x => [x]);
@@ -148,7 +153,4 @@ export class MainDOMSource {
148
153
  this._sanitation$.shamefullySendNext(null);
149
154
  }
150
155
 
151
- public isolateSource: (source: MainDOMSource, scope: string) => MainDOMSource;
152
- public isolateSink: IsolateSink<VNode>;
153
- public isolateValue: (node: any, scope: string) => any;
154
156
  }