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,175 @@
1
+ /**
2
+ * PLAN-4 GS-2: element command diagnostics (dev entry; the core carries no text).
3
+ *
4
+ * The core (src/extra/elementCommands.ts) calls `__SYGNAL_DIAGNOSTICS__.elementCommand(component,
5
+ * command, element?)` when a command can't run:
6
+ * - no element (`element` falsy): SYG640 (warn): nothing in the sending instance's DOM scope
7
+ * matched the target within about 1 s;
8
+ * - an element, but neither the control's spec command nor a method of the element: SYG641
9
+ * (error). It names the control's declared commands. When the element is a widget's host
10
+ * (PLAN-5 W-1, `__sw`): SYG142 (error), naming the widget's declared commands.
11
+ * installElementCommandHooks() publishes it on the core bridge.
12
+ *
13
+ * When a command is sent (checkSentCommand, from the elementCommands check's onModel, which wraps
14
+ * the instance's ELEMENT stream before the core subscribes it): SYG641 for a value that is not a
15
+ * command object, and for a method that changes the DOM Sygnal renders (remove, append,
16
+ * setAttribute...): the core runs any method the element has, and those break the next patch.
17
+ *
18
+ * renderComponent uses these functions too (mock DOM, and the real DOM without the dev entry), so
19
+ * this module has no side effects and doesn't import ./shared (which registers codes when loaded).
20
+ */
21
+ import type {DiagnosticCheck} from '../index'
22
+ import {DEV_CODE_SEVERITY} from '../codes'
23
+
24
+ /** The element commands Sygnal documents and types (the core runs any method of the element) */
25
+ export const NATIVE_COMMAND_NAMES = ['focus', 'blur', 'select', 'click', 'scrollIntoView', 'showModal', 'show', 'close', 'showPopover', 'hidePopover', 'togglePopover']
26
+
27
+ const core = (): any => (globalThis as any).__SYGNAL_DIAGNOSTICS__
28
+
29
+ const send = (code: string, details: any): void => {
30
+ try {
31
+ core()?.report(code, {severity: DEV_CODE_SEVERITY[code], ...details})
32
+ } catch (err) {
33
+ // 'error' mode: rethrown asynchronously (never into the caller)
34
+ setTimeout(() => { throw err })
35
+ }
36
+ }
37
+
38
+ const isControl = (x: any): boolean => typeof x == 'function' && !!x.__sygnalControl
39
+ const keyOf = (control: any): string => String(control).slice(15, -2)
40
+
41
+ /** a command as written: `{ focus: Email, preventScroll: true }` */
42
+ export const describeCommand = (cmd: any): string => {
43
+ if (!cmd || typeof cmd != 'object') return JSON.stringify(cmd) ?? String(cmd)
44
+ const parts = Object.keys(cmd).map(k => {
45
+ const v = cmd[k]
46
+ let text: string
47
+ if (isControl(v)) text = keyOf(v)
48
+ else try { text = JSON.stringify(v) ?? String(v) } catch (_) { text = String(v) }
49
+ return `${k}: ${text}`
50
+ })
51
+ return `{ ${parts.join(', ')} }`
52
+ }
53
+
54
+ const distance = (a: string, b: string): number => {
55
+ const row = Array.from({length: b.length + 1}, (_, i) => i)
56
+ for (let i = 1; i <= a.length; i++) {
57
+ let prev = row[0]
58
+ row[0] = i
59
+ for (let j = 1; j <= b.length; j++) {
60
+ const cur = row[j]
61
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1] == b[j - 1] ? 0 : 1))
62
+ prev = cur
63
+ }
64
+ }
65
+ return row[b.length]
66
+ }
67
+
68
+ const closest = (name: string, names: string[]): string | undefined => {
69
+ let best: string | undefined, d = 3
70
+ for (const n of names) {
71
+ const x = n.toLowerCase() == name.toLowerCase() ? 0 : distance(name, n)
72
+ if (x < d) { d = x; best = n }
73
+ }
74
+ return best
75
+ }
76
+
77
+ const tagOf = (el: any): string => el && typeof el.tagName == 'string' ? `<${el.tagName.toLowerCase()}>` : 'element'
78
+
79
+ const notACommand = (component: any, name: string, cmd: any) => send('SYG641', {
80
+ component,
81
+ message: `${name} sent ${describeCommand(cmd)} to the ELEMENT sink, which is not a command`,
82
+ fix: `Send { <method>: control or selector, ...options }, e.g. { focus: Email }, or an array of them`,
83
+ data: {command: cmd},
84
+ })
85
+
86
+ /** SYG640 (no `el`) or SYG641 (an element, but no command of that name) for `cmd` sent by `component` */
87
+ export function reportElementCommand(component: any, cmd: any, el?: any): void {
88
+ // a disposed instance's pending commands are dropped silently
89
+ if (component && component._disposed) return
90
+ const name = (component && component.name) || 'component'
91
+ const method = cmd && typeof cmd == 'object' ? Object.keys(cmd)[0] : undefined
92
+ if (method === undefined) return notACommand(component, name, cmd)
93
+ const target = cmd[method]
94
+ const what = `ELEMENT ${describeCommand(cmd)}`
95
+ const control = isControl(target) ? keyOf(target) : undefined
96
+ const data: any = {method, command: cmd, ...(control ? {control} : {selector: target == null ? target : String(target)})}
97
+ if (!el) {
98
+ const missing = target == null || target === ''
99
+ return send('SYG640', {
100
+ component,
101
+ message: missing
102
+ ? `${what} in ${name} has no target (${String(target)}), so the command was dropped`
103
+ : `${what} in ${name} matched no element: nothing in ${name}'s own view matches ${control ? `the control ${control}` : `'${String(target)}'`} (waited 1 s), so the command was dropped`,
104
+ fix: missing
105
+ ? `Give the method a control or a selector: { ${method}: Email }`
106
+ : `Render the element in ${name}'s view, or send the command from the component that renders it: a child component's elements are isolated from its parent, and a command reaches only its sender's own elements (in a Collection, each item sends its own)`,
107
+ data,
108
+ })
109
+ }
110
+ // PLAN-5 W-1: the matched element is a widget's host (selector or control target): SYG142 names
111
+ // the widget's declared commands
112
+ const widget = el.__sw?.w
113
+ if (widget) {
114
+ const declared = Object.keys(widget.commands)
115
+ const hint = closest(method, [...declared, ...NATIVE_COMMAND_NAMES])
116
+ return send('SYG142', {
117
+ component,
118
+ message: `${what} in ${name}: the matched ${tagOf(el)} is a widget's host, and the widget declares no '${method}' command` +
119
+ (hint ? ` (did you mean '${hint}'?)` : '') + (declared.length ? `; it declares: ${declared.join(', ')}` : '; it declares none'),
120
+ fix: `Add '${method}' to the widget's commands (defineWidget({ commands: { ${method}: (instance, options) => … } })), or send one it declares`,
121
+ data: {...data, element: tagOf(el), commands: declared},
122
+ })
123
+ }
124
+ const commands = control && target.spec && typeof target.spec == 'object' && target.spec.commands
125
+ ? Object.keys(target.spec.commands) : []
126
+ data.element = tagOf(el)
127
+ if (commands.length) data.commands = commands
128
+ let message: string, fix: string
129
+ if (NATIVE_COMMAND_NAMES.includes(method)) {
130
+ message = `${what} in ${name}: the matched ${tagOf(el)} has no ${method}() method${commands.length ? `, and the control ${control}'s spec declares no '${method}' command (it declares: ${commands.join(', ')})` : ''}`
131
+ fix = /^(show|showModal|close)$/.test(method)
132
+ ? `${method}() works on a <dialog> element: render the target as a <dialog>`
133
+ : /Popover$/.test(method)
134
+ ? `The popover methods need an element with the popover attribute (attrs: { popover: 'auto' }) in a browser with the Popover API`
135
+ : `Target an element that has ${method}()`
136
+ } else {
137
+ const hint = closest(method, [...commands, ...NATIVE_COMMAND_NAMES])
138
+ message = `${what} in ${name}: '${method}' is not an element command` + (hint ? ` (did you mean '${hint}'?)` : '') +
139
+ (el.tagName ? `: the matched ${tagOf(el)} has no such method` : '') +
140
+ (commands.length ? `, and the control ${control}'s spec declares only: ${commands.join(', ')}` : '')
141
+ fix = `Use ${commands.length ? `one of its commands (${commands.join(', ')}) or ` : ''}one of: ${NATIVE_COMMAND_NAMES.join(', ')} (or another method of the element, like play). The first key of a command is the method, the others are its options: { scrollIntoView: Row, block: 'nearest' }`
142
+ }
143
+ send('SYG641', {component, message, fix, data})
144
+ }
145
+
146
+ // methods that change the DOM snabbdom owns (the next patch undoes them or trips over them)
147
+ const DOM_MUTATORS = /^(remove|append|prepend|before|after|replaceWith|replaceChildren|appendChild|removeChild|insertBefore|replaceChild|insertAdjacent(Element|HTML|Text)|(set|remove|toggle)Attribute(NS|Node)?|attachShadow|setHTML(Unsafe)?|normalize)$/
148
+
149
+ /** SYG641 when it is sent: not a command object, or a method that changes the DOM Sygnal renders */
150
+ export function checkSentCommand(component: any, cmd: any): void {
151
+ if (cmd == null) return
152
+ const name = (component && component.name) || 'component'
153
+ if (typeof cmd != 'object' || Array.isArray(cmd) || !Object.keys(cmd).length) return notACommand(component, name, cmd)
154
+ const method = Object.keys(cmd)[0], target = cmd[method]
155
+ if (!DOM_MUTATORS.test(method) || (isControl(target) && target.spec?.commands?.[method])) return
156
+ send('SYG641', {
157
+ component,
158
+ message: `ELEMENT ${describeCommand(cmd)} in ${name}: ${method}() changes the DOM that ${name}'s view renders, so the next render undoes it or fails`,
159
+ fix: `Change the state and render the result instead; element commands are for what a view can't express (focus, scrolling, dialogs, popovers, media)`,
160
+ data: {method, command: cmd},
161
+ })
162
+ }
163
+
164
+ /** The dev entry check: checkSentCommand runs on every ELEMENT value through the core's onElementCommand hook (checks/next.ts) */
165
+ export const elementCommandsCheck: DiagnosticCheck = {
166
+ id: 'elementCommands',
167
+ }
168
+
169
+ /** Publish the element command hook on the core bridge. Returns an uninstall function. */
170
+ export function installElementCommandHooks(): () => void {
171
+ const c = core()
172
+ if (!c) return () => {}
173
+ c.elementCommand = reportElementCommand
174
+ return () => { if (c.elementCommand === reportElementCommand) c.elementCommand = undefined }
175
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * SYG105 — EVENTS type emitted but never selected, or selected but never
3
+ * emitted (info).
4
+ * SYG116 — a value with no string `type` (typically a function) reached the
5
+ * EVENTS bus (error, G-143).
6
+ *
7
+ * Mechanism: the EVENTS driver (src/extra/eventDriver.ts) calls onBusSelect
8
+ * for every EVENTS.select(type) and onBusEmit for every bus event.
9
+ * - emitted, nothing selects it: reported when the event is emitted. The bus
10
+ * is a broadcast, so an event nobody has subscribed to by then is dropped.
11
+ * - selected, never emitted: only knowable at the end of a session, so it is
12
+ * reported on demand by checkEventBus() (inspect / test helpers), never
13
+ * automatically.
14
+ * EVENTS.select() with no type (all events) counts as selecting everything.
15
+ */
16
+ import type {DiagnosticCheck} from '../index'
17
+ import {report, devReport, once, onReset, didYouMean} from './shared'
18
+
19
+ let selected = new Set<string>()
20
+ let emitted = new Set<string>()
21
+ let selectsAll = false
22
+
23
+ onReset(() => { selected = new Set(); emitted = new Set(); selectsAll = false })
24
+
25
+ export const eventsCheck: DiagnosticCheck = {
26
+ id: 'events',
27
+
28
+ onBusSelect(type) {
29
+ if (type === undefined || type === null || type === '') { selectsAll = true; return }
30
+ for (const t of ([] as any[]).concat(type)) if (typeof t === 'string') selected.add(t)
31
+ },
32
+
33
+ onBusEmit(type, emitterName) {
34
+ if (typeof type !== 'string') {
35
+ // SYG116 (G-143): no EVENTS.select() can ever receive it. The core copies each emitted
36
+ // value into a new object (devtools stamp), so a function arrives here as {}.
37
+ if (once(`SYG116:${emitterName}`)) {
38
+ devReport('SYG116', {
39
+ component: emitterName,
40
+ message: `${emitterName ? emitterName + "'s model" : 'A model'} sent a value with no string 'type' to the EVENTS sink, so nothing receives it. ` +
41
+ `The value was probably a function (a model entry that returns event(...) instead of being event(...)), or undefined/null`,
42
+ fix: `Make event() the sink entry itself: ACTION: { EVENTS: event('TYPE', (state, data) => payload) }; return ABORT to send nothing`,
43
+ data: {type, emitter: emitterName},
44
+ })
45
+ }
46
+ return
47
+ }
48
+ emitted.add(type)
49
+ if (selectsAll || selected.has(type) || !once(`SYG105:emit:${type}`)) return
50
+ report('SYG105', {
51
+ component: emitterName,
52
+ message: `EVENTS event '${type}' was emitted, but nothing selects it, so it was dropped${didYouMean(type, selected)}`,
53
+ fix: `Subscribe with EVENTS.select('${type}') in the component that should react, or check the event name`,
54
+ data: {type, direction: 'emitted-not-selected', emitter: emitterName},
55
+ })
56
+ },
57
+ }
58
+
59
+ export interface EventBusSummary {
60
+ selected: string[]
61
+ emitted: string[]
62
+ selectedNeverEmitted: string[]
63
+ emittedNeverSelected: string[]
64
+ }
65
+
66
+ /**
67
+ * Report SYG105 (info) for every EVENTS type that has been selected but never
68
+ * emitted so far, and return the bus registry. Intended for inspect tooling and
69
+ * tests, at a point where the session is "done".
70
+ */
71
+ export function checkEventBus(): EventBusSummary {
72
+ const selectedNeverEmitted = [...selected].filter(t => !emitted.has(t))
73
+ const emittedNeverSelected = selectsAll ? [] : [...emitted].filter(t => !selected.has(t))
74
+ for (const type of selectedNeverEmitted) {
75
+ if (!once(`SYG105:select:${type}`)) continue
76
+ report('SYG105', {
77
+ message: `EVENTS.select('${type}') is used, but no component has emitted '${type}'${didYouMean(type, emitted)}`,
78
+ fix: `Emit it from a model entry, e.g. { EVENTS: event('${type}', ...) }, or check the event name`,
79
+ data: {type, direction: 'selected-not-emitted'},
80
+ })
81
+ }
82
+ return {selected: [...selected], emitted: [...emitted], selectedNeverEmitted, emittedNeverSelected}
83
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * makeFetchDriver requests (PLAN-3 5-3: the query cache, D79/D80; G-175). Dev-entry codes
3
+ * (DEV_CODE_SEVERITY):
4
+ *
5
+ * SYG630 — a cached request that is not idempotent: `cache: true` or `staleTime` on a
6
+ * POST/PUT/PATCH/DELETE (a request with a body defaults to POST) (warn)
7
+ * SYG631 — `validate` is not a Standard Schema (no `~standard.validate`): the request
8
+ * fails (error)
9
+ * SYG632 — `{ invalidate }` matched no cache entry and no mounted resource (info)
10
+ * SYG633 — `{ abort: 'X' }` cancels the lane 'X', but this instance sends its `ok: 'X'`
11
+ * requests under another `key`, so nothing is cancelled (warn)
12
+ * SYG635 — `cache: true` / `staleTime` / `{ prefetch }` sent to a driver without a
13
+ * `queryCache()` (D88): nothing is cached (warn). The source's `__inspect().cache`
14
+ * is undefined without one
15
+ *
16
+ * Mechanism: onModel wraps the component's own model stream for every sink whose source
17
+ * takes the `resources` static (makeFetchDriver and renderComponent's HTTP fake) and checks
18
+ * each value as it is sent: requests, and each entry of a `{ resources }` declaration.
19
+ * SYG632 asks the source how many entries/resources the value matches (`__matches`).
20
+ * Each is reported once per component name and request URL / name.
21
+ */
22
+ import type {DiagnosticCheck} from '../index'
23
+ import {devReport, once, nameOf} from './shared'
24
+ import {isStandardSchema} from '../../standardSchema'
25
+
26
+ const IDEMPOTENT = /^(GET|HEAD)$/i
27
+
28
+ /** instance → reply key lanes it has sent, and ok name → the other key it was sent under */
29
+ const lanes = new WeakMap<object, {used: Set<string>; keyed: Map<string, string>}>()
30
+
31
+ const fetchSink = (component: any, name: string) => component?.sources?.[name]?.__sygnalStatic === 'resources'
32
+ /** 5-5: the sink's driver has no queryCache() */
33
+ const noCache = (component: any, sink: string) => {
34
+ const i = component?.sources?.[sink]?.__inspect?.()
35
+ return !!i && i.cache === undefined
36
+ }
37
+ const reportNoCache = (component: any, sink: string, what: string, url: string) => {
38
+ if (!noCache(component, sink) || !once(`SYG635:${nameOf(component)}:${what}:${url}`)) return
39
+ devReport('SYG635', {
40
+ component,
41
+ message: `${sink} ${url ? url + ' ' : ''}uses ${what}, but the ${sink} driver has no queryCache(), so nothing is cached`,
42
+ fix: `Give the driver a cache: makeFetchDriver({ cache: queryCache({ staleTime: 30000 }) }) (import { queryCache } from 'sygnal'); in tests renderComponent(C, { http: { cache: queryCache() } }). Or remove ${what}`,
43
+ data: {sink, url, what},
44
+ })
45
+ }
46
+
47
+ function checkOne(component: any, sink: string, req: any, where: string) {
48
+ if (!req || typeof req !== 'object') return
49
+ const name = nameOf(component)
50
+ const url = typeof req.url === 'string' ? req.url : ''
51
+ const method = String(req.method || (req.json !== undefined || req.body !== undefined ? 'POST' : 'GET')).toUpperCase()
52
+ if ((req.cache === true || req.staleTime != null) && !IDEMPOTENT.test(method) && once(`SYG630:${name}:${method}:${url}`)) {
53
+ devReport('SYG630', {
54
+ component,
55
+ message: `${where} ${method} ${url} sets ${req.cache === true ? 'cache: true' : 'staleTime'}, so a cached reply answers it instead of sending it again`,
56
+ fix: `Cache reads only: remove ${req.cache === true ? 'cache' : 'staleTime'} from the ${method}, and refresh the cached reads after it succeeds with invalidates: ['tag']`,
57
+ data: {sink, method, url},
58
+ })
59
+ }
60
+ if (req.cache === true || req.staleTime != null) reportNoCache(component, sink, req.cache === true ? 'cache: true' : 'staleTime', url)
61
+ if ('validate' in req && !isStandardSchema(req.validate) && once(`SYG631:${name}:${url}`)) {
62
+ devReport('SYG631', {
63
+ component,
64
+ message: `${where} ${url} has validate: ${typeof req.validate}, which is not a Standard Schema, so the request fails`,
65
+ fix: "Pass a Standard Schema object (zod, valibot, arktype, ...: anything with a '~standard'.validate function), e.g. validate: QuoteSchema",
66
+ data: {sink, url},
67
+ })
68
+ }
69
+ }
70
+
71
+ export function check(component: any, sink: string, req: any) {
72
+ if (!req || typeof req !== 'object') return
73
+ const name = nameOf(component)
74
+ const res = req.resources
75
+ if (res && typeof res === 'object') {
76
+ for (const k of Object.keys(res)) checkOne(component, sink, typeof res[k] === 'string' ? {url: res[k]} : res[k], `${name}.resources.${k}:`)
77
+ return
78
+ }
79
+ if (req.prefetch) {
80
+ const p = req.prefetch
81
+ reportNoCache(component, sink, '{ prefetch }', typeof p === 'string' ? p : String(p?.url ?? ''))
82
+ return
83
+ }
84
+ if ('invalidate' in req) {
85
+ const count = component.sources[sink]?.__matches?.(req.invalidate)
86
+ const what = typeof req.invalidate === 'function' ? 'the predicate' : JSON.stringify(req.invalidate)
87
+ if (count === 0 && once(`SYG632:${name}:${what}`)) {
88
+ devReport('SYG632', {
89
+ component,
90
+ message: `${sink} { invalidate: ${what} } matched no cache entry and no mounted resource`,
91
+ fix: "Give the resources (or cached requests) to refresh tags: ['quotes'] and invalidate that tag, or a URL prefix starting with '/' ('/api/quotes')",
92
+ data: {sink, invalidate: what},
93
+ })
94
+ }
95
+ return
96
+ }
97
+ let l = lanes.get(component)
98
+ if (!l) lanes.set(component, (l = {used: new Set(), keyed: new Map()}))
99
+ const {ok, error, key, abort} = req
100
+ if (typeof abort === 'string' && key === undefined) {
101
+ const other = l.keyed.get(abort)
102
+ if (other !== undefined && !l.used.has(abort) && once(`SYG633:${name}:${abort}`)) {
103
+ devReport('SYG633', {
104
+ component,
105
+ message: `${sink} { abort: '${abort}' } cancels the requests with key '${abort}', but ${name} sends its ok: '${abort}' requests with key: '${other}', so nothing is cancelled`,
106
+ fix: `Cancel by that key: { abort: true, key: '${other}' } (or drop the key, so the lane is the ok action)`,
107
+ data: {sink, abort, key: other},
108
+ })
109
+ }
110
+ return
111
+ }
112
+ if (typeof ok === 'string' || typeof error === 'string') {
113
+ const lane = key ?? ok ?? error
114
+ l.used.add(lane)
115
+ if (typeof ok === 'string' && key !== undefined && key !== ok) l.keyed.set(ok, String(key))
116
+ }
117
+ checkOne(component, sink, req, `A ${sink} request`)
118
+ }
119
+
120
+ export const fetchCheck: DiagnosticCheck = {
121
+ id: 'fetch',
122
+
123
+ onModel(component) {
124
+ const model$ = component?.model$
125
+ if (!model$ || typeof model$ !== 'object') return
126
+ for (const sink of Object.keys(model$)) {
127
+ const s$ = model$[sink]
128
+ if (!fetchSink(component, sink) || !s$ || typeof s$.map !== 'function') continue
129
+ model$[sink] = s$.map((req: any) => {
130
+ try { check(component, sink, req) } catch (_) {}
131
+ return req
132
+ })
133
+ }
134
+ },
135
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * PLAN-5 F-1: `form` behavior diagnostics (dev entry; src/extra/form.ts carries no text).
3
+ *
4
+ * The form module calls `__SYGNAL_DIAGNOSTICS__.form(code, ...args)`:
5
+ * 231 form(schema) with a schema that isn't a Standard Schema: throws the SYG231 Error (in
6
+ * production the first validation throws a plain TypeError)
7
+ * 230 (name, values) an input / focusout from a named field whose name isn't in `values`:
8
+ * SYG230 (warn, once per name); the event is ignored
9
+ * 232 (slice) a submit while submitting or while a queued submit waits for a check: SYG232
10
+ * (info, every time); the submit is dropped
11
+ * 233 (values, output) a valid submit: each top-level `values` key missing from the schema's
12
+ * output, SYG233 (warn, once per key): the schema strips it (zod's z.object does). G-377:
13
+ * only when every output key is a values key (an output with other keys reshapes: a rename)
14
+ * 235 (field, request) a check's request with `ok` / `error` / `latest` keys, which the form
15
+ * overwrites: SYG235 (warn, once per field)
16
+ * 234 (submit, key) G-577: a valid submit whose `submit` names one of the form's own actions,
17
+ * so it dispatched '<key>.<submit>' instead of the host's action: SYG234 (warn, once per
18
+ * key and name), at the submit (the form's own action then often throws: SYG214 / SYG216)
19
+ * Host checks (onModel, once per component name), for each `uses` entry made by form():
20
+ * SYG234 (warn) `submit` missing, not a host model entry, or one of the form's own action names
21
+ * (G-577: every action in that form's model, CHECKED_<field> included)
22
+ * SYG235 (warn) a `check` key that isn't a field of `values`
23
+ * SYG236 (warn) an array of objects in `values` with a row without an `id`
24
+ * SYG237 (warn) two form uses of one host with the same form selector (G-374)
25
+ */
26
+ import type {DiagnosticCheck} from '../index'
27
+ import {devReport, once, nameOf} from './shared'
28
+ import {docsUrlFor} from '../codes'
29
+ import {hasField} from '../../formHelpers'
30
+
31
+ // the form's own action names (G-577: read from the form's model, so CHECKED_<field> counts)
32
+ const ownActions = (b: any): string[] => Object.keys(b?.model || {})
33
+ const SUBMIT_FIX = (key: string, a: string, own: string[]) =>
34
+ `Name the submit after a host action the form doesn't define (not ${own.join(', ')}): submit: 'SAVE', and rename the model entry '${a}' to SAVE: { HTTP: (state, values) => ({ url, json: values, ok: '${key}.DONE', error: '${key}.ERRORS' }) }`
35
+ const RESERVED = ['ok', 'error', 'latest']
36
+
37
+ const what = (v: any): string =>
38
+ v === null || v === undefined ? String(v)
39
+ : typeof v == 'function' ? 'a function'
40
+ : Array.isArray(v) ? 'an array'
41
+ : typeof v == 'object' ? `an object without ~standard.validate${typeof v.parse == 'function' ? ' (a validator from before Standard Schema?)' : ''}`
42
+ : `a ${typeof v}`
43
+
44
+ export function reportForm(code: number, a?: any, b?: any, c?: any): any {
45
+ if (code == 231) {
46
+ const err: any = new TypeError(`[Sygnal SYG231] form(schema, options): the schema is ${what(a)}, not a Standard Schema. ` +
47
+ `Pass a Standard Schema object: a zod, valibot or arktype schema, or an object with '~standard': { version: 1, vendor, validate }. ${docsUrlFor('SYG231')}`)
48
+ err.code = 'SYG231'
49
+ throw err
50
+ }
51
+ if (code == 230) {
52
+ if (!once(`SYG230:${a}`)) return
53
+ devReport('SYG230', {
54
+ component: 'form',
55
+ message: `form: a field named '${a}' changed, but '${a}' isn't a field of the form's values (${Object.keys(b || {}).join(', ') || 'none'}); the change is ignored`,
56
+ fix: `Name the input after a path in values (name="email", name="addresses.<row id>.city"), add '${a}' to values, or move the input out of the form element`,
57
+ data: {name: a},
58
+ })
59
+ } else if (code == 232) {
60
+ devReport('SYG232', {
61
+ component: 'form',
62
+ message: `form: a submit was dropped: ${a?.submitting ? 'the previous submit is still being sent (no form.DONE / form.ERRORS yet)' : 'a submit is already queued, waiting for an async check or an async schema'}`,
63
+ fix: `Disable the submit button while state.form.submitting (or queued) is true; answer the submit with ok: 'form.DONE', error: 'form.ERRORS'`,
64
+ })
65
+ } else if (code == 233) {
66
+ if (!b || typeof b != 'object' || Object.keys(b).some(k => !(k in (a || {})))) return
67
+ for (const k of Object.keys(a || {})) {
68
+ if (!(k in b) && once(`SYG233:${k}`)) devReport('SYG233', {
69
+ component: 'form',
70
+ message: `form: values.${k} is not in the schema's output (stripped or renamed), so the submit action doesn't get it under that name (a schema strips keys it doesn't declare)`,
71
+ fix: `Declare '${k}' in the schema (or make the schema keep unknown keys), or remove it from values; if the schema renames it on purpose, ignore this`,
72
+ data: {key: k},
73
+ })
74
+ }
75
+ } else if (code == 234) {
76
+ if (!once(`SYG234:run:${b}:${a}`)) return
77
+ devReport('SYG234', {
78
+ component: 'form',
79
+ message: `form '${b}': a valid submit dispatched '${b}.${a}' (the form's own ${a} action), not the host's '${a}': submit: '${a}' names one of the form's own actions, which next() prefers`,
80
+ fix: SUBMIT_FIX(b, a, ownActions(c)),
81
+ data: {key: b, submit: a},
82
+ })
83
+ } else if (code == 235) {
84
+ const set = RESERVED.filter(k => b && k in b)
85
+ if (set.length && once(`SYG235:req:${a}`)) devReport('SYG235', {
86
+ component: 'form',
87
+ message: `form: the check for '${a}' returns a request with ${set.map(k => `'${k}'`).join(', ')}; the form sets them itself (its reply action and latest: true), so they are replaced`,
88
+ fix: `Return only the request: check: { ${a}: { request: (v) => ({ url, query }), error: (body) => message } }`,
89
+ data: {field: a, keys: set},
90
+ })
91
+ }
92
+ }
93
+
94
+ /** row ids missing in `values`: the names of the arrays of objects that have a row without an id */
95
+ const missingIds = (v: any, at = '', out: string[] = []): string[] => {
96
+ if (v && typeof v == 'object') {
97
+ if (Array.isArray(v) && v.some(r => r && typeof r == 'object' && !Array.isArray(r) && r.id == null)) out.push(at)
98
+ for (const k in v) missingIds(v[k], at ? `${at}.${k}` : k, out)
99
+ }
100
+ return out
101
+ }
102
+
103
+ export const formsCheck: DiagnosticCheck = {
104
+ id: 'forms',
105
+
106
+ onModel(component) {
107
+ const view = component?.view, uses = view?.uses
108
+ if (!uses || typeof uses != 'object') return
109
+ const name = nameOf(component), sels: Record<string, string> = {}
110
+ for (const key of Object.keys(uses)) {
111
+ const b = uses[key]
112
+ if (!b?.form) continue
113
+ // G-374: each form listens on its selector inside the host, so two with one selector hear
114
+ // each other's fields and submits
115
+ const sel = b.options?.form || 'form'
116
+ if (sel in sels) {
117
+ if (once(`SYG237:${name}:${sel}`)) devReport('SYG237', {
118
+ component,
119
+ message: `${name}'s forms '${sels[sel]}' and '${key}' both listen on '${sel}', so each hears the other's fields and submits`,
120
+ fix: `Give each form element its own class and pass it: ${sels[sel]}: form(schema, { ..., form: '.${sels[sel]}' }), ${key}: form(schema, { ..., form: '.${key}' })`,
121
+ data: {keys: [sels[sel], key], form: sel},
122
+ })
123
+ } else sels[sel] = key
124
+ if (!once(`SYG23x:${name}:${key}`)) continue
125
+ const o = b.options || {}, values = o.values || {}
126
+ const model = Object.keys(view.model || {}).map(a => a.split('|')[0].trim()), own = ownActions(b)
127
+ const clash = own.includes(o.submit)
128
+ if (typeof o.submit != 'string' || !model.includes(o.submit) || clash) {
129
+ devReport('SYG234', {
130
+ component,
131
+ message: typeof o.submit != 'string' ? `${name}'s form '${key}' has no submit action, so a valid submit dispatches nothing`
132
+ : clash ? `${name}'s form '${key}' names submit: '${o.submit}', one of the form's own actions; a valid submit dispatches the form's '${key}.${o.submit}'${model.includes(o.submit) ? `, never ${name}'s '${o.submit}' entry` : ''}`
133
+ : `${name}'s form '${key}' dispatches '${o.submit}' on a valid submit, but ${name} has no model entry '${o.submit}'`,
134
+ fix: clash ? SUBMIT_FIX(key, o.submit, own)
135
+ : `Name a host action and add it to the model: submit: 'SIGN_UP', model: { SIGN_UP: { HTTP: (state, values) => ({ url, json: values, ok: '${key}.DONE', error: '${key}.ERRORS' }) } }`,
136
+ data: {key, submit: o.submit},
137
+ })
138
+ }
139
+ for (const f of Object.keys(o.check || {})) {
140
+ if (!hasField(values, f)) devReport('SYG235', {
141
+ component,
142
+ message: `${name}'s form '${key}' has a check for '${f}', which isn't a field of its values; it never runs`,
143
+ fix: `Use a field name from values (${Object.keys(values).join(', ')})`,
144
+ data: {key, field: f},
145
+ })
146
+ }
147
+ for (const at of missingIds(values)) devReport('SYG236', {
148
+ component,
149
+ message: `${name}'s form '${key}': values.${at} has rows without an id, so its fields are named by index and errors, touched state and focus don't follow a row when another is removed`,
150
+ fix: `Give each row a unique id: ${at}: [{ id: 1, ... }] (form.ADD numbers new rows itself)`,
151
+ data: {key, field: at},
152
+ })
153
+ }
154
+ },
155
+ }
156
+
157
+ /** Install the `form` hook on the core bridge (form.ts calls it). */
158
+ export function installFormHooks(): () => void {
159
+ const c = (globalThis as any).__SYGNAL_DIAGNOSTICS__
160
+ if (!c) return () => {}
161
+ c.form = reportForm
162
+ return () => { if (c.form === reportForm) c.form = undefined }
163
+ }