sygnal 5.3.7 → 6.0.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 (438) hide show
  1. package/CHANGELOG.md +646 -0
  2. package/README.md +77 -47
  3. package/dist/astro/client.cjs.js +33 -8114
  4. package/dist/astro/client.cjs.js.map +1 -0
  5. package/dist/astro/client.mjs +33 -8114
  6. package/dist/astro/client.mjs.map +1 -0
  7. package/dist/astro/index.cjs.js +1039 -9
  8. package/dist/astro/index.cjs.js.map +1 -0
  9. package/dist/astro/index.mjs +1038 -9
  10. package/dist/astro/index.mjs.map +1 -0
  11. package/dist/astro/server.cjs.js +3400 -166
  12. package/dist/astro/server.cjs.js.map +1 -0
  13. package/dist/astro/server.mjs +3400 -166
  14. package/dist/astro/server.mjs.map +1 -0
  15. package/dist/devtools.cjs.js +1431 -0
  16. package/dist/devtools.cjs.js.map +1 -0
  17. package/dist/devtools.esm.js +1419 -0
  18. package/dist/devtools.esm.js.map +1 -0
  19. package/dist/diagnostics.cjs.js +4033 -0
  20. package/dist/diagnostics.cjs.js.map +1 -0
  21. package/dist/diagnostics.esm.js +4003 -0
  22. package/dist/diagnostics.esm.js.map +1 -0
  23. package/dist/element.cjs.js +230 -0
  24. package/dist/element.cjs.js.map +1 -0
  25. package/dist/element.esm.js +228 -0
  26. package/dist/element.esm.js.map +1 -0
  27. package/dist/guide/accessibility.md +257 -0
  28. package/dist/guide/adapters.md +221 -0
  29. package/dist/guide/behaviors.md +312 -0
  30. package/dist/guide/browser-sources.md +203 -0
  31. package/dist/guide/drag-and-drop.md +342 -0
  32. package/dist/guide/element-commands.md +306 -0
  33. package/dist/guide/error-boundaries.md +169 -0
  34. package/dist/guide/forms-reference.md +381 -0
  35. package/dist/guide/forms.md +250 -0
  36. package/dist/guide/http.md +299 -0
  37. package/dist/guide/inputs.md +173 -0
  38. package/dist/guide/persistence.md +245 -0
  39. package/dist/guide/recipes/carousel.md +155 -0
  40. package/dist/guide/recipes/charts.md +177 -0
  41. package/dist/guide/recipes/code-editor.md +135 -0
  42. package/dist/guide/recipes/data-grid.md +155 -0
  43. package/dist/guide/recipes/data-table.md +196 -0
  44. package/dist/guide/recipes/i18n.md +222 -0
  45. package/dist/guide/recipes/icons.md +115 -0
  46. package/dist/guide/recipes/overview.md +33 -0
  47. package/dist/guide/recipes/rich-text.md +154 -0
  48. package/dist/guide/resources.md +471 -0
  49. package/dist/guide/ssr.md +255 -0
  50. package/dist/guide/timers.md +178 -0
  51. package/dist/guide/ui/accordion.md +93 -0
  52. package/dist/guide/ui/combobox.md +144 -0
  53. package/dist/guide/ui/dialog.md +124 -0
  54. package/dist/guide/ui/disclosure.md +60 -0
  55. package/dist/guide/ui/menu.md +125 -0
  56. package/dist/guide/ui/overview.md +166 -0
  57. package/dist/guide/ui/popover.md +101 -0
  58. package/dist/guide/ui/select.md +114 -0
  59. package/dist/guide/ui/tabs.md +161 -0
  60. package/dist/guide/ui/toaster.md +152 -0
  61. package/dist/guide/ui/tooltip.md +103 -0
  62. package/dist/guide/undo.md +178 -0
  63. package/dist/guide/virtual-collections.md +183 -0
  64. package/dist/guide/web-components.md +179 -0
  65. package/dist/guide/widgets.md +217 -0
  66. package/dist/index.cjs.js +15283 -6279
  67. package/dist/index.cjs.js.map +1 -0
  68. package/dist/index.d.ts +3739 -370
  69. package/dist/index.esm.js +15230 -6274
  70. package/dist/index.esm.js.map +1 -0
  71. package/dist/jsx-dev-runtime.cjs.js +27 -329
  72. package/dist/jsx-dev-runtime.cjs.js.map +1 -0
  73. package/dist/jsx-dev-runtime.esm.js +20 -322
  74. package/dist/jsx-dev-runtime.esm.js.map +1 -0
  75. package/dist/jsx-runtime.cjs.js +27 -329
  76. package/dist/jsx-runtime.cjs.js.map +1 -0
  77. package/dist/jsx-runtime.esm.js +20 -322
  78. package/dist/jsx-runtime.esm.js.map +1 -0
  79. package/dist/jsx.cjs.js +11 -309
  80. package/dist/jsx.cjs.js.map +1 -0
  81. package/dist/jsx.esm.js +9 -310
  82. package/dist/jsx.esm.js.map +1 -0
  83. package/dist/react.cjs.js +117 -0
  84. package/dist/react.cjs.js.map +1 -0
  85. package/dist/react.esm.js +115 -0
  86. package/dist/react.esm.js.map +1 -0
  87. package/dist/shims/globalthis.cjs +20 -0
  88. package/dist/sygnal.min.js +2 -1
  89. package/dist/sygnal.min.js.map +1 -0
  90. package/dist/ui-combobox.cjs.js +169 -0
  91. package/dist/ui-combobox.cjs.js.map +1 -0
  92. package/dist/ui-combobox.esm.js +148 -0
  93. package/dist/ui-combobox.esm.js.map +1 -0
  94. package/dist/ui-menu.cjs.js +87 -0
  95. package/dist/ui-menu.cjs.js.map +1 -0
  96. package/dist/ui-menu.esm.js +66 -0
  97. package/dist/ui-menu.esm.js.map +1 -0
  98. package/dist/ui-select.cjs.js +104 -0
  99. package/dist/ui-select.cjs.js.map +1 -0
  100. package/dist/ui-select.esm.js +83 -0
  101. package/dist/ui-select.esm.js.map +1 -0
  102. package/dist/ui.cjs.js +738 -0
  103. package/dist/ui.cjs.js.map +1 -0
  104. package/dist/ui.esm.js +727 -0
  105. package/dist/ui.esm.js.map +1 -0
  106. package/dist/vike/ClientOnly.cjs.js +5 -14
  107. package/dist/vike/ClientOnly.cjs.js.map +1 -0
  108. package/dist/vike/ClientOnly.mjs +5 -14
  109. package/dist/vike/ClientOnly.mjs.map +1 -0
  110. package/dist/vike/{+config.js → config/+config.js} +10 -1
  111. package/dist/vike/config/+config.js.map +1 -0
  112. package/dist/vike/config/package.json +6 -0
  113. package/dist/vike/onRenderClient.cjs.js +166 -95
  114. package/dist/vike/onRenderClient.cjs.js.map +1 -0
  115. package/dist/vike/onRenderClient.mjs +166 -95
  116. package/dist/vike/onRenderClient.mjs.map +1 -0
  117. package/dist/vike/onRenderHtml.cjs.js +60 -24
  118. package/dist/vike/onRenderHtml.cjs.js.map +1 -0
  119. package/dist/vike/onRenderHtml.mjs +61 -25
  120. package/dist/vike/onRenderHtml.mjs.map +1 -0
  121. package/dist/vite/plugin.cjs.js +926 -65
  122. package/dist/vite/plugin.cjs.js.map +1 -0
  123. package/dist/vite/plugin.mjs +925 -65
  124. package/dist/vite/plugin.mjs.map +1 -0
  125. package/dist/zag.cjs.js +408 -0
  126. package/dist/zag.cjs.js.map +1 -0
  127. package/dist/zag.esm.js +405 -0
  128. package/dist/zag.esm.js.map +1 -0
  129. package/llms.txt +313 -0
  130. package/package.json +109 -16
  131. package/src/astro/client.ts +40 -18
  132. package/src/astro/index.d.ts +48 -1
  133. package/src/astro/index.ts +106 -9
  134. package/src/astro/server.ts +11 -3
  135. package/src/collection.ts +6 -90
  136. package/src/core/actions.ts +134 -0
  137. package/src/core/cell.ts +202 -0
  138. package/src/core/debug.ts +23 -0
  139. package/src/core/define.ts +242 -0
  140. package/src/core/hooks.ts +314 -0
  141. package/src/core/hosts/collection.ts +325 -0
  142. package/src/core/hosts/switchable.ts +146 -0
  143. package/src/core/instance.ts +592 -0
  144. package/src/core/markers/clientonly.ts +13 -0
  145. package/src/core/markers/lazy.ts +40 -0
  146. package/src/core/markers/portal.ts +96 -0
  147. package/src/core/markers/suspense.ts +41 -0
  148. package/src/core/markers/transition.ts +70 -0
  149. package/src/core/registry.ts +25 -0
  150. package/src/core/runtime.ts +573 -0
  151. package/src/core/statics.ts +104 -0
  152. package/src/core/teardown.ts +60 -0
  153. package/src/core/view.ts +37 -0
  154. package/src/cycle/dom/DocumentDOMSource.ts +13 -8
  155. package/src/cycle/dom/ElementFinder.ts +7 -9
  156. package/src/cycle/dom/EventDelegator.ts +174 -223
  157. package/src/cycle/dom/IsolateModule.ts +65 -35
  158. package/src/cycle/dom/MainDOMSource.ts +13 -2
  159. package/src/cycle/dom/PriorityQueue.ts +4 -2
  160. package/src/cycle/dom/SymbolTree.ts +18 -47
  161. package/src/cycle/dom/classNameModule.ts +76 -0
  162. package/src/cycle/dom/controlledInputModule.ts +50 -0
  163. package/src/cycle/dom/enrichEventStream.ts +29 -45
  164. package/src/cycle/dom/fragment.ts +7 -0
  165. package/src/cycle/dom/isolate.ts +30 -27
  166. package/src/cycle/dom/makeDOMDriver.ts +115 -57
  167. package/src/cycle/dom/mockDOMSource.ts +83 -9
  168. package/src/cycle/dom/modules.ts +21 -4
  169. package/src/cycle/dom/propsModule.ts +65 -0
  170. package/src/cycle/dom/selectModule.ts +22 -17
  171. package/src/cycle/dom/snabbdom.ts +1 -6
  172. package/src/cycle/dom/thunk.ts +3 -0
  173. package/src/cycle/dom/utils.ts +98 -0
  174. package/src/cycle/dom/viewTransition.ts +43 -0
  175. package/src/cycle/state/StateSource.ts +20 -4
  176. package/src/cycle/state/objIsEqual.ts +50 -0
  177. package/src/cycle/state/types.ts +0 -10
  178. package/src/defineComponent.ts +34 -0
  179. package/src/devtools.d.ts +151 -0
  180. package/src/devtools.ts +34 -0
  181. package/src/element.d.ts +59 -0
  182. package/src/element.ts +235 -0
  183. package/src/extra/backoff.ts +9 -0
  184. package/src/extra/behaviors.ts +169 -0
  185. package/src/extra/browserSignals.ts +23 -0
  186. package/src/extra/browserSources.ts +326 -0
  187. package/src/extra/command.ts +3 -3
  188. package/src/extra/controls.ts +47 -0
  189. package/src/extra/copyAsTest.ts +286 -0
  190. package/src/extra/devtools.ts +109 -31
  191. package/src/extra/devtoolsActions.ts +379 -0
  192. package/src/extra/devtoolsHook.ts +9 -0
  193. package/src/extra/devtoolsNext.ts +83 -0
  194. package/src/extra/diagnostics/checks/actionLog.ts +108 -0
  195. package/src/extra/diagnostics/checks/behaviors.ts +49 -0
  196. package/src/extra/diagnostics/checks/browserSources.ts +96 -0
  197. package/src/extra/diagnostics/checks/collections.ts +67 -0
  198. package/src/extra/diagnostics/checks/controls.ts +148 -0
  199. package/src/extra/diagnostics/checks/dataset.ts +57 -0
  200. package/src/extra/diagnostics/checks/dom.ts +250 -0
  201. package/src/extra/diagnostics/checks/elementCommands.ts +175 -0
  202. package/src/extra/diagnostics/checks/events.ts +83 -0
  203. package/src/extra/diagnostics/checks/fetch.ts +135 -0
  204. package/src/extra/diagnostics/checks/forms.ts +163 -0
  205. package/src/extra/diagnostics/checks/index.ts +202 -0
  206. package/src/extra/diagnostics/checks/inspect.ts +427 -0
  207. package/src/extra/diagnostics/checks/next.ts +417 -0
  208. package/src/extra/diagnostics/checks/persist.ts +54 -0
  209. package/src/extra/diagnostics/checks/props.ts +56 -0
  210. package/src/extra/diagnostics/checks/public.d.ts +302 -0
  211. package/src/extra/diagnostics/checks/replies.ts +128 -0
  212. package/src/extra/diagnostics/checks/router.ts +100 -0
  213. package/src/extra/diagnostics/checks/rxjsHints.ts +88 -0
  214. package/src/extra/diagnostics/checks/shared.ts +203 -0
  215. package/src/extra/diagnostics/checks/shorthand.ts +115 -0
  216. package/src/extra/diagnostics/checks/sortable.ts +129 -0
  217. package/src/extra/diagnostics/checks/state.ts +148 -0
  218. package/src/extra/diagnostics/checks/statics.ts +48 -0
  219. package/src/extra/diagnostics/checks/strict.ts +55 -0
  220. package/src/extra/diagnostics/checks/timers.ts +80 -0
  221. package/src/extra/diagnostics/checks/viewTransitions.ts +47 -0
  222. package/src/extra/diagnostics/checks/virtual.ts +73 -0
  223. package/src/extra/diagnostics/checks/widgets.ts +109 -0
  224. package/src/extra/diagnostics/checks/wiring.ts +121 -0
  225. package/src/extra/diagnostics/codes.ts +477 -0
  226. package/src/extra/diagnostics/index.ts +353 -0
  227. package/src/extra/diagnostics/legacy.ts +76 -0
  228. package/src/extra/driverFactories.ts +172 -60
  229. package/src/extra/elementCommands.ts +53 -0
  230. package/src/extra/eventDriver.ts +4 -0
  231. package/src/extra/fetchDriver.ts +519 -0
  232. package/src/extra/flatten.ts +75 -0
  233. package/src/extra/focusWithin.ts +27 -0
  234. package/src/extra/form.ts +268 -0
  235. package/src/extra/formHelpers.ts +139 -0
  236. package/src/extra/head.ts +106 -0
  237. package/src/extra/hmr.ts +2 -3
  238. package/src/extra/owned.ts +24 -0
  239. package/src/extra/pager.ts +50 -0
  240. package/src/extra/persist.ts +133 -0
  241. package/src/extra/pwa.ts +1 -1
  242. package/src/extra/queryCache.ts +107 -0
  243. package/src/extra/reducers.ts +13 -6
  244. package/src/extra/reduxDevtools.ts +92 -0
  245. package/src/extra/replies.ts +54 -0
  246. package/src/extra/router.ts +323 -0
  247. package/src/extra/run.ts +94 -210
  248. package/src/extra/selection.ts +76 -0
  249. package/src/extra/socketDriver.ts +265 -0
  250. package/src/extra/sortable.ts +377 -0
  251. package/src/extra/ssr.ts +427 -160
  252. package/src/extra/standardSchema.ts +27 -0
  253. package/src/extra/testing.ts +3158 -190
  254. package/src/extra/timers.ts +95 -0
  255. package/src/extra/undo.ts +261 -0
  256. package/src/extra/viewTransitions.ts +38 -0
  257. package/src/extra/virtual.ts +513 -0
  258. package/src/extra/widget.ts +201 -0
  259. package/src/extra/xstreamExtras.ts +269 -0
  260. package/src/index.d.ts +3012 -87
  261. package/src/index.ts +32 -10
  262. package/src/jsx-runtime.ts +15 -1
  263. package/src/jsx.ts +2 -1
  264. package/src/lazy.ts +50 -7
  265. package/src/portal.ts +3 -1
  266. package/src/pragma/index.ts +262 -134
  267. package/src/react-peers.d.ts +5 -0
  268. package/src/react.d.ts +43 -0
  269. package/src/react.ts +110 -0
  270. package/src/shared.ts +48 -0
  271. package/src/slot.ts +1 -1
  272. package/src/suspense.ts +3 -1
  273. package/src/switchable.ts +6 -119
  274. package/src/transition.ts +3 -1
  275. package/src/ui/accordion.ts +64 -0
  276. package/src/ui/dialog.ts +129 -0
  277. package/src/ui/disclosure.ts +35 -0
  278. package/src/ui/popover.ts +45 -0
  279. package/src/ui/shared.ts +94 -0
  280. package/src/ui/tabs.ts +82 -0
  281. package/src/ui/toaster.ts +198 -0
  282. package/src/ui/tooltip.ts +70 -0
  283. package/src/ui/zag/combobox.ts +115 -0
  284. package/src/ui/zag/menu.ts +40 -0
  285. package/src/ui/zag/select.ts +54 -0
  286. package/src/ui/zag/shared.ts +43 -0
  287. package/src/ui-combobox.d.ts +20 -0
  288. package/src/ui-combobox.ts +6 -0
  289. package/src/ui-menu.d.ts +30 -0
  290. package/src/ui-menu.ts +6 -0
  291. package/src/ui-select.d.ts +17 -0
  292. package/src/ui-select.ts +6 -0
  293. package/src/ui-zag-types.d.ts +42 -0
  294. package/src/ui.d.ts +208 -0
  295. package/src/ui.ts +15 -0
  296. package/src/vike/+config.ts +9 -1
  297. package/src/vike/ClientOnly.ts +1 -1
  298. package/src/vike/onRenderClient.ts +144 -96
  299. package/src/vike/onRenderHtml.ts +69 -26
  300. package/src/vike/types.ts +27 -4
  301. package/src/vite/globalthis-shim.ts +18 -0
  302. package/src/vite/globalthis.ts +56 -0
  303. package/src/vite/plugin.d.ts +114 -2
  304. package/src/vite/plugin.ts +920 -56
  305. package/src/zag.d.ts +64 -0
  306. package/src/zag.ts +238 -0
  307. package/dist/astro/astro/client.d.ts +0 -5
  308. package/dist/astro/astro/index.d.ts +0 -16
  309. package/dist/astro/astro/server.d.ts +0 -12
  310. package/dist/astro/client.d.ts +0 -5
  311. package/dist/astro/client.esm.js +0 -4665
  312. package/dist/astro/collection.d.ts +0 -12
  313. package/dist/astro/component.d.ts +0 -22
  314. package/dist/astro/cycle/dom/BodyDOMSource.d.ts +0 -10
  315. package/dist/astro/cycle/dom/DOMSource.d.ts +0 -11
  316. package/dist/astro/cycle/dom/DocumentDOMSource.d.ts +0 -12
  317. package/dist/astro/cycle/dom/ElementFinder.d.ts +0 -8
  318. package/dist/astro/cycle/dom/EventDelegator.d.ts +0 -34
  319. package/dist/astro/cycle/dom/IsolateModule.d.ts +0 -23
  320. package/dist/astro/cycle/dom/MainDOMSource.d.ts +0 -32
  321. package/dist/astro/cycle/dom/PriorityQueue.d.ts +0 -7
  322. package/dist/astro/cycle/dom/ScopeChecker.d.ts +0 -9
  323. package/dist/astro/cycle/dom/SymbolTree.d.ts +0 -9
  324. package/dist/astro/cycle/dom/VNodeWrapper.d.ts +0 -8
  325. package/dist/astro/cycle/dom/enrichEventStream.d.ts +0 -24
  326. package/dist/astro/cycle/dom/fromEvent.d.ts +0 -6
  327. package/dist/astro/cycle/dom/index.d.ts +0 -8
  328. package/dist/astro/cycle/dom/isolate.d.ts +0 -9
  329. package/dist/astro/cycle/dom/makeDOMDriver.d.ts +0 -11
  330. package/dist/astro/cycle/dom/mockDOMSource.d.ts +0 -17
  331. package/dist/astro/cycle/dom/modules.d.ts +0 -5
  332. package/dist/astro/cycle/dom/snabbdom.d.ts +0 -22
  333. package/dist/astro/cycle/dom/styleModule.d.ts +0 -13
  334. package/dist/astro/cycle/dom/thunk.d.ts +0 -11
  335. package/dist/astro/cycle/dom/utils.d.ts +0 -8
  336. package/dist/astro/cycle/isolate/index.d.ts +0 -42
  337. package/dist/astro/cycle/run/adapt.d.ts +0 -6
  338. package/dist/astro/cycle/run/index.d.ts +0 -36
  339. package/dist/astro/cycle/run/internals.d.ts +0 -8
  340. package/dist/astro/cycle/run/types.d.ts +0 -49
  341. package/dist/astro/cycle/state/Collection.d.ts +0 -29
  342. package/dist/astro/cycle/state/StateSource.d.ts +0 -20
  343. package/dist/astro/cycle/state/index.d.ts +0 -5
  344. package/dist/astro/cycle/state/pickCombine.d.ts +0 -3
  345. package/dist/astro/cycle/state/pickMerge.d.ts +0 -3
  346. package/dist/astro/cycle/state/types.d.ts +0 -18
  347. package/dist/astro/cycle/state/withState.d.ts +0 -17
  348. package/dist/astro/extra/classes.d.ts +0 -7
  349. package/dist/astro/extra/devtools.d.ts +0 -78
  350. package/dist/astro/extra/dragDriver.d.ts +0 -20
  351. package/dist/astro/extra/driverFactories.d.ts +0 -12
  352. package/dist/astro/extra/eventDriver.d.ts +0 -9
  353. package/dist/astro/extra/exactState.d.ts +0 -1
  354. package/dist/astro/extra/hmr.d.ts +0 -17
  355. package/dist/astro/extra/logDriver.d.ts +0 -2
  356. package/dist/astro/extra/processDrag.d.ts +0 -19
  357. package/dist/astro/extra/processForm.d.ts +0 -15
  358. package/dist/astro/extra/run.d.ts +0 -13
  359. package/dist/astro/extra/xstreamCompat.d.ts +0 -5
  360. package/dist/astro/index.d.ts +0 -19
  361. package/dist/astro/index.esm.js +0 -27
  362. package/dist/astro/jsx-dev-runtime.d.ts +0 -1
  363. package/dist/astro/jsx-runtime.d.ts +0 -4
  364. package/dist/astro/jsx.d.ts +0 -2
  365. package/dist/astro/pragma/fn.d.ts +0 -7
  366. package/dist/astro/pragma/index.d.ts +0 -7
  367. package/dist/astro/pragma/is.d.ts +0 -10
  368. package/dist/astro/server.d.ts +0 -12
  369. package/dist/astro/server.esm.js +0 -25
  370. package/dist/astro/switchable.d.ts +0 -7
  371. package/dist/collection.d.ts +0 -12
  372. package/dist/component.d.ts +0 -22
  373. package/dist/cycle/dom/BodyDOMSource.d.ts +0 -10
  374. package/dist/cycle/dom/DOMSource.d.ts +0 -11
  375. package/dist/cycle/dom/DocumentDOMSource.d.ts +0 -12
  376. package/dist/cycle/dom/ElementFinder.d.ts +0 -8
  377. package/dist/cycle/dom/EventDelegator.d.ts +0 -34
  378. package/dist/cycle/dom/IsolateModule.d.ts +0 -23
  379. package/dist/cycle/dom/MainDOMSource.d.ts +0 -32
  380. package/dist/cycle/dom/PriorityQueue.d.ts +0 -7
  381. package/dist/cycle/dom/ScopeChecker.d.ts +0 -9
  382. package/dist/cycle/dom/SymbolTree.d.ts +0 -9
  383. package/dist/cycle/dom/VNodeWrapper.d.ts +0 -8
  384. package/dist/cycle/dom/enrichEventStream.d.ts +0 -24
  385. package/dist/cycle/dom/fromEvent.d.ts +0 -6
  386. package/dist/cycle/dom/index.d.ts +0 -8
  387. package/dist/cycle/dom/isolate.d.ts +0 -9
  388. package/dist/cycle/dom/makeDOMDriver.d.ts +0 -11
  389. package/dist/cycle/dom/mockDOMSource.d.ts +0 -17
  390. package/dist/cycle/dom/modules.d.ts +0 -5
  391. package/dist/cycle/dom/snabbdom.d.ts +0 -22
  392. package/dist/cycle/dom/styleModule.d.ts +0 -13
  393. package/dist/cycle/dom/thunk.d.ts +0 -11
  394. package/dist/cycle/dom/utils.d.ts +0 -8
  395. package/dist/cycle/isolate/index.d.ts +0 -42
  396. package/dist/cycle/run/adapt.d.ts +0 -6
  397. package/dist/cycle/run/index.d.ts +0 -36
  398. package/dist/cycle/run/internals.d.ts +0 -8
  399. package/dist/cycle/run/types.d.ts +0 -49
  400. package/dist/cycle/state/Collection.d.ts +0 -29
  401. package/dist/cycle/state/StateSource.d.ts +0 -20
  402. package/dist/cycle/state/index.d.ts +0 -5
  403. package/dist/cycle/state/pickCombine.d.ts +0 -3
  404. package/dist/cycle/state/pickMerge.d.ts +0 -3
  405. package/dist/cycle/state/types.d.ts +0 -18
  406. package/dist/cycle/state/withState.d.ts +0 -17
  407. package/dist/extra/classes.d.ts +0 -7
  408. package/dist/extra/devtools.d.ts +0 -78
  409. package/dist/extra/dragDriver.d.ts +0 -20
  410. package/dist/extra/driverFactories.d.ts +0 -12
  411. package/dist/extra/eventDriver.d.ts +0 -9
  412. package/dist/extra/exactState.d.ts +0 -1
  413. package/dist/extra/hmr.d.ts +0 -17
  414. package/dist/extra/logDriver.d.ts +0 -2
  415. package/dist/extra/processDrag.d.ts +0 -19
  416. package/dist/extra/processForm.d.ts +0 -15
  417. package/dist/extra/run.d.ts +0 -13
  418. package/dist/extra/xstreamCompat.d.ts +0 -5
  419. package/dist/jsx-dev-runtime.d.ts +0 -1
  420. package/dist/jsx-dev-runtime.js +0 -224
  421. package/dist/jsx-runtime.d.ts +0 -4
  422. package/dist/jsx-runtime.js +0 -224
  423. package/dist/jsx.d.ts +0 -2
  424. package/dist/pragma/fn.d.ts +0 -7
  425. package/dist/pragma/index.d.ts +0 -7
  426. package/dist/pragma/is.d.ts +0 -10
  427. package/dist/switchable.d.ts +0 -7
  428. package/dist/vike/+config.cjs.js +0 -65
  429. package/src/component.ts +0 -2256
  430. package/src/cycle/isolate/index.ts +0 -196
  431. package/src/cycle/run/index.ts +0 -151
  432. package/src/cycle/run/internals.ts +0 -143
  433. package/src/cycle/state/Collection.ts +0 -174
  434. package/src/cycle/state/index.ts +0 -5
  435. package/src/cycle/state/pickCombine.ts +0 -167
  436. package/src/cycle/state/pickMerge.ts +0 -122
  437. package/src/cycle/state/withState.ts +0 -47
  438. package/src/pragma/fn.ts +0 -55
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Shared plumbing for the runtime checks (PLAN-1 workstream 1A).
3
+ *
4
+ * The checks are bundled separately from the main 'sygnal' entry
5
+ * ('sygnal/diagnostics'). They must talk to the SAME diagnostics core
6
+ * instance the components use, so they never import the core's functions
7
+ * directly: `import '../index'` only makes sure the core is loaded (in the
8
+ * dist build that import is rewritten to the external 'sygnal' package, see
9
+ * rollup.config.mjs), and the functions are read from the bridge object the
10
+ * core publishes on globalThis.__SYGNAL_DIAGNOSTICS__.
11
+ */
12
+ import '../index'
13
+ import type {DiagnosticCheck, DiagnosticDetails, Diagnostic} from '../index'
14
+ import {CODE_TITLES, DEV_CODE_SEVERITY, registerCodes} from '../codes'
15
+ import type {DiagnosticSeverity} from '../codes'
16
+
17
+ interface CoreBridge {
18
+ registerCheck(check: DiagnosticCheck): () => void
19
+ report(code: string, details: DiagnosticDetails): Diagnostic | undefined
20
+ [key: string]: any
21
+ }
22
+
23
+ export const bridge = (): CoreBridge => (globalThis as any).__SYGNAL_DIAGNOSTICS__
24
+
25
+ export const report = (code: string, details: DiagnosticDetails): Diagnostic | undefined =>
26
+ bridge().report(code, details)
27
+
28
+ /**
29
+ * report() for code that is NOT running inside a core hook (timers, wrapped
30
+ * methods): never throws synchronously; an 'error'-mode DiagnosticError is
31
+ * rethrown asynchronously, like the core does for hooks.
32
+ */
33
+ export const reportSafely = (code: string, details: DiagnosticDetails): Diagnostic | undefined => {
34
+ try {
35
+ return report(code, details)
36
+ } catch (err) {
37
+ const raise = () => { throw err }
38
+ typeof queueMicrotask === 'function' ? queueMicrotask(raise) : setTimeout(raise)
39
+ return undefined
40
+ }
41
+ }
42
+
43
+ registerCodes(Object.keys(DEV_CODE_SEVERITY).map(code =>
44
+ [code, DEV_CODE_SEVERITY[code], CODE_TITLES[code]] as [string, DiagnosticSeverity, string]))
45
+
46
+ /**
47
+ * reportSafely() for the dev-entry-only codes (DEV_CODE_SEVERITY, G-143): the main bundle's
48
+ * core doesn't know their severity, so it is passed explicitly.
49
+ */
50
+ export const devReport = (code: string, details: DiagnosticDetails): Diagnostic | undefined =>
51
+ reportSafely(code, {severity: DEV_CODE_SEVERITY[code], ...details})
52
+
53
+ /** Tunables (test seam: configureChecks()). */
54
+ export const timing = {
55
+ /** delay after a render before DOM checks run (lets nested renders patch) */
56
+ settleMs: 50,
57
+ /** SYG103 escalates to warn after this much render-idle time ... */
58
+ idleMs: 2000,
59
+ /** ... and only once the component has rendered at least this often */
60
+ minRenders: 3,
61
+ }
62
+
63
+ /**
64
+ * Built-in actions Sygnal dispatches itself; never "missing" or "unreachable".
65
+ * (HYDRATE is not one since 6.0, D66: nothing dispatches it, so it is an ordinary action.)
66
+ */
67
+ export const BUILTIN_ACTIONS = new Set(['BOOTSTRAP', 'INITIALIZE', 'DISPOSE', 'READY', 'RESOURCE'])
68
+
69
+ /** Sinks the core handles itself: their values never go to a reply-action driver. */
70
+ // (PLAN-4 GS-2: ELEMENT too; `{ scrollIntoView: Row, block: 'center' }` names no action)
71
+ const NON_REPLY_SINK = /^(STATE|EFFECT|EVENTS|PARENT|READY|DOM|CHILD|ELEMENT|PERSIST)$/
72
+
73
+ // `ok: 'X'` / `"error": "X"` in function source (minified code keeps string literals and keys)
74
+ const keyedNames = (keys: string) => new RegExp(`(?:^|[{,\\s])["']?(?:${keys})["']?\\s*:\\s*(["'\`])([\\w$.:/-]+)\\1`, 'g')
75
+ // (PLAN-3 5-4b: and a router `{ block: 'ACTION' }`)
76
+ const REPLY_IN_SOURCE = keyedNames('ok|error|block')
77
+ const CONNECTION_IN_SOURCE = keyedNames('message|open|close|error')
78
+ // G-224: a `timers` static's `action: 'TICK'` / `frame: 'FRAME'`
79
+ const TIMER_IN_SOURCE = keyedNames('action|frame')
80
+ // PLAN-5 B-3: a `browser` static's `action: 'SEEN'` / `error: 'GEO_FAILED'`
81
+ const BROWSER_IN_SOURCE = keyedNames('action|error')
82
+
83
+ const namesIn = (fn: any, re: RegExp, out: Set<string>) => {
84
+ if (typeof fn !== 'function') return
85
+ let src = ''
86
+ try { src = Function.prototype.toString.call(fn) } catch (_) { return }
87
+ re.lastIndex = 0
88
+ for (let m = re.exec(src); m; m = re.exec(src)) out.add(m[2])
89
+ }
90
+
91
+ /**
92
+ * Reply actions a component's requests and `connections` static can name, read from the
93
+ * source of its non-STATE sink functions (`HTTP: (s) => ({ url, ok: 'LOADED', error: 'FAILED' })`)
94
+ * and of `connections` (message/open/close/error). A heuristic for SYG102 (PLAN-3): a name
95
+ * built at run time is missed (SYG102 stays info, so that only costs a false hint).
96
+ */
97
+ export function replyNamesOf(component: any): Set<string> {
98
+ const out = new Set<string>()
99
+ const model = component && component.model
100
+ if (model && typeof model === 'object') {
101
+ for (const key of Object.keys(model)) {
102
+ const value = model[key]
103
+ const bar = key.indexOf('|')
104
+ if (bar >= 0) {
105
+ if (!NON_REPLY_SINK.test(key.slice(bar + 1).trim())) namesIn(value, REPLY_IN_SOURCE, out)
106
+ } else if (value && typeof value === 'object') {
107
+ for (const sink of Object.keys(value)) {
108
+ if (!NON_REPLY_SINK.test(sink)) namesIn(value[sink], REPLY_IN_SOURCE, out)
109
+ }
110
+ }
111
+ }
112
+ }
113
+ namesIn(component?.view?.connections, CONNECTION_IN_SOURCE, out)
114
+ // G-224: makeTimerDriver() replies the actions the `timers` static names (a function of the
115
+ // state, or an object of them)
116
+ const timers = component?.view?.timers
117
+ if (timers && typeof timers === 'object') for (const k in timers) namesIn(timers[k], TIMER_IN_SOURCE, out)
118
+ else namesIn(timers, TIMER_IN_SOURCE, out)
119
+ // PLAN-5 B-3: the browser driver replies the actions the `browser` static names
120
+ const browser = component?.view?.browser
121
+ if (browser && typeof browser === 'object') for (const k in browser) namesIn(browser[k], BROWSER_IN_SOURCE, out)
122
+ else namesIn(browser, BROWSER_IN_SOURCE, out)
123
+ // PLAN-3 5-4b: the router replies the action a `route` static names
124
+ const route = component?.view?.route
125
+ if (typeof route === 'string') out.add(route)
126
+ // PLAN-4 GS-5: persist() adds RESTORE (sent after hydration and by sync)
127
+ if (component?.view?.persist) out.add('RESTORE')
128
+ return out
129
+ }
130
+
131
+ /** Reply actions a component instance's requests were seen naming (replies.ts → inspect). */
132
+ export const replySeen = new WeakMap<object, Set<string>>()
133
+
134
+ /** Synthetic/internal actions (`__TEST_ACTION__`, `__NOOP_ACTION__`, ...) and built-ins. */
135
+ export const isInternalAction = (action: string): boolean =>
136
+ typeof action !== 'string' || action.startsWith('__') || BUILTIN_ACTIONS.has(action)
137
+
138
+ let seen = new Set<string>()
139
+
140
+ /** true the first time `key` is seen (dedupes reports, e.g. per component name). */
141
+ export const once = (key: string): boolean => (seen.has(key) ? false : (seen.add(key), true))
142
+ /** undo once(key): the report it guarded wasn't made (G-470: diagnostics off) */
143
+ export const unsee = (key: string): void => void seen.delete(key)
144
+ /** whether once(`key`) has reported already (a check can skip its work: SYG435) */
145
+ export const onced = (key: string): boolean => seen.has(key)
146
+
147
+ /**
148
+ * Forget only the once() dedupe set (G-051: renderComponent calls it through the core bridge
149
+ * when an outermost instance starts, so findings are reported again in every test). Unlike
150
+ * resetCheckState() it keeps the checks' registries, e.g. inspect()'s live records.
151
+ */
152
+ export const resetOnce = (): void => { seen = new Set() }
153
+ {
154
+ const core = bridge()
155
+ if (core) core.resetOnce = resetOnce
156
+ }
157
+
158
+ const resetters: Array<() => void> = []
159
+ export const onReset = (fn: () => void): void => { resetters.push(fn) }
160
+
161
+ /** Clear every check's dedupe/registry state (tests). */
162
+ export function resetCheckState(): void {
163
+ seen = new Set()
164
+ resetters.forEach(fn => fn())
165
+ }
166
+
167
+ export const nameOf = (component: any): string => (component && component.name) || 'component'
168
+
169
+ export const isPlainObject = (value: any): boolean =>
170
+ !!value && typeof value === 'object' && !Array.isArray(value) &&
171
+ (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)
172
+
173
+ /** Levenshtein distance (small strings only). */
174
+ function distance(a: string, b: string): number {
175
+ const row = Array.from({length: b.length + 1}, (_, i) => i)
176
+ for (let i = 1; i <= a.length; i++) {
177
+ let prev = row[0]
178
+ row[0] = i
179
+ for (let j = 1; j <= b.length; j++) {
180
+ const tmp = row[j]
181
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1))
182
+ prev = tmp
183
+ }
184
+ }
185
+ return row[b.length]
186
+ }
187
+
188
+ /** Closest candidate to `name` (case-insensitive match or edit distance <= 2), if any. */
189
+ export function suggest(name: string, candidates: Iterable<string>): string | undefined {
190
+ let best: string | undefined
191
+ let bestScore = Infinity
192
+ for (const c of candidates) {
193
+ if (c === name) continue
194
+ const score = c.toLowerCase() === name.toLowerCase() ? 0 : distance(c, name)
195
+ if (score < bestScore) { best = c; bestScore = score }
196
+ }
197
+ return bestScore <= Math.min(2, Math.floor(name.length / 3)) ? best : undefined
198
+ }
199
+
200
+ export const didYouMean = (name: string, candidates: Iterable<string>): string => {
201
+ const s = suggest(name, candidates)
202
+ return s ? ` (did you mean '${s}'?)` : ''
203
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * SYG115 — unknown DOM event shorthand (warn, G-143).
3
+ *
4
+ * The core's DOM source Proxy (the DOM source wrapper) turns ANY unknown property
5
+ * into an event shorthand: `DOM.<name>(sel)` = `DOM.select(sel).events('<name>')`. A made-up
6
+ * name such as `DOM.key('.x')` or `DOM.enter('.x')` therefore listens for an event the browser
7
+ * never fires, and the action silently never happens.
8
+ *
9
+ * Mechanism: the `sources` hook (called before each component's intent) wraps the DOM source
10
+ * in a second Proxy. Calling a shorthand whose name is not a known DOM event reports SYG115
11
+ * (once per component name and event), then behaves exactly like the core shorthand. Only the
12
+ * call is reported, not the property read, so tools that inspect the source (pretty-printers)
13
+ * don't trigger it. `DOM.select(sel).events(name)` is never checked: it is the explicit form
14
+ * for custom events.
15
+ */
16
+ import type {DiagnosticCheck} from '../index'
17
+ import {devReport, once, nameOf, suggest} from './shared'
18
+
19
+ // Standard DOM / UI event names (lower case). In a browser, `on<name>` on an element or the
20
+ // window also counts, so newer events are accepted there too.
21
+ const KNOWN = new Set((
22
+ 'abort afterprint animationcancel animationend animationiteration animationstart auxclick ' +
23
+ 'beforeinput beforeprint beforetoggle beforeunload blur cancel canplay canplaythrough change click close ' +
24
+ 'compositionend compositionstart compositionupdate contextlost contextmenu contextrestored copy cuechange cut ' +
25
+ 'dblclick drag dragend dragenter dragleave dragover dragstart drop durationchange emptied ended error ' +
26
+ 'focus focusin focusout formdata fullscreenchange fullscreenerror gotpointercapture hashchange input invalid ' +
27
+ 'keydown keypress keyup languagechange load loadeddata loadedmetadata loadstart lostpointercapture message ' +
28
+ 'messageerror mousedown mouseenter mouseleave mousemove mouseout mouseover mouseup offline online pagehide ' +
29
+ 'pageshow paste pause play playing pointercancel pointerdown pointerenter pointerleave pointermove pointerout ' +
30
+ 'pointerover pointerrawupdate pointerup popstate progress ratechange rejectionhandled reset resize scroll ' +
31
+ 'scrollend search securitypolicyviolation seeked seeking select selectionchange selectstart slotchange stalled ' +
32
+ 'storage submit suspend timeupdate toggle touchcancel touchend touchmove touchstart transitioncancel ' +
33
+ 'transitionend transitionrun transitionstart unhandledrejection unload visibilitychange volumechange waiting wheel'
34
+ ).split(' '))
35
+
36
+ const isKnown = (name: string): boolean => {
37
+ // PLAN-5 0-S6: a hyphenated name is a custom element's event (DOM['wa-hover']('.food')): no
38
+ // standard DOM event has a hyphen, and a bracketed name is no typo for one
39
+ if (KNOWN.has(name) || name.includes('-')) return true
40
+ const on = 'on' + name
41
+ const g: any = globalThis
42
+ try {
43
+ return !!((g.HTMLElement && on in g.HTMLElement.prototype) || (g.window && on in g.window) ||
44
+ (g.Document && on in g.Document.prototype))
45
+ } catch (_) { return false }
46
+ }
47
+
48
+ // Names agents reach for that are enriched-stream methods or key names, not events
49
+ const KEY_FIX = (sel: string, key?: string) =>
50
+ `Listen for the real event and read the key: DOM.keydown(${sel}).key()` +
51
+ (key ? `.filter(k => k === '${key}')` : '')
52
+ const SPECIAL: Record<string, (sel: string) => string> = {
53
+ key: sel => KEY_FIX(sel),
54
+ keys: sel => KEY_FIX(sel),
55
+ enter: sel => KEY_FIX(sel, 'Enter'),
56
+ escape: sel => KEY_FIX(sel, 'Escape'),
57
+ esc: sel => KEY_FIX(sel, 'Escape'),
58
+ value: sel => `Listen for the real event and read the value: DOM.input(${sel}).value()`,
59
+ checked: sel => `Listen for the real event and read the box: DOM.change(${sel}).checked()`,
60
+ data: sel => `Listen for the real event and read the attribute: DOM.click(${sel}).data('name')`,
61
+ doubleclick: sel => `Use DOM.dblclick(${sel})`,
62
+ }
63
+
64
+ const fixFor = (name: string, selector: any): string => {
65
+ const sel = typeof selector === 'string' ? `'${selector}'` : 'sel'
66
+ const special = SPECIAL[name.toLowerCase()]
67
+ if (special) return special(sel)
68
+ const s = suggest(name, KNOWN)
69
+ return (s ? `Did you mean DOM.${s}(${sel})? ` : '') +
70
+ `If '${name}' is a custom event you dispatch yourself, use the explicit form DOM.select(${sel}).events('${name}')`
71
+ }
72
+
73
+ const wrapped = new WeakMap<object, any>()
74
+
75
+ function checkedDOM(component: any, dom: any): any {
76
+ if (!dom || typeof dom != 'object') return dom
77
+ let proxy = wrapped.get(dom)
78
+ if (proxy) return proxy
79
+ const name = nameOf(component)
80
+ proxy = new Proxy(dom, {
81
+ get(t: any, k: any) {
82
+ const v = t[k]
83
+ if (typeof k != 'string' || k in t || typeof v != 'function' || isKnown(k)) return v
84
+ return function (this: any, selector: any, ...rest: any[]) {
85
+ if (once(`SYG115:${name}:${k}`)) {
86
+ devReport('SYG115', {
87
+ component,
88
+ message: `${name}.intent uses DOM.${k}(${typeof selector == 'string' ? `'${selector}'` : '…'}), but '${k}' is not a DOM event, so it never fires`,
89
+ fix: fixFor(k, selector),
90
+ data: {event: k, selector},
91
+ })
92
+ }
93
+ return v.call(this, selector, ...rest)
94
+ }
95
+ },
96
+ })
97
+ wrapped.set(dom, proxy)
98
+ return proxy
99
+ }
100
+
101
+ export const shorthandCheck: DiagnosticCheck = {
102
+ id: 'shorthand',
103
+
104
+ sources(component, sources) {
105
+ if (!sources || typeof Proxy != 'function') return
106
+ const domName = component?.DOMSourceName || 'DOM'
107
+ if (!sources[domName]) return
108
+ return new Proxy(sources, {
109
+ get(t: any, k: any) {
110
+ const v = t[k]
111
+ return k === domName ? checkedDOM(component, v) : v
112
+ },
113
+ })
114
+ },
115
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * PLAN-5 B-1 (3-D): `sortable` behavior diagnostics (dev entry; src/extra/sortable.ts carries no
3
+ * text). The behavior calls `__SYGNAL_DIAGNOSTICS__.sortable(code, ...args)`:
4
+ * 145 (item element, item selector, attr) a press or key on an item whose element has no
5
+ * `attr`: SYG145 (warn, once per selector); the event is ignored
6
+ * 146 (host root, item, handle, attr) the host's first pointerdown / keydown: SYG146 (warn,
7
+ * once per selector) when elements carry `attr` under the root but none matches `item`,
8
+ * or items exist but none contains `handle`. Returns true once it could decide (an empty
9
+ * list can't tell: the check runs again at the next interaction)
10
+ * 147 (from key, host state, uses key) when the host starts: a `from` key whose value isn't
11
+ * an array: SYG147 (warn, once per key)
12
+ * 435 (3-H G-449) (fn, uses key, from keys, idOf): the host's own items' ids as shown, per
13
+ * container (fn() returns [the item elements, the root]; the behavior only asks in dev), or nothing when
14
+ * every list has been reported (3-L G-478: no scan). A row's place is its `data-index`
15
+ * when it has one (VirtualCollection's pinned focused row is out of DOM order). (shown
16
+ * groups, host state, from keys, idField, uses key) at a keyboard pick-up or a pointer
17
+ * press: SYG435 (warn, once per uses key and list) when a list's shown items (those in
18
+ * the container holding most of them: an element elsewhere with a matching id is no list
19
+ * item; a row's container is its nearest ancestor holding another row, 3-O G-505) aren't
20
+ * one run of the array in its order (a Collection's sort, or a filter hiding entries
21
+ * between shown ones)
22
+ */
23
+ import {devReport, once, onced, suggest} from './shared'
24
+
25
+ const all = (root: any, sel: string): any[] => {
26
+ try { return [...root.querySelectorAll(sel)] } catch (_) { return [] }
27
+ }
28
+
29
+ export function reportSortable(code: number, a?: any, b?: any, c?: any, d?: any, e?: any): any {
30
+ if (code == 145) {
31
+ if (once(`SYG145:${b}:${c}`)) devReport('SYG145', {
32
+ component: 'sortable',
33
+ message: `sortable: an item ('${b}') has no ${c} attribute, so it can't be moved (the behavior finds each item in the list by that id)`,
34
+ fix: `Render the item's id on its element: <li className="${String(b).replace(/^\./, '')}" ${c}={item.id}>, or name the attribute: sortable({ ..., attr: 'data-key' })`,
35
+ data: {item: b, attr: c},
36
+ })
37
+ return
38
+ }
39
+ if (code == 146) {
40
+ const root = a, item = b, handle = c, attr = d
41
+ if (!root?.querySelectorAll) return true
42
+ const items = all(root, item).filter(el => el !== root)
43
+ if (!items.length) {
44
+ // nothing carries the id attribute either: an empty list (decide later)
45
+ if (!all(root, `[${attr}]`).length) return false
46
+ report146('item', item, `no element under the host matches it, though elements with ${attr} are rendered`, `Use the selector of the items' elements (the element with ${attr}), e.g. item: '.task'`)
47
+ return true
48
+ }
49
+ if (handle && !items.some(el => all(el, handle).length)) {
50
+ report146('handle', handle, `no item ('${item}') contains an element that matches it`, `Use the selector of the drag handle inside each item, e.g. handle: '.grip', or leave handle out to drag the whole item`)
51
+ }
52
+ return true
53
+ }
54
+ if (code == 435) {
55
+ // (fn, uses key, from keys, idOf) the shown ids per container: computed only here (dev)
56
+ if (typeof a == 'function') {
57
+ if ((c || []).every((l: string) => onced(`SYG435:${b}:${l}`))) return
58
+ try {
59
+ const by = new Map<any, any[]>(), [els, root] = a(), n = new Map<any, number>()
60
+ // a row's place: its data-index (a VirtualCollection row below the host's root; its
61
+ // pinned row isn't in DOM order)
62
+ const rows = els.map((el: any) => {
63
+ const r0 = el.closest?.('[data-index]'), row = r0 && r0 !== root && root.contains?.(r0) ? r0 : el
64
+ return [el, row, Number(row === el && !r0 ? NaN : row.getAttribute('data-index'))]
65
+ })
66
+ // 3-O G-505: a row's container is its nearest ancestor holding another row (items each in
67
+ // their own wrapper, <li><div data-id>, a table cell, a Collection item's root, are still
68
+ // one list); `n` counts the rows below each ancestor
69
+ const up = (row: any, f: (p: any) => any) => { for (let p = row.parentElement; p; p = p === root ? null : p.parentElement) if (f(p)) return p }
70
+ for (const [, row] of rows) up(row, p => void n.set(p, (n.get(p) || 0) + 1))
71
+ for (const [el, row, at] of rows) {
72
+ const p = up(row, p => n.get(p)! > 1) || row.parentElement
73
+ by.has(p) || by.set(p, [])
74
+ by.get(p)!.push([isNaN(at) ? by.get(p)!.length : at, String(d(el))])
75
+ }
76
+ return [...by.values()].map(g => g.sort((x, y) => x[0] - y[0]).map(x => x[1]))
77
+ } catch (_) { return }
78
+ }
79
+ // (shown groups, host state, from keys, idField, uses key) at a keyboard pick-up or a press
80
+ const groups: string[][] = Array.isArray(a?.[0]) ? a : [a || []]
81
+ for (const l of c || []) {
82
+ if (onced(`SYG435:${e}:${l}`)) continue
83
+ const at = new Map<string, number>()
84
+ if (Array.isArray(b?.[l])) b[l].forEach((x: any, i: number) => at.set(String(x?.[d]), i))
85
+ // the list's container: the one holding most of its entries (others: stray matches)
86
+ let dom: number[] = []
87
+ for (const g of groups) {
88
+ const ix = g.filter(i => at.has(i)).map(i => at.get(i)!)
89
+ if (ix.length > dom.length) dom = ix
90
+ }
91
+ // a contiguous run in the array's order is fine (all of it, or a window of it)
92
+ if (!dom.some((v, j) => v !== dom[0] + j)) continue
93
+ if (once(`SYG435:${e}:${l}`)) devReport('SYG435', {
94
+ component: 'sortable',
95
+ message: `sortable '${e}': state.${l} is shown ${dom.length < at.size ? 'with entries hidden between the shown ones' : 'in another order'} (a Collection with ${dom.length < at.size ? 'filter' : 'sort'}?), but moves and the announced positions follow the array: a keyboard step can pass entries nobody sees, or change nothing visible`,
96
+ fix: `Reorder a list that is shown as it is stored: drop the Collection's sort (sort state.${l} itself), and hide the drag handles while a filter hides entries`,
97
+ data: {from: l, key: e},
98
+ })
99
+ }
100
+ return
101
+ }
102
+ if (code == 147) {
103
+ const st = b || {}, arrays = Object.keys(st).filter(k => Array.isArray(st[k]))
104
+ const near = suggest(a, arrays)
105
+ if (once(`SYG147:${c}:${a}`)) devReport('SYG147', {
106
+ component: 'sortable',
107
+ message: `sortable '${c}': from names '${a}', but state.${a} is ${st[a] === undefined ? 'missing' : 'not an array'} when the host starts${near ? ` (did you mean '${near}'?)` : ''}; nothing in it can be moved`,
108
+ fix: `Name the host state's list: sortable({ from: '${near || arrays[0] || 'items'}', ... })${arrays.length ? ` (arrays in the state: ${arrays.join(', ')})` : `, and start it as an array (initialState: { ${a}: [] })`}`,
109
+ data: {from: a, key: c},
110
+ })
111
+ }
112
+ }
113
+
114
+ const report146 = (option: string, selector: string, why: string, fix: string) => {
115
+ if (once(`SYG146:${option}:${selector}`)) devReport('SYG146', {
116
+ component: 'sortable',
117
+ message: `sortable: ${option} '${selector}' matches nothing: ${why}, so no item can be dragged`,
118
+ fix,
119
+ data: {option, selector},
120
+ })
121
+ }
122
+
123
+ /** Install the `sortable` hook on the core bridge (sortable.ts calls it). */
124
+ export function installSortableHooks(): () => void {
125
+ const c = (globalThis as any).__SYGNAL_DIAGNOSTICS__
126
+ if (!c) return () => {}
127
+ c.sortable = reportSortable
128
+ return () => { if (c.sortable === reportSortable) c.sortable = undefined }
129
+ }
@@ -0,0 +1,148 @@
1
+ /**
2
+ * SYG201 — STATE reducer dropped keys that existed in the previous state (warn)
3
+ * SYG202 — STATE reducer returned undefined (warn)
4
+ * SYG221 — set() called with a string (error, G-143): the string is spread
5
+ * into the state as keys '0', '1', ... (one per character)
6
+ * SYG238 — G-393: a root with a model but no initialState (and no state from renderComponent's
7
+ * `initialState` or an HMR swap): its state is undefined, so it renders nothing until an
8
+ * action sets one (the 5.x / PLAN-4 behaviour; a root without a model renders from
9
+ * `initialState || true`, G-172). onModel, at construction, once per component name.
10
+ * SYG222 — STATE reducer returned the object it got after mutating it in place (warn, dev;
11
+ * PLAN-4 GS-4): since 6.0 the same object back means "no change", so the mutation
12
+ * is ignored. Mechanism: onIntent (called before the model is read) wraps the
13
+ * instance's STATE reducers; the wrapper takes a shallow snapshot (keys and top-level
14
+ * values) before the reducer runs and compares it when the reducer returns the same
15
+ * reference; a behavior slice (state[key] of a `uses` entry) is snapshotted the same
16
+ * way (keys reported as 'key.field', G-214). Nested mutations (state.list.push) are
17
+ * below the snapshot. Zero bytes
18
+ * in the core: the wrapping only happens with the dev entry and diagnostics on.
19
+ *
20
+ * Mechanism: onReducer(component, action, prevState, nextState). Reported once
21
+ * per action per component name. Not reported for:
22
+ * - synthetic actions (`__*`) and INITIALIZE (whole-state replacement; HYDRATE is an
23
+ * ordinary action since 6.0, D66)
24
+ * - SYG202: a Collection item (`() => undefined` is its documented self-removal, G-357)
25
+ * - keys that are calculated fields (they are re-derived after the reducer)
26
+ * - keys whose previous value was undefined
27
+ * The set()/toggle() helpers always spread the previous state, so they can't
28
+ * drop keys and never trigger SYG201.
29
+ */
30
+ import type {DiagnosticCheck} from '../index'
31
+ import {report, devReport, once, nameOf, isPlainObject} from './shared'
32
+ import {ORIGINAL} from '../../../shared'
33
+
34
+ const SKIP = new Set(['INITIALIZE'])
35
+
36
+ // SYG222: the instance's STATE reducers, wrapped (see the header)
37
+ const WRAPPED = Symbol('SYG222')
38
+
39
+ export function watchMutation(component: any, action: string, fn: any): any {
40
+ if (typeof fn !== 'function' || fn[WRAPPED]) return fn
41
+ const wrapped: any = (state: any, ...rest: any[]) => {
42
+ if (!isPlainObject(state)) return fn(state, ...rest)
43
+ // the state's top level, and (G-214) each behavior slice's: a behavior reducer that mutates
44
+ // its slice gets the host state back unchanged (behaviors.ts), so state[key] looks the same
45
+ const snaps = [[state, ''], ...(component._uses || []).map(([k]: any) => [state[k], k + '.'])]
46
+ .filter(([o]) => isPlainObject(o)).map(([o, p]) => [o, p, Object.keys(o), Object.values(o)])
47
+ const out = fn(state, ...rest)
48
+ if (out === state) {
49
+ const changed = snaps.flatMap(([o, p, keys, values]: any) => keys.filter((k: string, i: number) => !(k in o) || o[k] !== values[i])
50
+ .concat(Object.keys(o).filter(k => !keys.includes(k))).map((k: string) => p + k))
51
+ const name = nameOf(component)
52
+ if (changed.length && once(`SYG222:${name}:${action}`)) {
53
+ const list = changed.map(k => `'${k}'`).join(', ')
54
+ devReport('SYG222', {
55
+ component,
56
+ message: `The STATE reducer for '${action}' changed ${list} in place and returned the same object. Returning the object a reducer got means "no change" (as ABORT), so the change is ignored and nothing re-renders`,
57
+ fix: `Return a new object: ${changed[0].includes('.') ? `(slice, data) => ({ ...slice, ${changed[0].split('.')[1]}: … })` : `(state, data) => ({ ...state, ${changed[0]}: … })`}. To write updates as mutations, wrap the reducer in immer's produce()`,
58
+ data: {action, keys: changed},
59
+ })
60
+ }
61
+ }
62
+ return out
63
+ }
64
+ wrapped[WRAPPED] = true
65
+ // checks that read reducer source (reply actions) see the original; so do the action log
66
+ // and t.explain() (ORIGINAL)
67
+ wrapped.toString = () => fn.toString()
68
+ wrapped[ORIGINAL] = fn
69
+ return wrapped
70
+ }
71
+
72
+ export const stateCheck: DiagnosticCheck = {
73
+ id: 'state',
74
+
75
+ // SYG222: checks/next.ts wraps the STATE reducers (watchMutation) through wrapHandler
76
+
77
+ onModel(component) {
78
+ // SYG238 (G-393): the initial state is set at construction (D165), before onModel
79
+ const n = component?.__next
80
+ if (!n?.isRoot || !n.def?.model || n.state !== undefined) return
81
+ const name = nameOf(component)
82
+ if (!once(`SYG238:${name}`)) return
83
+ devReport('SYG238', {
84
+ component,
85
+ message: `${name} is a root with a model but no initialState: its state is undefined, so it renders nothing until an action sets one`,
86
+ fix: `Add ${name}.initialState = { … } (the root's start state; a child gets its state from its parent instead)`,
87
+ data: {},
88
+ })
89
+ },
90
+
91
+ onReducer(component, action, prevState, nextState) {
92
+ if (typeof action !== 'string' || action.startsWith('__') || SKIP.has(action)) return
93
+ const name = nameOf(component)
94
+
95
+ if (nextState === undefined) {
96
+ // G-357: returning undefined from a Collection item's reducer is the documented way to
97
+ // remove the item: not reported. In another sub-component it is info, in the root (where
98
+ // it wipes the whole app state) a warning.
99
+ if (component?.__next?.kind === 'item' || !once(`SYG202:${name}:${action}`)) return
100
+ const sub = !!component?.isSubComponent
101
+ report('SYG202', {
102
+ component,
103
+ severity: sub ? 'info' : 'warn',
104
+ message: `The STATE reducer for '${action}' returned undefined` +
105
+ (sub ? ' (only a Collection item removes itself this way)' : ''),
106
+ fix: `Return the new state, e.g. (state, data) => ({ ...state, ... }), or return ABORT to leave the state unchanged`,
107
+ data: {action},
108
+ })
109
+ return
110
+ }
111
+
112
+ if (!isPlainObject(prevState) || !isPlainObject(nextState)) return
113
+
114
+ // SYG221 (G-143): set('city') spreads the string into the state: { ...state, 0: 'c', 1: 'i', ... }
115
+ const chars: string[] = []
116
+ while (typeof nextState[chars.length] === 'string' && nextState[chars.length].length === 1 && !(chars.length in prevState)) {
117
+ chars.push(nextState[chars.length])
118
+ }
119
+ if (chars.length > 0 && !(chars.length in nextState)) {
120
+ if (!once(`SYG221:${name}:${action}`)) return
121
+ const field = chars.join('')
122
+ const update = /^[A-Za-z_$][\w$]*$/.test(field)
123
+ ? `set((state, ${field}) => ({ ${field} }))`
124
+ : `set((state, value) => ({ ['${field}']: value }))`
125
+ devReport('SYG221', {
126
+ component,
127
+ message: `The STATE reducer for '${action}' added the keys ${chars.map((_, i) => `'${i}'`).slice(0, 4).join(', ')}${chars.length > 4 ? ', …' : ''} ` +
128
+ `(one per character of '${field}'): set() was called with a string, set('${field}'). set() takes an object or a function`,
129
+ fix: `To store the action data in '${field}', pass a function: ${update}. For a fixed value pass an object: set({ ${field}: … })`,
130
+ data: {action, field},
131
+ })
132
+ return
133
+ }
134
+
135
+ const calculated: Set<string> | null = component?._calculatedFieldNames || null
136
+ const dropped = Object.keys(prevState).filter(key =>
137
+ !(key in nextState) && prevState[key] !== undefined && !(calculated && calculated.has(key)))
138
+ if (dropped.length === 0 || !once(`SYG201:${name}:${action}`)) return
139
+
140
+ const list = dropped.map(k => `'${k}'`).join(', ')
141
+ report('SYG201', {
142
+ component,
143
+ message: `The STATE reducer for '${action}' returned a state without ${list}, which ${dropped.length === 1 ? 'was' : 'were'} in the previous state (likely a missing ...state spread)`,
144
+ fix: `Spread the previous state: (state, data) => ({ ...state, ... }). If removing ${dropped.length === 1 ? 'the key' : 'these keys'} is intended, ignore this`,
145
+ data: {action, droppedKeys: dropped},
146
+ })
147
+ },
148
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * PLAN-4.5 D152: shared statics are frozen in dev (dev entry only; the core never freezes).
3
+ *
4
+ * Since P45-B a component's `initialState`, `model`, `context` and `calculated` statics are
5
+ * shared by all its instances (no per-instance deep copy). Mutating one in place (a reducer's
6
+ * `state.items.push(x)` on the initial state, a view that writes into state) would change every
7
+ * instance, and every later one. With diagnostics on, `initialState` is deep-frozen (its plain
8
+ * objects and arrays; class instances such as Map or Date are left as they are) and the other
9
+ * statics are frozen at the top level, so such a mutation throws a TypeError where it happens
10
+ * (in a reducer: reported as SYG216 with the error attached; in a view: SYG406).
11
+ *
12
+ * Frozen: the component function's own statics, and the options given to component() (G-280:
13
+ * `component({ view, model, initialState })`; the instance's `_o`, the values before behaviors or
14
+ * persist() made per-instance copies). Not frozen: the instance's values (G-268), and an
15
+ * initial state a wrapper built from its caller's objects (G-275: marked with `owned()` by
16
+ * sygnal/element (host props), Vike (pageContext.data, hydrated state) and renderComponent's
17
+ * `initialState` option; the caller may still change those). Mechanism: onIntent, which runs
18
+ * during construction, before the model is wired.
19
+ */
20
+ import type {DiagnosticCheck} from '../index'
21
+ import {OWNED} from '../../owned'
22
+
23
+ const plain = (v: any): boolean => {
24
+ if (!v || typeof v != 'object') return false
25
+ const p = Object.getPrototypeOf(v)
26
+ return Array.isArray(v) || p === Object.prototype || p === null
27
+ }
28
+
29
+ const deepFreeze = (v: any): void => {
30
+ if (!plain(v) || Object.isFrozen(v) || v[OWNED]) return
31
+ Object.freeze(v)
32
+ for (const k of Object.keys(v)) deepFreeze(v[k])
33
+ }
34
+
35
+ const freeze = (o: any): void => {
36
+ if (!o || (typeof o != 'object' && typeof o != 'function')) return
37
+ deepFreeze(o.initialState)
38
+ for (const k of ['model', 'context', 'calculated']) if (plain(o[k])) Object.freeze(o[k])
39
+ }
40
+
41
+ export const staticsCheck: DiagnosticCheck = {
42
+ id: 'statics',
43
+
44
+ onIntent(component) {
45
+ if (typeof component?.view == 'function') freeze(component.view)
46
+ freeze(component?._o)
47
+ },
48
+ }