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.
- package/CHANGELOG.md +646 -0
- package/README.md +77 -47
- package/dist/astro/client.cjs.js +33 -8114
- package/dist/astro/client.cjs.js.map +1 -0
- package/dist/astro/client.mjs +33 -8114
- package/dist/astro/client.mjs.map +1 -0
- package/dist/astro/index.cjs.js +1039 -9
- package/dist/astro/index.cjs.js.map +1 -0
- package/dist/astro/index.mjs +1038 -9
- package/dist/astro/index.mjs.map +1 -0
- package/dist/astro/server.cjs.js +3400 -166
- package/dist/astro/server.cjs.js.map +1 -0
- package/dist/astro/server.mjs +3400 -166
- package/dist/astro/server.mjs.map +1 -0
- package/dist/devtools.cjs.js +1431 -0
- package/dist/devtools.cjs.js.map +1 -0
- package/dist/devtools.esm.js +1419 -0
- package/dist/devtools.esm.js.map +1 -0
- package/dist/diagnostics.cjs.js +4033 -0
- package/dist/diagnostics.cjs.js.map +1 -0
- package/dist/diagnostics.esm.js +4003 -0
- package/dist/diagnostics.esm.js.map +1 -0
- package/dist/element.cjs.js +230 -0
- package/dist/element.cjs.js.map +1 -0
- package/dist/element.esm.js +228 -0
- package/dist/element.esm.js.map +1 -0
- package/dist/guide/accessibility.md +257 -0
- package/dist/guide/adapters.md +221 -0
- package/dist/guide/behaviors.md +312 -0
- package/dist/guide/browser-sources.md +203 -0
- package/dist/guide/drag-and-drop.md +342 -0
- package/dist/guide/element-commands.md +306 -0
- package/dist/guide/error-boundaries.md +169 -0
- package/dist/guide/forms-reference.md +381 -0
- package/dist/guide/forms.md +250 -0
- package/dist/guide/http.md +299 -0
- package/dist/guide/inputs.md +173 -0
- package/dist/guide/persistence.md +245 -0
- package/dist/guide/recipes/carousel.md +155 -0
- package/dist/guide/recipes/charts.md +177 -0
- package/dist/guide/recipes/code-editor.md +135 -0
- package/dist/guide/recipes/data-grid.md +155 -0
- package/dist/guide/recipes/data-table.md +196 -0
- package/dist/guide/recipes/i18n.md +222 -0
- package/dist/guide/recipes/icons.md +115 -0
- package/dist/guide/recipes/overview.md +33 -0
- package/dist/guide/recipes/rich-text.md +154 -0
- package/dist/guide/resources.md +471 -0
- package/dist/guide/ssr.md +255 -0
- package/dist/guide/timers.md +178 -0
- package/dist/guide/ui/accordion.md +93 -0
- package/dist/guide/ui/combobox.md +144 -0
- package/dist/guide/ui/dialog.md +124 -0
- package/dist/guide/ui/disclosure.md +60 -0
- package/dist/guide/ui/menu.md +125 -0
- package/dist/guide/ui/overview.md +166 -0
- package/dist/guide/ui/popover.md +101 -0
- package/dist/guide/ui/select.md +114 -0
- package/dist/guide/ui/tabs.md +161 -0
- package/dist/guide/ui/toaster.md +152 -0
- package/dist/guide/ui/tooltip.md +103 -0
- package/dist/guide/undo.md +178 -0
- package/dist/guide/virtual-collections.md +183 -0
- package/dist/guide/web-components.md +179 -0
- package/dist/guide/widgets.md +217 -0
- package/dist/index.cjs.js +15283 -6279
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +3739 -370
- package/dist/index.esm.js +15230 -6274
- package/dist/index.esm.js.map +1 -0
- package/dist/jsx-dev-runtime.cjs.js +27 -329
- package/dist/jsx-dev-runtime.cjs.js.map +1 -0
- package/dist/jsx-dev-runtime.esm.js +20 -322
- package/dist/jsx-dev-runtime.esm.js.map +1 -0
- package/dist/jsx-runtime.cjs.js +27 -329
- package/dist/jsx-runtime.cjs.js.map +1 -0
- package/dist/jsx-runtime.esm.js +20 -322
- package/dist/jsx-runtime.esm.js.map +1 -0
- package/dist/jsx.cjs.js +11 -309
- package/dist/jsx.cjs.js.map +1 -0
- package/dist/jsx.esm.js +9 -310
- package/dist/jsx.esm.js.map +1 -0
- package/dist/react.cjs.js +117 -0
- package/dist/react.cjs.js.map +1 -0
- package/dist/react.esm.js +115 -0
- package/dist/react.esm.js.map +1 -0
- package/dist/shims/globalthis.cjs +20 -0
- package/dist/sygnal.min.js +2 -1
- package/dist/sygnal.min.js.map +1 -0
- package/dist/ui-combobox.cjs.js +169 -0
- package/dist/ui-combobox.cjs.js.map +1 -0
- package/dist/ui-combobox.esm.js +148 -0
- package/dist/ui-combobox.esm.js.map +1 -0
- package/dist/ui-menu.cjs.js +87 -0
- package/dist/ui-menu.cjs.js.map +1 -0
- package/dist/ui-menu.esm.js +66 -0
- package/dist/ui-menu.esm.js.map +1 -0
- package/dist/ui-select.cjs.js +104 -0
- package/dist/ui-select.cjs.js.map +1 -0
- package/dist/ui-select.esm.js +83 -0
- package/dist/ui-select.esm.js.map +1 -0
- package/dist/ui.cjs.js +738 -0
- package/dist/ui.cjs.js.map +1 -0
- package/dist/ui.esm.js +727 -0
- package/dist/ui.esm.js.map +1 -0
- package/dist/vike/ClientOnly.cjs.js +5 -14
- package/dist/vike/ClientOnly.cjs.js.map +1 -0
- package/dist/vike/ClientOnly.mjs +5 -14
- package/dist/vike/ClientOnly.mjs.map +1 -0
- package/dist/vike/{+config.js → config/+config.js} +10 -1
- package/dist/vike/config/+config.js.map +1 -0
- package/dist/vike/config/package.json +6 -0
- package/dist/vike/onRenderClient.cjs.js +166 -95
- package/dist/vike/onRenderClient.cjs.js.map +1 -0
- package/dist/vike/onRenderClient.mjs +166 -95
- package/dist/vike/onRenderClient.mjs.map +1 -0
- package/dist/vike/onRenderHtml.cjs.js +60 -24
- package/dist/vike/onRenderHtml.cjs.js.map +1 -0
- package/dist/vike/onRenderHtml.mjs +61 -25
- package/dist/vike/onRenderHtml.mjs.map +1 -0
- package/dist/vite/plugin.cjs.js +926 -65
- package/dist/vite/plugin.cjs.js.map +1 -0
- package/dist/vite/plugin.mjs +925 -65
- package/dist/vite/plugin.mjs.map +1 -0
- package/dist/zag.cjs.js +408 -0
- package/dist/zag.cjs.js.map +1 -0
- package/dist/zag.esm.js +405 -0
- package/dist/zag.esm.js.map +1 -0
- package/llms.txt +313 -0
- package/package.json +109 -16
- package/src/astro/client.ts +40 -18
- package/src/astro/index.d.ts +48 -1
- package/src/astro/index.ts +106 -9
- package/src/astro/server.ts +11 -3
- package/src/collection.ts +6 -90
- package/src/core/actions.ts +134 -0
- package/src/core/cell.ts +202 -0
- package/src/core/debug.ts +23 -0
- package/src/core/define.ts +242 -0
- package/src/core/hooks.ts +314 -0
- package/src/core/hosts/collection.ts +325 -0
- package/src/core/hosts/switchable.ts +146 -0
- package/src/core/instance.ts +592 -0
- package/src/core/markers/clientonly.ts +13 -0
- package/src/core/markers/lazy.ts +40 -0
- package/src/core/markers/portal.ts +96 -0
- package/src/core/markers/suspense.ts +41 -0
- package/src/core/markers/transition.ts +70 -0
- package/src/core/registry.ts +25 -0
- package/src/core/runtime.ts +573 -0
- package/src/core/statics.ts +104 -0
- package/src/core/teardown.ts +60 -0
- package/src/core/view.ts +37 -0
- package/src/cycle/dom/DocumentDOMSource.ts +13 -8
- package/src/cycle/dom/ElementFinder.ts +7 -9
- package/src/cycle/dom/EventDelegator.ts +174 -223
- package/src/cycle/dom/IsolateModule.ts +65 -35
- package/src/cycle/dom/MainDOMSource.ts +13 -2
- package/src/cycle/dom/PriorityQueue.ts +4 -2
- package/src/cycle/dom/SymbolTree.ts +18 -47
- package/src/cycle/dom/classNameModule.ts +76 -0
- package/src/cycle/dom/controlledInputModule.ts +50 -0
- package/src/cycle/dom/enrichEventStream.ts +29 -45
- package/src/cycle/dom/fragment.ts +7 -0
- package/src/cycle/dom/isolate.ts +30 -27
- package/src/cycle/dom/makeDOMDriver.ts +115 -57
- package/src/cycle/dom/mockDOMSource.ts +83 -9
- package/src/cycle/dom/modules.ts +21 -4
- package/src/cycle/dom/propsModule.ts +65 -0
- package/src/cycle/dom/selectModule.ts +22 -17
- package/src/cycle/dom/snabbdom.ts +1 -6
- package/src/cycle/dom/thunk.ts +3 -0
- package/src/cycle/dom/utils.ts +98 -0
- package/src/cycle/dom/viewTransition.ts +43 -0
- package/src/cycle/state/StateSource.ts +20 -4
- package/src/cycle/state/objIsEqual.ts +50 -0
- package/src/cycle/state/types.ts +0 -10
- package/src/defineComponent.ts +34 -0
- package/src/devtools.d.ts +151 -0
- package/src/devtools.ts +34 -0
- package/src/element.d.ts +59 -0
- package/src/element.ts +235 -0
- package/src/extra/backoff.ts +9 -0
- package/src/extra/behaviors.ts +169 -0
- package/src/extra/browserSignals.ts +23 -0
- package/src/extra/browserSources.ts +326 -0
- package/src/extra/command.ts +3 -3
- package/src/extra/controls.ts +47 -0
- package/src/extra/copyAsTest.ts +286 -0
- package/src/extra/devtools.ts +109 -31
- package/src/extra/devtoolsActions.ts +379 -0
- package/src/extra/devtoolsHook.ts +9 -0
- package/src/extra/devtoolsNext.ts +83 -0
- package/src/extra/diagnostics/checks/actionLog.ts +108 -0
- package/src/extra/diagnostics/checks/behaviors.ts +49 -0
- package/src/extra/diagnostics/checks/browserSources.ts +96 -0
- package/src/extra/diagnostics/checks/collections.ts +67 -0
- package/src/extra/diagnostics/checks/controls.ts +148 -0
- package/src/extra/diagnostics/checks/dataset.ts +57 -0
- package/src/extra/diagnostics/checks/dom.ts +250 -0
- package/src/extra/diagnostics/checks/elementCommands.ts +175 -0
- package/src/extra/diagnostics/checks/events.ts +83 -0
- package/src/extra/diagnostics/checks/fetch.ts +135 -0
- package/src/extra/diagnostics/checks/forms.ts +163 -0
- package/src/extra/diagnostics/checks/index.ts +202 -0
- package/src/extra/diagnostics/checks/inspect.ts +427 -0
- package/src/extra/diagnostics/checks/next.ts +417 -0
- package/src/extra/diagnostics/checks/persist.ts +54 -0
- package/src/extra/diagnostics/checks/props.ts +56 -0
- package/src/extra/diagnostics/checks/public.d.ts +302 -0
- package/src/extra/diagnostics/checks/replies.ts +128 -0
- package/src/extra/diagnostics/checks/router.ts +100 -0
- package/src/extra/diagnostics/checks/rxjsHints.ts +88 -0
- package/src/extra/diagnostics/checks/shared.ts +203 -0
- package/src/extra/diagnostics/checks/shorthand.ts +115 -0
- package/src/extra/diagnostics/checks/sortable.ts +129 -0
- package/src/extra/diagnostics/checks/state.ts +148 -0
- package/src/extra/diagnostics/checks/statics.ts +48 -0
- package/src/extra/diagnostics/checks/strict.ts +55 -0
- package/src/extra/diagnostics/checks/timers.ts +80 -0
- package/src/extra/diagnostics/checks/viewTransitions.ts +47 -0
- package/src/extra/diagnostics/checks/virtual.ts +73 -0
- package/src/extra/diagnostics/checks/widgets.ts +109 -0
- package/src/extra/diagnostics/checks/wiring.ts +121 -0
- package/src/extra/diagnostics/codes.ts +477 -0
- package/src/extra/diagnostics/index.ts +353 -0
- package/src/extra/diagnostics/legacy.ts +76 -0
- package/src/extra/driverFactories.ts +172 -60
- package/src/extra/elementCommands.ts +53 -0
- package/src/extra/eventDriver.ts +4 -0
- package/src/extra/fetchDriver.ts +519 -0
- package/src/extra/flatten.ts +75 -0
- package/src/extra/focusWithin.ts +27 -0
- package/src/extra/form.ts +268 -0
- package/src/extra/formHelpers.ts +139 -0
- package/src/extra/head.ts +106 -0
- package/src/extra/hmr.ts +2 -3
- package/src/extra/owned.ts +24 -0
- package/src/extra/pager.ts +50 -0
- package/src/extra/persist.ts +133 -0
- package/src/extra/pwa.ts +1 -1
- package/src/extra/queryCache.ts +107 -0
- package/src/extra/reducers.ts +13 -6
- package/src/extra/reduxDevtools.ts +92 -0
- package/src/extra/replies.ts +54 -0
- package/src/extra/router.ts +323 -0
- package/src/extra/run.ts +94 -210
- package/src/extra/selection.ts +76 -0
- package/src/extra/socketDriver.ts +265 -0
- package/src/extra/sortable.ts +377 -0
- package/src/extra/ssr.ts +427 -160
- package/src/extra/standardSchema.ts +27 -0
- package/src/extra/testing.ts +3158 -190
- package/src/extra/timers.ts +95 -0
- package/src/extra/undo.ts +261 -0
- package/src/extra/viewTransitions.ts +38 -0
- package/src/extra/virtual.ts +513 -0
- package/src/extra/widget.ts +201 -0
- package/src/extra/xstreamExtras.ts +269 -0
- package/src/index.d.ts +3012 -87
- package/src/index.ts +32 -10
- package/src/jsx-runtime.ts +15 -1
- package/src/jsx.ts +2 -1
- package/src/lazy.ts +50 -7
- package/src/portal.ts +3 -1
- package/src/pragma/index.ts +262 -134
- package/src/react-peers.d.ts +5 -0
- package/src/react.d.ts +43 -0
- package/src/react.ts +110 -0
- package/src/shared.ts +48 -0
- package/src/slot.ts +1 -1
- package/src/suspense.ts +3 -1
- package/src/switchable.ts +6 -119
- package/src/transition.ts +3 -1
- package/src/ui/accordion.ts +64 -0
- package/src/ui/dialog.ts +129 -0
- package/src/ui/disclosure.ts +35 -0
- package/src/ui/popover.ts +45 -0
- package/src/ui/shared.ts +94 -0
- package/src/ui/tabs.ts +82 -0
- package/src/ui/toaster.ts +198 -0
- package/src/ui/tooltip.ts +70 -0
- package/src/ui/zag/combobox.ts +115 -0
- package/src/ui/zag/menu.ts +40 -0
- package/src/ui/zag/select.ts +54 -0
- package/src/ui/zag/shared.ts +43 -0
- package/src/ui-combobox.d.ts +20 -0
- package/src/ui-combobox.ts +6 -0
- package/src/ui-menu.d.ts +30 -0
- package/src/ui-menu.ts +6 -0
- package/src/ui-select.d.ts +17 -0
- package/src/ui-select.ts +6 -0
- package/src/ui-zag-types.d.ts +42 -0
- package/src/ui.d.ts +208 -0
- package/src/ui.ts +15 -0
- package/src/vike/+config.ts +9 -1
- package/src/vike/ClientOnly.ts +1 -1
- package/src/vike/onRenderClient.ts +144 -96
- package/src/vike/onRenderHtml.ts +69 -26
- package/src/vike/types.ts +27 -4
- package/src/vite/globalthis-shim.ts +18 -0
- package/src/vite/globalthis.ts +56 -0
- package/src/vite/plugin.d.ts +114 -2
- package/src/vite/plugin.ts +920 -56
- package/src/zag.d.ts +64 -0
- package/src/zag.ts +238 -0
- package/dist/astro/astro/client.d.ts +0 -5
- package/dist/astro/astro/index.d.ts +0 -16
- package/dist/astro/astro/server.d.ts +0 -12
- package/dist/astro/client.d.ts +0 -5
- package/dist/astro/client.esm.js +0 -4665
- package/dist/astro/collection.d.ts +0 -12
- package/dist/astro/component.d.ts +0 -22
- package/dist/astro/cycle/dom/BodyDOMSource.d.ts +0 -10
- package/dist/astro/cycle/dom/DOMSource.d.ts +0 -11
- package/dist/astro/cycle/dom/DocumentDOMSource.d.ts +0 -12
- package/dist/astro/cycle/dom/ElementFinder.d.ts +0 -8
- package/dist/astro/cycle/dom/EventDelegator.d.ts +0 -34
- package/dist/astro/cycle/dom/IsolateModule.d.ts +0 -23
- package/dist/astro/cycle/dom/MainDOMSource.d.ts +0 -32
- package/dist/astro/cycle/dom/PriorityQueue.d.ts +0 -7
- package/dist/astro/cycle/dom/ScopeChecker.d.ts +0 -9
- package/dist/astro/cycle/dom/SymbolTree.d.ts +0 -9
- package/dist/astro/cycle/dom/VNodeWrapper.d.ts +0 -8
- package/dist/astro/cycle/dom/enrichEventStream.d.ts +0 -24
- package/dist/astro/cycle/dom/fromEvent.d.ts +0 -6
- package/dist/astro/cycle/dom/index.d.ts +0 -8
- package/dist/astro/cycle/dom/isolate.d.ts +0 -9
- package/dist/astro/cycle/dom/makeDOMDriver.d.ts +0 -11
- package/dist/astro/cycle/dom/mockDOMSource.d.ts +0 -17
- package/dist/astro/cycle/dom/modules.d.ts +0 -5
- package/dist/astro/cycle/dom/snabbdom.d.ts +0 -22
- package/dist/astro/cycle/dom/styleModule.d.ts +0 -13
- package/dist/astro/cycle/dom/thunk.d.ts +0 -11
- package/dist/astro/cycle/dom/utils.d.ts +0 -8
- package/dist/astro/cycle/isolate/index.d.ts +0 -42
- package/dist/astro/cycle/run/adapt.d.ts +0 -6
- package/dist/astro/cycle/run/index.d.ts +0 -36
- package/dist/astro/cycle/run/internals.d.ts +0 -8
- package/dist/astro/cycle/run/types.d.ts +0 -49
- package/dist/astro/cycle/state/Collection.d.ts +0 -29
- package/dist/astro/cycle/state/StateSource.d.ts +0 -20
- package/dist/astro/cycle/state/index.d.ts +0 -5
- package/dist/astro/cycle/state/pickCombine.d.ts +0 -3
- package/dist/astro/cycle/state/pickMerge.d.ts +0 -3
- package/dist/astro/cycle/state/types.d.ts +0 -18
- package/dist/astro/cycle/state/withState.d.ts +0 -17
- package/dist/astro/extra/classes.d.ts +0 -7
- package/dist/astro/extra/devtools.d.ts +0 -78
- package/dist/astro/extra/dragDriver.d.ts +0 -20
- package/dist/astro/extra/driverFactories.d.ts +0 -12
- package/dist/astro/extra/eventDriver.d.ts +0 -9
- package/dist/astro/extra/exactState.d.ts +0 -1
- package/dist/astro/extra/hmr.d.ts +0 -17
- package/dist/astro/extra/logDriver.d.ts +0 -2
- package/dist/astro/extra/processDrag.d.ts +0 -19
- package/dist/astro/extra/processForm.d.ts +0 -15
- package/dist/astro/extra/run.d.ts +0 -13
- package/dist/astro/extra/xstreamCompat.d.ts +0 -5
- package/dist/astro/index.d.ts +0 -19
- package/dist/astro/index.esm.js +0 -27
- package/dist/astro/jsx-dev-runtime.d.ts +0 -1
- package/dist/astro/jsx-runtime.d.ts +0 -4
- package/dist/astro/jsx.d.ts +0 -2
- package/dist/astro/pragma/fn.d.ts +0 -7
- package/dist/astro/pragma/index.d.ts +0 -7
- package/dist/astro/pragma/is.d.ts +0 -10
- package/dist/astro/server.d.ts +0 -12
- package/dist/astro/server.esm.js +0 -25
- package/dist/astro/switchable.d.ts +0 -7
- package/dist/collection.d.ts +0 -12
- package/dist/component.d.ts +0 -22
- package/dist/cycle/dom/BodyDOMSource.d.ts +0 -10
- package/dist/cycle/dom/DOMSource.d.ts +0 -11
- package/dist/cycle/dom/DocumentDOMSource.d.ts +0 -12
- package/dist/cycle/dom/ElementFinder.d.ts +0 -8
- package/dist/cycle/dom/EventDelegator.d.ts +0 -34
- package/dist/cycle/dom/IsolateModule.d.ts +0 -23
- package/dist/cycle/dom/MainDOMSource.d.ts +0 -32
- package/dist/cycle/dom/PriorityQueue.d.ts +0 -7
- package/dist/cycle/dom/ScopeChecker.d.ts +0 -9
- package/dist/cycle/dom/SymbolTree.d.ts +0 -9
- package/dist/cycle/dom/VNodeWrapper.d.ts +0 -8
- package/dist/cycle/dom/enrichEventStream.d.ts +0 -24
- package/dist/cycle/dom/fromEvent.d.ts +0 -6
- package/dist/cycle/dom/index.d.ts +0 -8
- package/dist/cycle/dom/isolate.d.ts +0 -9
- package/dist/cycle/dom/makeDOMDriver.d.ts +0 -11
- package/dist/cycle/dom/mockDOMSource.d.ts +0 -17
- package/dist/cycle/dom/modules.d.ts +0 -5
- package/dist/cycle/dom/snabbdom.d.ts +0 -22
- package/dist/cycle/dom/styleModule.d.ts +0 -13
- package/dist/cycle/dom/thunk.d.ts +0 -11
- package/dist/cycle/dom/utils.d.ts +0 -8
- package/dist/cycle/isolate/index.d.ts +0 -42
- package/dist/cycle/run/adapt.d.ts +0 -6
- package/dist/cycle/run/index.d.ts +0 -36
- package/dist/cycle/run/internals.d.ts +0 -8
- package/dist/cycle/run/types.d.ts +0 -49
- package/dist/cycle/state/Collection.d.ts +0 -29
- package/dist/cycle/state/StateSource.d.ts +0 -20
- package/dist/cycle/state/index.d.ts +0 -5
- package/dist/cycle/state/pickCombine.d.ts +0 -3
- package/dist/cycle/state/pickMerge.d.ts +0 -3
- package/dist/cycle/state/types.d.ts +0 -18
- package/dist/cycle/state/withState.d.ts +0 -17
- package/dist/extra/classes.d.ts +0 -7
- package/dist/extra/devtools.d.ts +0 -78
- package/dist/extra/dragDriver.d.ts +0 -20
- package/dist/extra/driverFactories.d.ts +0 -12
- package/dist/extra/eventDriver.d.ts +0 -9
- package/dist/extra/exactState.d.ts +0 -1
- package/dist/extra/hmr.d.ts +0 -17
- package/dist/extra/logDriver.d.ts +0 -2
- package/dist/extra/processDrag.d.ts +0 -19
- package/dist/extra/processForm.d.ts +0 -15
- package/dist/extra/run.d.ts +0 -13
- package/dist/extra/xstreamCompat.d.ts +0 -5
- package/dist/jsx-dev-runtime.d.ts +0 -1
- package/dist/jsx-dev-runtime.js +0 -224
- package/dist/jsx-runtime.d.ts +0 -4
- package/dist/jsx-runtime.js +0 -224
- package/dist/jsx.d.ts +0 -2
- package/dist/pragma/fn.d.ts +0 -7
- package/dist/pragma/index.d.ts +0 -7
- package/dist/pragma/is.d.ts +0 -10
- package/dist/switchable.d.ts +0 -7
- package/dist/vike/+config.cjs.js +0 -65
- package/src/component.ts +0 -2256
- package/src/cycle/isolate/index.ts +0 -196
- package/src/cycle/run/index.ts +0 -151
- package/src/cycle/run/internals.ts +0 -143
- package/src/cycle/state/Collection.ts +0 -174
- package/src/cycle/state/index.ts +0 -5
- package/src/cycle/state/pickCombine.ts +0 -167
- package/src/cycle/state/pickMerge.ts +0 -122
- package/src/cycle/state/withState.ts +0 -47
- package/src/pragma/fn.ts +0 -55
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLAN-4.6 R4: the dev entry's checks on the next component core (src/core/).
|
|
3
|
+
*
|
|
4
|
+
* The core has no instance to patch and no diagnostics calls of its own: it reads this
|
|
5
|
+
* entry's hooks once per app (`__SYGNAL_DIAGNOSTICS__.nextHooks(api)`, 04-hooks-contract §2.2)
|
|
6
|
+
* and calls them with read-only InstanceViews. This module maps them onto the existing checks:
|
|
7
|
+
*
|
|
8
|
+
* - each instance gets a facade with the fields the checks read from today's instances (name,
|
|
9
|
+
* view, model, sources, currentState, ...), so the DiagnosticCheck hooks (onIntent, onModel,
|
|
10
|
+
* onRender, onReducer, onDispose, sources) run unchanged through the core's dispatchers;
|
|
11
|
+
* - what the checks did by patching an instance becomes a hook: SYG222's reducer wrapping
|
|
12
|
+
* (wrapHandler), the reply / fetch / EVENTS taps of a sink (onSink), the ELEMENT check
|
|
13
|
+
* (onElementCommand), the Collection check (onHostProps), the action log (actionLog.ts);
|
|
14
|
+
* - the core's own dev codes: SYG423 (D168: a view context tracking skipped would have
|
|
15
|
+
* rendered differently), SYG424 (D169/D177: duplicate Collection ids), SYG425 (D174: an
|
|
16
|
+
* isolatedState child kept a slice that lacks its initialState keys), SYG148 / SYG149 (G-419:
|
|
17
|
+
* a Collection's viewTransitionName that isn't an identifier, the same name twice on the
|
|
18
|
+
* page after a patch, G-460: on rendered elements), SYG612 (D173: a form
|
|
19
|
+
* 6.0 removed, met at runtime; once per form and component, with a link to the migration
|
|
20
|
+
* guide).
|
|
21
|
+
*
|
|
22
|
+
* Only while diagnostics are on (the checks' own rule); production builds never load this.
|
|
23
|
+
*/
|
|
24
|
+
import {bridge, devReport, reportSafely, once, onReset, nameOf, isPlainObject} from './shared'
|
|
25
|
+
import {DEV_CODE_SEVERITY} from '../codes'
|
|
26
|
+
import {watchMutation} from './state'
|
|
27
|
+
import {checkRequest} from './replies'
|
|
28
|
+
import {check as checkFetch} from './fetch'
|
|
29
|
+
import {checkSentCommand} from './elementCommands'
|
|
30
|
+
import {checkCollection} from './collections'
|
|
31
|
+
import {eventEmitted, actionListener as recentActions} from './inspect'
|
|
32
|
+
import {actionHooks} from './actionLog'
|
|
33
|
+
|
|
34
|
+
const MIGRATE = 'https://sygnal.js.org/guide/migrating-to-6'
|
|
35
|
+
|
|
36
|
+
/** the removed forms (D162-D164) by migration-guide anchor: what it was, how to write it now */
|
|
37
|
+
export const REMOVED: Record<string, [what: string, fix: string]> = {
|
|
38
|
+
'string-tags': ['a component named by a string tag (<Name /> as a string, looked up in .components)', 'Import the component and use it as a JSX tag: <Badge />'],
|
|
39
|
+
'components': ['the .components registry', 'Import each component and use it as a JSX tag; remove .components'],
|
|
40
|
+
'collection-of-name': ['<Collection of="Name"> (a name)', 'Pass the component itself: <Collection of={Item} from="items" />'],
|
|
41
|
+
'collection-wrapper': ["the Collection's wrapper element (className, style, class, attrs, data-*, on, ref or hook on <Collection>)", 'Collection renders its items directly into its parent: put them on your own wrapping element, <ul className="x"><Collection of={Item} from="items" /></ul>'],
|
|
42
|
+
'child-select-name': ["CHILD.select('Name') (a name)", 'Pass the component itself: CHILD.select(Item)'],
|
|
43
|
+
'pipe-keys': ["'ACTION | SINK' model keys", "Use the object form: ACTION: { SINK: (state, data) => ... }"],
|
|
44
|
+
'peers': ['.peers', 'Render the peer as a sibling in the parent view'],
|
|
45
|
+
'hmractions': ['hmrActions', 'Remove hmrActions; sygnal/vite keeps the state across a hot update'],
|
|
46
|
+
'positional-views': ['a view with positional parameters (props, state, context, peers)', 'Destructure the one argument: function C({ state, context, ...props })'],
|
|
47
|
+
'source-names': ['DOMSourceName / stateSourceName', "Remove them: the sources are always DOM and STATE"],
|
|
48
|
+
'storecalculatedinstate': ['storeCalculatedInState', 'Remove it: calculated fields are always part of the state the view and reducers get'],
|
|
49
|
+
'component-factory': ['the component({ ... }) factory (with sources / isolateOpts)', 'Write a function component with statics (C.model, C.intent, C.initialState), or use defineComponent({ ... })'],
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 4-H: the marker's props / data buckets the removed Collection wrapper element took */
|
|
53
|
+
const WRAPPER = ['className', 'style', 'class', 'attrs', 'dataset', 'on', 'hook']
|
|
54
|
+
|
|
55
|
+
const STATICS: Array<[string, string]> = [['components', 'components'], ['peers', 'peers'], ['hmrActions', 'hmractions'], ['DOMSourceName', 'source-names'], ['stateSourceName', 'source-names'], ['storeCalculatedInState', 'storecalculatedinstate']]
|
|
56
|
+
|
|
57
|
+
function removed(component: any, name: string, anchor: string, detail?: string): void {
|
|
58
|
+
if (!once(`SYG612:${name}:${anchor}`)) return
|
|
59
|
+
const [what, fix] = REMOVED[anchor]
|
|
60
|
+
devReport('SYG612', {
|
|
61
|
+
component: name,
|
|
62
|
+
message: `${name} uses ${what}${detail ? ` (${detail})` : ''}, which Sygnal 6.0 removed, so it is ignored or fails`,
|
|
63
|
+
fix: `${fix}. See ${MIGRATE}#${anchor}`,
|
|
64
|
+
data: {form: anchor, guide: `${MIGRATE}#${anchor}`, ...(detail ? {detail} : {})},
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** the removed forms a definition shows (statics, view arity, model keys), once per function */
|
|
69
|
+
let seenDefs = new WeakSet<object>()
|
|
70
|
+
// G-332: resetChecks() forgets them (as the once() dedupe)
|
|
71
|
+
onReset(() => { seenDefs = new WeakSet<object>() })
|
|
72
|
+
function removedStatics(f: any): void {
|
|
73
|
+
const view = f.view
|
|
74
|
+
if (!view || seenDefs.has(view)) return
|
|
75
|
+
seenDefs.add(view)
|
|
76
|
+
const name = f.name
|
|
77
|
+
if (view.isSygnalComponent) removed(f, name, 'component-factory')
|
|
78
|
+
for (const [k, anchor] of STATICS) if (view[k] !== undefined && !(k == 'components' && isPlainObject(view[k]) && !Object.keys(view[k]).length)) removed(f, name, anchor, k)
|
|
79
|
+
// G-343: a defineComponent() wrapper's own arity is 1; the view it calls is __sygnalView
|
|
80
|
+
const arity = typeof view == 'function' ? (view.__sygnalView || view).length : 0
|
|
81
|
+
if (arity > 1 && !view.__sygnalLazy && !view.isSygnalComponent) removed(f, name, 'positional-views', `${arity} parameters`)
|
|
82
|
+
const model = view.model
|
|
83
|
+
if (isPlainObject(model)) for (const k of Object.keys(model)) if (k.includes('|')) removed(f, name, 'pipe-keys', `'${k}'`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** string tags left in a rendered vnode (the core renders them as elements) */
|
|
87
|
+
function removedTags(f: any, v: any, depth = 0): void {
|
|
88
|
+
if (!v || typeof v != 'object' || depth > 300) return
|
|
89
|
+
const sel = v.sel
|
|
90
|
+
// G-329: a component name is PascalCase; an all-caps tag (h('SPAN'), h(el.tagName)) is an element
|
|
91
|
+
const tag = typeof sel == 'string' ? sel.split(/[.#]/)[0] : ''
|
|
92
|
+
if (/^[A-Z]/.test(tag) && /[a-z]/.test(tag) && !(v.data && v.data.c)) removed(f, f.name, 'string-tags', `<${tag}>`)
|
|
93
|
+
const kids = v.children
|
|
94
|
+
if (Array.isArray(kids)) for (const k of kids) removedTags(f, k, depth + 1)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** the instance fields the checks read from today's Component instances */
|
|
98
|
+
class Facade {
|
|
99
|
+
_so: any
|
|
100
|
+
constructor(public __next: any) {}
|
|
101
|
+
get name() { return this.__next.name }
|
|
102
|
+
get view() { return this.__next.def.view }
|
|
103
|
+
get model() {
|
|
104
|
+
const m: Record<string, any> = {}
|
|
105
|
+
this.__next.def.handlers.forEach((hs: any[], a: string) => { const e: any = m[a] = {}; for (const [s, fn] of hs) e[s] = fn })
|
|
106
|
+
return m
|
|
107
|
+
}
|
|
108
|
+
get modelMap(): Record<string, string[]> {
|
|
109
|
+
const m: Record<string, string[]> = {}
|
|
110
|
+
this.__next.def.handlers.forEach((hs: any[], a: string) => { m[a] = hs.map(h => h[0]) })
|
|
111
|
+
// the built-in INITIALIZE (today's model map ends with it for every component)
|
|
112
|
+
if (!m.INITIALIZE) m.INITIALIZE = ['STATE']
|
|
113
|
+
return m
|
|
114
|
+
}
|
|
115
|
+
get sources() { return this._so || this.__next.sources }
|
|
116
|
+
get sourceNames() { return Object.keys(this.__next.sources) }
|
|
117
|
+
get stateSourceName() { return 'STATE' }
|
|
118
|
+
get DOMSourceName() { return 'DOM' }
|
|
119
|
+
get isSubComponent() { return !this.__next.isRoot }
|
|
120
|
+
get initialState() { return this.__next.def.initialState }
|
|
121
|
+
get currentState() { return this.__next.state }
|
|
122
|
+
get currentProps() { return this.__next.props }
|
|
123
|
+
get currentContext() { return this.__next.context }
|
|
124
|
+
get _componentNumber() { return this.__next.id }
|
|
125
|
+
get calculated() { return this.__next.def.view.calculated }
|
|
126
|
+
get _calculatedFieldNames() { return this.__next.def.calcNames }
|
|
127
|
+
get context() { return this.__next.def.view.context }
|
|
128
|
+
get _uses() { return Object.entries(this.__next.def.view.uses || {}) }
|
|
129
|
+
get _behaviorActions() { return this.__next.def.behaviorActions }
|
|
130
|
+
get intent$() { return {__sygnalTestActions: this.__next.def.testActions || []} }
|
|
131
|
+
get _disposed() { return this.__next.disposed }
|
|
132
|
+
/** the state already has its calculated fields */
|
|
133
|
+
addCalculated(s: any) { return s }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const facades = new WeakMap<object, Facade>()
|
|
137
|
+
const fac = (iv: any): Facade => {
|
|
138
|
+
let f = facades.get(iv)
|
|
139
|
+
if (!f) facades.set(iv, f = new Facade(iv))
|
|
140
|
+
return f
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** structural vnode equality (functions equal: a view may make new closures every call) */
|
|
144
|
+
function same(a: any, b: any, d = 0): boolean {
|
|
145
|
+
if (a === b || (typeof a == 'function' && typeof b == 'function')) return true
|
|
146
|
+
if (!a || !b || typeof a != 'object' || typeof b != 'object') return a !== a && b !== b
|
|
147
|
+
if (d > 300) return true
|
|
148
|
+
if (Array.isArray(a) != Array.isArray(b)) return false
|
|
149
|
+
const ka = Object.keys(a).filter(k => k != 'elm' && a[k] !== undefined), kb = Object.keys(b).filter(k => k != 'elm' && b[k] !== undefined)
|
|
150
|
+
if (ka.length != kb.length) return false
|
|
151
|
+
for (const k of ka) if (!same(a[k], b[k], d + 1)) return false
|
|
152
|
+
return true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const SAMPLE = 16, CAP = 20
|
|
156
|
+
|
|
157
|
+
/** a CSS identifier's start, then identifier characters (`<prefix>-<id>` is one when the prefix starts one) */
|
|
158
|
+
const IDENT = /^(--|-?[A-Za-z_\u0080-\uffff])[\w\u0080-\uffff-]*$/
|
|
159
|
+
|
|
160
|
+
/** G-419 SYG148: a Collection's viewTransitionName that is not a CSS identifier (true: a valid one) */
|
|
161
|
+
function vtPrefix(name: string, p: any): boolean {
|
|
162
|
+
if (typeof p == 'string' && IDENT.test(p)) return true
|
|
163
|
+
const shown = typeof p == 'string' ? JSON.stringify(p) : `{${typeof p}}`
|
|
164
|
+
if (once(`SYG148:${name}:${String(p)}`)) devReport('SYG148', {
|
|
165
|
+
component: name,
|
|
166
|
+
message: `A Collection in ${name} has viewTransitionName=${shown}, which is not a CSS identifier, so every item's view-transition-name (${typeof p == 'string' ? p : '<prefix>'}-<id>) is invalid CSS: the browser ignores it and nothing animates`,
|
|
167
|
+
fix: 'Use a CSS identifier as the prefix: letters, digits, - and _, not starting with a digit (viewTransitionName="card")',
|
|
168
|
+
data: {value: p},
|
|
169
|
+
})
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* G-460: an element the browser renders (a View Transition captures only those): not in a
|
|
175
|
+
* display: none subtree (a hidden tab panel, a list shown only at another width). checkVisibility()
|
|
176
|
+
* where there is one; else (jsdom) no display: none on it or an ancestor
|
|
177
|
+
*/
|
|
178
|
+
function rendered(el: any): boolean {
|
|
179
|
+
if (!el?.isConnected) return false
|
|
180
|
+
if (typeof el.checkVisibility == 'function') return el.checkVisibility()
|
|
181
|
+
const cs = el.ownerDocument?.defaultView?.getComputedStyle
|
|
182
|
+
for (let n = el; n && n.nodeType == 1; n = n.parentNode) if (n.hidden || cs?.(n).display == 'none') return false
|
|
183
|
+
return true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* G-419 SYG149: two elements of the patched page with the same view-transition-name from
|
|
188
|
+
* Collections with the same prefix (the same id shown in two of them): the browser skips the
|
|
189
|
+
* whole View Transition. Walks the vnode tree (dev only, while a named Collection is mounted);
|
|
190
|
+
* G-460: a name found twice is checked on the DOM after the patch (a microtask later; when a View
|
|
191
|
+
* Transition defers the patch, again a little later): only rendered elements count. Returns the
|
|
192
|
+
* groups of vnodes with one name (none: an empty list)
|
|
193
|
+
*/
|
|
194
|
+
function vtDuplicates(root: any, prefixes: Set<string>): any[][] {
|
|
195
|
+
const seen = new Map<string, any[]>(), dup: any[][] = []
|
|
196
|
+
const stack = [root]
|
|
197
|
+
while (stack.length) {
|
|
198
|
+
const v = stack.pop()
|
|
199
|
+
if (!v || typeof v != 'object') continue
|
|
200
|
+
const st = v.data?.style, n = st?.viewTransitionName
|
|
201
|
+
if (typeof n == 'string' && prefixes.has(st.viewTransitionClass)) {
|
|
202
|
+
const g = seen.get(n)
|
|
203
|
+
if (!g) seen.set(n, [v])
|
|
204
|
+
else if (g.push(v) == 2) dup.push(g)
|
|
205
|
+
}
|
|
206
|
+
if (Array.isArray(v.children)) for (let i = v.children.length; i--;) stack.push(v.children[i])
|
|
207
|
+
}
|
|
208
|
+
return dup
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// G-538: a fragment root never gets an element (the DOM driver flattens it into the container):
|
|
212
|
+
// it has run when each of its children has one (a kept child has its old one: a View Transition
|
|
213
|
+
// patch of a fragment root whose children are all kept is checked before it runs)
|
|
214
|
+
const patched = (v: any): any => v.elm || !v.sel && v.children?.every((c: any) => !c || patched(c))
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* G-527: the patches waiting for their SYG149 check ([root vnode, duplicate groups], newest last,
|
|
218
|
+
* at most 8) and one chain of checks over them: a microtask after the first, then every 50 ms
|
|
219
|
+
* while some wait. The patch of a root has run when it has its element (a View Transition patches
|
|
220
|
+
* in its update callback, and replaces a held vnode it hasn't patched yet; kept vnodes have their
|
|
221
|
+
* old elements before that): each check takes the newest patch that ran and drops it and the older
|
|
222
|
+
* ones. A newer patch joins the queue and doesn't restart the chain, so patches closer than 50 ms
|
|
223
|
+
* (View Transitions back to back) can't starve it. A patch without duplicates joins only a queue
|
|
224
|
+
* that waits (once it has run, the older ones are moot). G-501: the chain stops once the app (or
|
|
225
|
+
* every named Collection) is gone, and after 10 tries without a patched root (the mock DOM; a root
|
|
226
|
+
* that never gets its element isn't checked on its children's old elements)
|
|
227
|
+
*/
|
|
228
|
+
function vtChecker(live: () => any) {
|
|
229
|
+
let q: any[][] = [], busy = false, tries = 0
|
|
230
|
+
const vtRendered = (): void => {
|
|
231
|
+
if (!live()) { q = []; busy = false; return }
|
|
232
|
+
let i = q.length
|
|
233
|
+
while (i-- && !patched(q[i][0]));
|
|
234
|
+
if (i >= 0) { vtReport(q[i][1]); q = q.slice(i + 1); tries = 0 }
|
|
235
|
+
else tries++
|
|
236
|
+
if (!q.length || tries > 10) { q = []; busy = false; return }
|
|
237
|
+
setTimeout(vtRendered, 50)
|
|
238
|
+
}
|
|
239
|
+
return (root: any, dup: any[][]) => {
|
|
240
|
+
if (!dup.length && !q.length) return
|
|
241
|
+
q.push([root, dup])
|
|
242
|
+
if (q.length > 8) q.shift()
|
|
243
|
+
if (!busy) { busy = true; tries = 0; queueMicrotask(vtRendered) }
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function vtReport(dup: any[][]): void {
|
|
248
|
+
for (const g of dup) {
|
|
249
|
+
const v = g[0], st = v.data.style, n = st.viewTransitionName
|
|
250
|
+
if (g.filter(x => rendered(x.elm)).length < 2 || !once(`SYG149:${n}`)) continue
|
|
251
|
+
devReport('SYG149', {
|
|
252
|
+
component: 'Collection',
|
|
253
|
+
message: `Two elements on the page have view-transition-name '${n}': Collections with viewTransitionName="${st.viewTransitionClass}" show the item with that id at the same time. Names must be unique when a View Transition starts, so the browser skips every transition while both are shown`,
|
|
254
|
+
fix: `Give Collections that can show the same item at once different prefixes (viewTransitionName="${st.viewTransitionClass}" and another), or name the item yourself with its own style`,
|
|
255
|
+
data: {name: n, prefix: st.viewTransitionClass},
|
|
256
|
+
})
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** The hooks one app gets (installChecks publishes this on the bridge). */
|
|
261
|
+
export function nextHooks(_api: any): any {
|
|
262
|
+
const core = bridge(), H = core.hooks
|
|
263
|
+
const on = () => H.on()
|
|
264
|
+
const modelOf = (f: Facade) => f.modelMap
|
|
265
|
+
const wired = new WeakSet<object>()
|
|
266
|
+
const wire = (iv: any, names: string[]) => {
|
|
267
|
+
const f = fac(iv)
|
|
268
|
+
if (wired.has(f)) return
|
|
269
|
+
wired.add(f)
|
|
270
|
+
H.onIntent(f, names)
|
|
271
|
+
H.onModel(f, modelOf(f))
|
|
272
|
+
}
|
|
273
|
+
// D168: the views context tracking skipped, checked on a sample (each instance's first two
|
|
274
|
+
// skips, then 1 in 16; at most CAP per tick)
|
|
275
|
+
const skips = new WeakMap<object, number>()
|
|
276
|
+
let budget = 0
|
|
277
|
+
const log = actionHooks(recentActions, () => on())
|
|
278
|
+
// G-419: the viewTransitionName prefixes this app's Collections use; G-460: per instance that
|
|
279
|
+
// renders them, so the SYG149 walk stops once every one of them is disposed
|
|
280
|
+
const vtp = new Map<any, Set<string>>()
|
|
281
|
+
// G-527: one chain of SYG149 checks over the latest patches (G-501: it stops once every instance
|
|
282
|
+
// that renders a named Collection is disposed, the app's dispose included)
|
|
283
|
+
const vtCheck = vtChecker(() => vtp.size && on())
|
|
284
|
+
return {
|
|
285
|
+
onCreate(iv: any) {
|
|
286
|
+
if (!on()) return
|
|
287
|
+
log.onCreate(iv)
|
|
288
|
+
const f = fac(iv)
|
|
289
|
+
removedStatics(f)
|
|
290
|
+
if (!iv.def.intent) wire(iv, [])
|
|
291
|
+
},
|
|
292
|
+
wrapSources(iv: any, so: any) {
|
|
293
|
+
const f = fac(iv)
|
|
294
|
+
f._so = so
|
|
295
|
+
let out
|
|
296
|
+
try { out = H.sourcesFor(f) } finally { f._so = undefined }
|
|
297
|
+
if (!on() || typeof Proxy != 'function') return out
|
|
298
|
+
// D173: CHILD.select('Name')
|
|
299
|
+
const s = out || so
|
|
300
|
+
return new Proxy(s, {
|
|
301
|
+
get(t: any, k: any) {
|
|
302
|
+
const v = t[k]
|
|
303
|
+
if (k !== 'CHILD' || !v || typeof v.select != 'function') return v
|
|
304
|
+
return {...v, select: (x: any, ...r: any[]) => { if (typeof x == 'string') removed(f, f.name, 'child-select-name', `CHILD.select('${x}')`); return v.select(x, ...r) }}
|
|
305
|
+
},
|
|
306
|
+
})
|
|
307
|
+
},
|
|
308
|
+
onIntent(iv: any, names: string[]) {
|
|
309
|
+
wire(iv, names)
|
|
310
|
+
},
|
|
311
|
+
onRender(iv: any, vnode: any) {
|
|
312
|
+
const f = fac(iv)
|
|
313
|
+
H.onRender(f, vnode)
|
|
314
|
+
if (on()) removedTags(f, vnode)
|
|
315
|
+
},
|
|
316
|
+
onReducer(iv: any, type: string, prev: any, next: any) {
|
|
317
|
+
H.onReducer(fac(iv), type, prev, next, 'STATE')
|
|
318
|
+
},
|
|
319
|
+
onDispose(iv: any) {
|
|
320
|
+
vtp.delete(iv)
|
|
321
|
+
H.onDispose(fac(iv))
|
|
322
|
+
},
|
|
323
|
+
onAction(iv: any, a: any) { log.onAction(iv, a) },
|
|
324
|
+
wrapHandler(iv: any, type: string, sink: string, fn: any) {
|
|
325
|
+
let h = log.wrapHandler(iv, type, sink, fn) || fn
|
|
326
|
+
// SYG222: the STATE reducers, wrapped (today the check rewrote the instance's model)
|
|
327
|
+
if (on() && sink == 'STATE' && typeof fn == 'function') h = watchMutation(fac(iv), type, h)
|
|
328
|
+
return h === fn ? undefined : h
|
|
329
|
+
},
|
|
330
|
+
onSink(iv: any, type: string | null, sink: string, v: any) {
|
|
331
|
+
if (!on()) return
|
|
332
|
+
const f = fac(iv), src = iv.sources[sink]
|
|
333
|
+
try {
|
|
334
|
+
if (sink == 'EVENTS') eventEmitted(f, v)
|
|
335
|
+
if (src && src.__sygnalReplies === true) checkRequest(f, sink, v, Object.keys(f.modelMap))
|
|
336
|
+
if (src && src.__sygnalStatic === 'resources') checkFetch(f, sink, v)
|
|
337
|
+
} catch (_) { /* a check never breaks the app */ }
|
|
338
|
+
},
|
|
339
|
+
onElementCommand(iv: any, cmd: any) {
|
|
340
|
+
if (!on()) return
|
|
341
|
+
const f = fac(iv)
|
|
342
|
+
for (const c of ([] as any[]).concat(cmd)) checkSentCommand(f, c)
|
|
343
|
+
},
|
|
344
|
+
onHostProps(owner: any, sel: string, props: any, data?: any) {
|
|
345
|
+
if (!on() || (sel != 'collection' && sel != 'virtual-collection')) return
|
|
346
|
+
const f = fac(owner)
|
|
347
|
+
if (typeof props.of == 'string') removed(f, f.name, 'collection-of-name', `of="${props.of}"`)
|
|
348
|
+
// 4-H (D229): what went only to the removed wrapper div (VirtualCollection keeps its container)
|
|
349
|
+
if (sel == 'collection') {
|
|
350
|
+
const w = WRAPPER.filter(k => k == 'className' ? props.className != null : data?.[k] != null)
|
|
351
|
+
if (w.length) removed(f, f.name, 'collection-wrapper', w.map(k => k == 'dataset' ? 'data-*' : k == 'hook' ? 'ref / hook' : k).join(', '))
|
|
352
|
+
}
|
|
353
|
+
try { checkCollection(f, {data: {props}}) } catch (_) { /* ignore */ }
|
|
354
|
+
// G-419: SYG148 for a prefix that isn't an identifier; a valid one is checked for
|
|
355
|
+
// duplicate names after each patch (SYG149). G-460: a falsy one names nothing (as the host)
|
|
356
|
+
const p = props.viewTransitionName
|
|
357
|
+
if (p && vtPrefix(f.name, p)) {
|
|
358
|
+
let s = vtp.get(owner)
|
|
359
|
+
if (!s) vtp.set(owner, s = new Set())
|
|
360
|
+
s.add(p)
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
onPatch(vnode: any) {
|
|
364
|
+
if (!vtp.size || !on()) return
|
|
365
|
+
const all = new Set<string>()
|
|
366
|
+
for (const s of vtp.values()) for (const p of s) all.add(p)
|
|
367
|
+
try { vtCheck(vnode, vtDuplicates(vnode, all)) } catch (_) { /* a check never breaks the app */ }
|
|
368
|
+
},
|
|
369
|
+
onDuplicateKey(owner: any, key: any) {
|
|
370
|
+
if (!on()) return
|
|
371
|
+
const name = owner.name
|
|
372
|
+
if (!once(`SYG424:${name}:${String(key)}`)) return
|
|
373
|
+
devReport('SYG424', {
|
|
374
|
+
component: name,
|
|
375
|
+
message: `A Collection in ${name} has more than one item with the id ${JSON.stringify(key) ?? String(key)} (duplicate ids); only the first of them renders, and their writes go to it`,
|
|
376
|
+
fix: 'Give every item a unique id (or no id: an item without one is keyed by its index)',
|
|
377
|
+
data: {id: key},
|
|
378
|
+
})
|
|
379
|
+
},
|
|
380
|
+
onStateSeed(iv: any, slice: any, init: any) {
|
|
381
|
+
if (!on() || !isPlainObject(init) || !isPlainObject(slice)) return
|
|
382
|
+
// G-333: a key initialState sets to undefined is no data the slice lacks
|
|
383
|
+
const missing = Object.keys(init).filter(k => init[k] !== undefined && !(k in slice))
|
|
384
|
+
const name = iv.name
|
|
385
|
+
if (!missing.length || !once(`SYG425:${name}:${missing.join(',')}`)) return
|
|
386
|
+
const list = missing.map(k => `'${k}'`).join(', ')
|
|
387
|
+
devReport('SYG425', {
|
|
388
|
+
component: name,
|
|
389
|
+
message: `${name} has isolatedState and is bound to a slice of its parent's state that already exists, so it keeps that slice instead of its initialState; the slice has no ${list}, which ${name}.initialState defines`,
|
|
390
|
+
fix: `To start ${name} from its initialState instead, add resetState to the tag (<${name} state="..." resetState />). To keep the parent's data, initialize ${list} in the parent's state`,
|
|
391
|
+
data: {missing, slice: Object.keys(slice)},
|
|
392
|
+
})
|
|
393
|
+
},
|
|
394
|
+
onContextSkip(iv: any, render: (c: any) => any, prev: any, next: any, keys: string[]) {
|
|
395
|
+
if (!on() || !prev || !next) return
|
|
396
|
+
const n = (skips.get(iv) || 0) + 1
|
|
397
|
+
skips.set(iv, n)
|
|
398
|
+
if ((n > 2 && n % SAMPLE) || budget >= CAP) return
|
|
399
|
+
if (!budget++) queueMicrotask(() => { budget = 0 })
|
|
400
|
+
let a, b, c
|
|
401
|
+
try { a = render(prev); b = render(next); c = render(prev) } catch (_) { return }
|
|
402
|
+
// G-327: a control render: a view whose output differs between two calls with the same
|
|
403
|
+
// context (a render counter, Date.now(), Math.random(), generated ids) proves nothing
|
|
404
|
+
if (same(a, b) || !same(a, c)) return
|
|
405
|
+
const name = iv.name
|
|
406
|
+
const changed = Object.keys(next).filter(k => next[k] !== prev[k])
|
|
407
|
+
if (!once(`SYG423:${name}`)) return
|
|
408
|
+
reportSafely('SYG423', {
|
|
409
|
+
severity: DEV_CODE_SEVERITY.SYG423,
|
|
410
|
+
component: name,
|
|
411
|
+
message: `The context keys ${changed.map(k => `'${k}'`).join(', ')} changed and ${name}'s view was not re-rendered, because the view read none of them during its last call (it read: ${keys.length ? keys.map(k => `'${k}'`).join(', ') : 'nothing'}). Calling it with the new context renders something else, so the page shows a stale render`,
|
|
412
|
+
fix: `Read context entries in the view itself (context.theme), not through a value kept from an earlier render or a function that runs later; Object.getOwnPropertyDescriptor and similar reflection aren't tracked`,
|
|
413
|
+
data: {keysRead: keys, changed},
|
|
414
|
+
})
|
|
415
|
+
},
|
|
416
|
+
}
|
|
417
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLAN-4 GS-5: persist() checks (dev entry only; sygnal-check reports both statically).
|
|
3
|
+
*
|
|
4
|
+
* SYG224 (error) — a `persist` static on a component that isn't the root: the core only sets it
|
|
5
|
+
* up on the root (the one run() or renderComponent() is given), so nothing is saved
|
|
6
|
+
* or restored. Under Vike's Layout/Wrapper shell (the `__vike` source) the message says
|
|
7
|
+
* persist isn't supported on Vike pages, layouts or wrappers yet.
|
|
8
|
+
* SYG223 (warn) — a `pick` / `omit` key of persist() that isn't a key of the component's
|
|
9
|
+
* `initialState` static (a typo: that key is never saved, or never left out).
|
|
10
|
+
* Once per component name. Mechanism: onModel reads the static `view.persist` (the persist()
|
|
11
|
+
* value carries its options) and `view.initialState`.
|
|
12
|
+
*/
|
|
13
|
+
import type {DiagnosticCheck} from '../index'
|
|
14
|
+
import {devReport, nameOf, once, didYouMean} from './shared'
|
|
15
|
+
|
|
16
|
+
export const persistCheck: DiagnosticCheck = {
|
|
17
|
+
id: 'persist',
|
|
18
|
+
|
|
19
|
+
onModel(component) {
|
|
20
|
+
const view = component?.view, p = view?.persist
|
|
21
|
+
if (!p) return
|
|
22
|
+
const name = nameOf(component)
|
|
23
|
+
if (component.isSubComponent) {
|
|
24
|
+
// 3-B2: under Vike's Layout/Wrapper shell (onRenderClient's `__vike` source) the Page,
|
|
25
|
+
// Layouts and Wrappers are all sub-components of the shell root
|
|
26
|
+
const vike = component.sources?.__vike
|
|
27
|
+
if (once('SYG224:' + name)) devReport('SYG224', {
|
|
28
|
+
component,
|
|
29
|
+
message: vike
|
|
30
|
+
? `${name}.persist is set, but ${name} renders in Vike's Layout/Wrapper shell (the shell is the app's root), so its state is never saved or restored: persist() isn't supported on Vike pages, layouts or wrappers yet`
|
|
31
|
+
: `${name}.persist is set, but ${name} is not the root component, so its state is never saved or restored (persist() works on the root only)`,
|
|
32
|
+
fix: vike
|
|
33
|
+
? `Remove persist from ${name}. Save the state yourself (STATE.watch writing to localStorage, read back in BOOTSTRAP), or use persist() on a component you run() yourself, or on a Vike page with no Layout or Wrapper (it is its own root)`
|
|
34
|
+
: `Move persist to the root component (the one passed to run()) and pick the keys it holds: persist({ key, pick: ['${name.toLowerCase()}'] })`,
|
|
35
|
+
data: {key: p.options?.key, ...(vike && {vike: true})},
|
|
36
|
+
})
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
const o = p.options, init = view.initialState
|
|
40
|
+
if (!o || !init || typeof init != 'object') return
|
|
41
|
+
const keys = Object.keys(init)
|
|
42
|
+
for (const opt of ['pick', 'omit']) {
|
|
43
|
+
for (const k of Array.isArray(o[opt]) ? o[opt] : []) {
|
|
44
|
+
if (keys.includes(k) || !once(`SYG223:${name}:${opt}:${k}`)) continue
|
|
45
|
+
devReport('SYG223', {
|
|
46
|
+
component,
|
|
47
|
+
message: `persist ${opt} names '${k}', which is not a key of ${name}.initialState, so it is ${opt == 'pick' ? 'never saved' : 'never left out'}${didYouMean(k, keys)}`,
|
|
48
|
+
fix: `Use top-level keys of initialState in ${opt} (${keys.join(', ')}), or add '${k}' to initialState`,
|
|
49
|
+
data: {option: opt, key: k, stateKeys: keys},
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SYG106 — a prop passed by the parent is silently overwritten (warn; error in
|
|
3
|
+
* strict mode, G-007 part 2).
|
|
4
|
+
*
|
|
5
|
+
* The view is called with { ...props, state, children, slots, context, uid },
|
|
6
|
+
* so a parent prop with one of those names never reaches the child. (Tracker
|
|
7
|
+
* G-007 item 2.) `state` is special: Sygnal reads it as the child's state
|
|
8
|
+
* lens (a state field name or a { get, set } object), so it is only reported
|
|
9
|
+
* when the value can't be a lens.
|
|
10
|
+
*
|
|
11
|
+
* Mechanism: onRender reads the component's current (sanitized) props.
|
|
12
|
+
* Reported once per component name and prop.
|
|
13
|
+
*/
|
|
14
|
+
import type {DiagnosticCheck} from '../index'
|
|
15
|
+
import {report, once, nameOf} from './shared'
|
|
16
|
+
import {isStrictEnabled} from './strict'
|
|
17
|
+
|
|
18
|
+
// PLAN-4 GS-9: uid (the view's uid(name?) function)
|
|
19
|
+
// (R5: 6.0 passes no `peers`, D164: a prop of that name reaches the view)
|
|
20
|
+
const RESERVED = ['children', 'slots', 'context', 'uid']
|
|
21
|
+
|
|
22
|
+
export const propsCheck: DiagnosticCheck = {
|
|
23
|
+
id: 'props',
|
|
24
|
+
|
|
25
|
+
onRender(component) {
|
|
26
|
+
const props = component && component.currentProps
|
|
27
|
+
if (!props || typeof props !== 'object') return
|
|
28
|
+
const name = nameOf(component)
|
|
29
|
+
const severity = isStrictEnabled() ? 'error' : 'warn'
|
|
30
|
+
|
|
31
|
+
for (const key of RESERVED) {
|
|
32
|
+
if (!(key in props) || !once(`SYG106:${name}:${key}`)) continue
|
|
33
|
+
report('SYG106', {
|
|
34
|
+
component,
|
|
35
|
+
severity,
|
|
36
|
+
message: `The prop '${key}' passed to ${name} is overwritten: '${key}' is a reserved view argument, so the child never sees the parent's value`,
|
|
37
|
+
fix: `Rename the prop (for example '${key}Value'), in the parent and in ${name}`,
|
|
38
|
+
data: {prop: key},
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if ('state' in props) {
|
|
43
|
+
const value = props.state
|
|
44
|
+
const isLens = typeof value === 'string' || (value && typeof value === 'object' && typeof value.get === 'function')
|
|
45
|
+
if (!isLens && value !== undefined && once(`SYG106:${name}:state`)) {
|
|
46
|
+
report('SYG106', {
|
|
47
|
+
component,
|
|
48
|
+
severity,
|
|
49
|
+
message: `The prop 'state' passed to ${name} is not data: Sygnal reads it as the child's state lens (a state field name or a { get, set } object), and the view's 'state' argument is the child's own state`,
|
|
50
|
+
fix: `To give ${name} a slice of the parent state use state="fieldName"; to pass data, use another prop name (for example 'item')`,
|
|
51
|
+
data: {prop: 'state', valueType: typeof value},
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
}
|