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,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the 'sygnal/diagnostics' entry (runtime consistency checks).
|
|
3
|
+
*
|
|
4
|
+
* import 'sygnal/diagnostics' // registers all checks (side effect)
|
|
5
|
+
*
|
|
6
|
+
* Checks only run while diagnostics are on: run(App, drivers, { diagnostics: 'warn' }),
|
|
7
|
+
* or globalThis.__SYGNAL_DEV__ (set by the Vite plugin in dev).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type DiagnosticSeverity = 'error' | 'warn' | 'info'
|
|
11
|
+
|
|
12
|
+
export interface DiagnosticCodeInfo {
|
|
13
|
+
code: string
|
|
14
|
+
severity: DiagnosticSeverity
|
|
15
|
+
title: string
|
|
16
|
+
/** anchor on https://sygnal.js.org/reference/errors, e.g. 'syg101' */
|
|
17
|
+
docsSlug: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** A runtime check, as registered with the diagnostics core. */
|
|
21
|
+
export interface DiagnosticCheck {
|
|
22
|
+
id: string
|
|
23
|
+
onIntent?: (component: any, actionNames: string[], selectorsUsed: string[] | undefined) => void
|
|
24
|
+
onModel?: (component: any, modelMap: Record<string, string[]>) => void
|
|
25
|
+
onRender?: (component: any, rootVnode: any) => void
|
|
26
|
+
onReducer?: (component: any, action: string, prevState: any, nextState: any, sinkName: string) => void
|
|
27
|
+
onDispose?: (component: any) => void
|
|
28
|
+
onSelector?: (domSource: any, selector: string) => void
|
|
29
|
+
onBusEmit?: (type: string, emitterName?: string) => void
|
|
30
|
+
onBusSelect?: (type: string | string[] | undefined) => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Strict mode (canonical forms, SYG5xx): turn the runtime strict checks on
|
|
35
|
+
* (true), off (false), or back to the default (undefined → on only when
|
|
36
|
+
* globalThis.__SYGNAL_STRICT__ === true). Off by default. Diagnostics must
|
|
37
|
+
* also be on. Runtime rules: SYG501 (positional view args), SYG504 ('ACTION | SINK'
|
|
38
|
+
* keys); `sygnal-check --strict` checks SYG501-508 statically (SYG502 is retired in 6.0).
|
|
39
|
+
* In tests: renderComponent(C, { strict: true }).
|
|
40
|
+
*/
|
|
41
|
+
export function configureStrict(on?: boolean): void
|
|
42
|
+
|
|
43
|
+
/** Whether the runtime strict checks are on. */
|
|
44
|
+
export function isStrictEnabled(): boolean
|
|
45
|
+
|
|
46
|
+
/** Every runtime check in this entry. */
|
|
47
|
+
export const checks: DiagnosticCheck[]
|
|
48
|
+
|
|
49
|
+
/** Re-register all checks and the RxJS hints (done automatically on import). Returns an uninstall function. */
|
|
50
|
+
export function installChecks(): () => void
|
|
51
|
+
|
|
52
|
+
export interface ChecksOptions {
|
|
53
|
+
/** delay after a render before the DOM checks (SYG103/104) run; default 50 ms */
|
|
54
|
+
settleMs?: number
|
|
55
|
+
/** render-idle time before SYG103 escalates to warn; default 2000 ms */
|
|
56
|
+
idleMs?: number
|
|
57
|
+
/** renders required before SYG103 escalates to warn; default 3 */
|
|
58
|
+
minRenders?: number
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Tune the timing of the DOM checks (mostly for tests). */
|
|
62
|
+
export function configureChecks(options?: ChecksOptions): void
|
|
63
|
+
|
|
64
|
+
/** Forget what the checks have seen and reported (dedupe state, registries). For tests. */
|
|
65
|
+
export function resetChecks(): void
|
|
66
|
+
|
|
67
|
+
export interface EventBusSummary {
|
|
68
|
+
selected: string[]
|
|
69
|
+
emitted: string[]
|
|
70
|
+
selectedNeverEmitted: string[]
|
|
71
|
+
emittedNeverSelected: string[]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Report SYG105 (info) for every EVENTS type selected but never emitted so far,
|
|
76
|
+
* and return the EVENTS bus registry.
|
|
77
|
+
*/
|
|
78
|
+
export function checkEventBus(): EventBusSummary
|
|
79
|
+
|
|
80
|
+
/** RxJS operator name -> xstream equivalent (used by SYG301). */
|
|
81
|
+
export const RXJS_HINTS: Record<string, string>
|
|
82
|
+
|
|
83
|
+
/** Metadata for every registered diagnostic code. */
|
|
84
|
+
export function listCodes(): DiagnosticCodeInfo[]
|
|
85
|
+
|
|
86
|
+
/** Metadata for one diagnostic code, or undefined if unknown. */
|
|
87
|
+
export function getCodeInfo(code: string): DiagnosticCodeInfo | undefined
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// inspect (PLAN-1 workstream 2B): a machine-readable app graph. The same shape
|
|
91
|
+
// is produced at runtime (inspect(), getDevTools().inspect(), renderComponent's
|
|
92
|
+
// t.inspect()) and statically (`sygnal-check --graph --json`); JSON Schema:
|
|
93
|
+
// sygnal-check/schema/inspect.schema.json. Fields one side cannot know are
|
|
94
|
+
// null (or omitted).
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
/** How an action is dispatched. */
|
|
98
|
+
export type InspectActionTrigger = 'intent' | 'next' | 'reply' | 'builtin' | 'unknown'
|
|
99
|
+
|
|
100
|
+
export interface InspectAction {
|
|
101
|
+
name: string
|
|
102
|
+
/**
|
|
103
|
+
* 'intent': returned by the component's intent. 'builtin': BOOTSTRAP,
|
|
104
|
+
* INITIALIZE, DISPOSE or READY. 'reply': named as a reply action by a request
|
|
105
|
+
* (`ok: 'NAME'` / `error: 'NAME'`) or a `connections` entry (statically: a
|
|
106
|
+
* string literal; at runtime: a request the instance was seen sending).
|
|
107
|
+
* 'next': dispatched with next() (statically: a next('NAME') literal; at
|
|
108
|
+
* runtime: a model-only action whose STATE reducer was seen running).
|
|
109
|
+
* 'unknown': none of these is known.
|
|
110
|
+
*/
|
|
111
|
+
trigger: InspectActionTrigger
|
|
112
|
+
/** sinks of the model entry (STATE, EVENTS, EFFECT, PARENT, custom drivers); [] without a model entry */
|
|
113
|
+
sinks: string[]
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface InspectChild {
|
|
117
|
+
name: string
|
|
118
|
+
via: 'tag' | 'collection' | 'switchable' | 'slot'
|
|
119
|
+
/** Collection: the `from` state field, when known */
|
|
120
|
+
from?: string | null
|
|
121
|
+
/** runtime: number of live instances behind this entry (e.g. Collection items) */
|
|
122
|
+
count?: number
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface InspectSelector {
|
|
126
|
+
/** CSS selector passed to DOM.select() (chained selects joined with a space) */
|
|
127
|
+
selector: string
|
|
128
|
+
/** event types listened to; null when unknown (runtime, real DOM) */
|
|
129
|
+
events: string[] | null
|
|
130
|
+
/**
|
|
131
|
+
* whether it matches an element the component itself renders; null when unknown.
|
|
132
|
+
* static: the class/id is in the view source. renderComponent: an element of the latest
|
|
133
|
+
* render matches (a conditionally rendered element that is hidden right now gives false).
|
|
134
|
+
* real DOM: true once a DOM check saw it match, false after SYG103/SYG104, else null.
|
|
135
|
+
*/
|
|
136
|
+
matched: boolean | null
|
|
137
|
+
/** the child component whose (isolated) elements it matches instead, if any */
|
|
138
|
+
isolationHit: string | null
|
|
139
|
+
/** PLAN-4 CT-1: the control this selector is (its key), when it is `[data-control="<Key>"]` */
|
|
140
|
+
control?: string
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** PLAN-4 CT-1: a control (controls()) a component renders */
|
|
144
|
+
export interface InspectControl {
|
|
145
|
+
/** the control's key (rendered as data-control="<name>") */
|
|
146
|
+
name: string
|
|
147
|
+
/** the intrinsic tag of a tag spec; null for a spec object */
|
|
148
|
+
element: string | null
|
|
149
|
+
/** a spec object's kind (e.g. 'widget') */
|
|
150
|
+
kind?: string
|
|
151
|
+
/** whether the component's intent listens to it; null when unknown */
|
|
152
|
+
listened: boolean | null
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export interface InspectDiagnostic {
|
|
156
|
+
code: string
|
|
157
|
+
severity: DiagnosticSeverity
|
|
158
|
+
component?: string
|
|
159
|
+
message: string
|
|
160
|
+
fix?: string
|
|
161
|
+
docsUrl?: string
|
|
162
|
+
data?: any
|
|
163
|
+
/** static only */
|
|
164
|
+
file?: string
|
|
165
|
+
line?: number
|
|
166
|
+
column?: number
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface InspectComponent {
|
|
170
|
+
name: string
|
|
171
|
+
/** runtime: instance number (as a string); static: 'file:line' of the definition */
|
|
172
|
+
id: string
|
|
173
|
+
/** runtime: the parent instance's id; static: null (see the parents' `children`) */
|
|
174
|
+
parentId: string | null
|
|
175
|
+
/** static only: source file, relative to the working directory */
|
|
176
|
+
file?: string
|
|
177
|
+
/**
|
|
178
|
+
* runtime: how the instance was created; static: 'root' when no scanned
|
|
179
|
+
* component renders it, else how it is first rendered
|
|
180
|
+
*/
|
|
181
|
+
kind: 'root' | 'child' | 'collection-item' | 'switchable'
|
|
182
|
+
actions: InspectAction[]
|
|
183
|
+
/** state keys (runtime: current state; static: initialState) */
|
|
184
|
+
stateKeys: string[]
|
|
185
|
+
/** calculated field names */
|
|
186
|
+
calculated: string[]
|
|
187
|
+
/** context fields this component provides (Component.context) */
|
|
188
|
+
contextProvides: string[]
|
|
189
|
+
/** context fields its view reads (static only; null at runtime) */
|
|
190
|
+
contextConsumes?: string[] | null
|
|
191
|
+
/** EVENTS types this component emits */
|
|
192
|
+
eventsEmitted: string[]
|
|
193
|
+
/** EVENTS types this component selects ('*' = EVENTS.select() without a type) */
|
|
194
|
+
eventsSelected: string[]
|
|
195
|
+
children: InspectChild[]
|
|
196
|
+
selectors: InspectSelector[]
|
|
197
|
+
/** PLAN-4 CT-1: the controls it renders (runtime: seen in its renders); omitted when none */
|
|
198
|
+
controls?: InspectControl[]
|
|
199
|
+
/** static only, PLAN-4 GS-2 (G-224): the element commands (ELEMENT sink) its model sends; omitted when none */
|
|
200
|
+
commands?: InspectCommand[]
|
|
201
|
+
/** static only, PLAN-4 GS-7 (G-224): the literal timer specs of its `timers` static; omitted when none */
|
|
202
|
+
timers?: InspectTimer[]
|
|
203
|
+
diagnostics: InspectDiagnostic[]
|
|
204
|
+
/** runtime, PLAN-3 5-3: the instance's `resources` and their state */
|
|
205
|
+
resources?: InspectResource[]
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** PLAN-4 GS-2: an element command a model entry sends (static inspect, sygnal-check --graph) */
|
|
209
|
+
export interface InspectCommand {
|
|
210
|
+
/** the model entry that sends it */
|
|
211
|
+
action: string
|
|
212
|
+
/** the command's method (its first key) */
|
|
213
|
+
method: string
|
|
214
|
+
/** the control's key or the selector; null when not static */
|
|
215
|
+
target: string | null
|
|
216
|
+
/** the control it targets (its key) */
|
|
217
|
+
control?: string
|
|
218
|
+
/** intent actions listening on the target for a native event the command causes (close, toggle...) */
|
|
219
|
+
triggers?: string[]
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** PLAN-4 GS-7: a literal timer spec of a `timers` static (static inspect, sygnal-check --graph) */
|
|
223
|
+
export interface InspectTimer {
|
|
224
|
+
name: string
|
|
225
|
+
every?: number | null
|
|
226
|
+
after?: number | null
|
|
227
|
+
/** the action a frame timer dispatches */
|
|
228
|
+
frame?: string | null
|
|
229
|
+
action?: string | null
|
|
230
|
+
background?: true
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** PLAN-3 5-3: a resource of a component instance (runtime inspect()) */
|
|
234
|
+
export interface InspectResource {
|
|
235
|
+
name: string
|
|
236
|
+
status: 'idle' | 'loading' | 'success' | 'error'
|
|
237
|
+
refreshing: boolean
|
|
238
|
+
/** `data` is set (a success, or kept through a refetch) */
|
|
239
|
+
hasData: boolean
|
|
240
|
+
/** the error message while 'error' */
|
|
241
|
+
error?: string
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** PLAN-3 5-3: a makeFetchDriver cache entry (runtime inspect(), t.cache) */
|
|
245
|
+
export interface InspectCacheEntry {
|
|
246
|
+
key: string
|
|
247
|
+
age?: number
|
|
248
|
+
stale: boolean
|
|
249
|
+
subscribers: number
|
|
250
|
+
data: any
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export interface InspectGraph {
|
|
254
|
+
version: 1
|
|
255
|
+
/** which implementation produced the graph */
|
|
256
|
+
source?: 'runtime' | 'static'
|
|
257
|
+
components: InspectComponent[]
|
|
258
|
+
/** EVENTS bus: type -> names of the components that emit / select it */
|
|
259
|
+
events: Record<string, { emitters: string[]; selectors: string[] }>
|
|
260
|
+
/** diagnostics not tied to a listed component */
|
|
261
|
+
diagnostics: InspectDiagnostic[]
|
|
262
|
+
/** runtime, PLAN-3 5-3: makeFetchDriver cache entries, by sink name (empty when no cache) */
|
|
263
|
+
cache?: Record<string, InspectCacheEntry[]>
|
|
264
|
+
/** runtime, PLAN-4 2-C (GS-10): with `inspect({ actions })`, the most recent actions, oldest first */
|
|
265
|
+
recentActions?: InspectRecentAction[]
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** PLAN-4 2-C (GS-10): one recent action (inspect({ actions })) */
|
|
269
|
+
export interface InspectRecentAction {
|
|
270
|
+
type: string
|
|
271
|
+
/** the action's data, when it is JSON-safe and small (a DOM event is left out) */
|
|
272
|
+
data?: any
|
|
273
|
+
/** the component's name */
|
|
274
|
+
component: string
|
|
275
|
+
/** the instance's id (InspectComponent.id) */
|
|
276
|
+
instance: string
|
|
277
|
+
/** the sinks that produced a value for it (not ABORT) */
|
|
278
|
+
sinks: string[]
|
|
279
|
+
cause: 'intent' | 'next' | 'reply' | 'built-in' | 'simulateAction' | 'behavior'
|
|
280
|
+
/** ms since the dev entry started recording (or the last resetChecks()) */
|
|
281
|
+
at: number
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export interface InspectOptions {
|
|
285
|
+
/** only these component instances (ids as in InspectComponent.id) */
|
|
286
|
+
ids?: Array<string | number>
|
|
287
|
+
/** selector details by component id (renderComponent passes its mock-DOM view) */
|
|
288
|
+
selectors?: Record<string, InspectSelector[]>
|
|
289
|
+
/** diagnostics to attach (default: the devtools' collected diagnostics, when available) */
|
|
290
|
+
diagnostics?: Array<{ code: string; severity: DiagnosticSeverity; component?: string; message: string; [key: string]: any }>
|
|
291
|
+
/** PLAN-4 2-C: add `recentActions`: true for every kept one (the last 200), a number for the last n */
|
|
292
|
+
actions?: boolean | number
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* The app graph of the live component instances, built from what the
|
|
297
|
+
* diagnostics hooks have seen (so diagnostics must be on while the app runs).
|
|
298
|
+
* Disposed components are pruned. Also available as `getDevTools().inspect()`
|
|
299
|
+
* / `window.__SYGNAL_DEVTOOLS__.inspect()` once the devtools bridge exists
|
|
300
|
+
* (run() in a browser), and as renderComponent's `t.inspect()`.
|
|
301
|
+
*/
|
|
302
|
+
export function inspect(options?: InspectOptions): InspectGraph
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reply actions (PLAN-3 §1.1, workstream 1-D).
|
|
3
|
+
*
|
|
4
|
+
* SYG112 — a request names a reply action the sending component has no
|
|
5
|
+
* model entry for (error: the reply is dropped). Dev-entry code
|
|
6
|
+
* (DEV_CODE_SEVERITY).
|
|
7
|
+
* SYG508 — strict: the component reads the replies to its own request with
|
|
8
|
+
* X.select('cat') / X.errors('cat') where reply actions would do.
|
|
9
|
+
*
|
|
10
|
+
* Mechanism:
|
|
11
|
+
* onSink (checks/next.ts: checkRequest) every value an instance sends to a
|
|
12
|
+
* reply-capable sink (`__sygnalReplies === true`: makeFetchDriver,
|
|
13
|
+
* driverFromAsync, the socket driver) is checked as it is sent.
|
|
14
|
+
* The names it sees feed inspect()'s 'reply' trigger.
|
|
15
|
+
* sources (strict only) wraps those sources for the intent, recording the
|
|
16
|
+
* select()/errors() categories the instance reads.
|
|
17
|
+
* A request's `category` is compared for SYG508 (driverFromAsync's custom
|
|
18
|
+
* `selector` property is not known at run time; sygnal-check covers it).
|
|
19
|
+
* Reported once per component name and action / category.
|
|
20
|
+
*
|
|
21
|
+
* `{ connections }` values (the `connections` static, which the core sends to
|
|
22
|
+
* the same sink, or one a model entry sends) are checked the same way:
|
|
23
|
+
* each connection's message / open / close / error and its SSE `events` names.
|
|
24
|
+
*/
|
|
25
|
+
import type {DiagnosticCheck} from '../index'
|
|
26
|
+
import {STRICT_CODE_SEVERITY} from '../codes'
|
|
27
|
+
import {devReport, reportSafely, once, nameOf, suggest, replySeen} from './shared'
|
|
28
|
+
import {isStrictEnabled} from './strict'
|
|
29
|
+
|
|
30
|
+
const REPLY_KEYS = ['ok', 'error']
|
|
31
|
+
// PLAN-3 5-4b: on the router's sink, `{ route }` (the route static) and `{ block }` name reply actions
|
|
32
|
+
const ROUTER_KEYS = ['route', 'block']
|
|
33
|
+
const CONNECTION_KEYS = ['message', 'open', 'close', 'error']
|
|
34
|
+
|
|
35
|
+
/** [action, key, connection name?] for every reply action name a sink value names */
|
|
36
|
+
function replyNames(req: any, router?: boolean): Array<[string, string, string?]> {
|
|
37
|
+
const out: Array<[string, string, string?]> = []
|
|
38
|
+
for (const key of router ? REPLY_KEYS.concat(ROUTER_KEYS) : REPLY_KEYS) if (typeof req[key] === 'string') out.push([req[key], key])
|
|
39
|
+
const conns = req.connections
|
|
40
|
+
if (conns && typeof conns === 'object') {
|
|
41
|
+
for (const name of Object.keys(conns)) {
|
|
42
|
+
const spec = conns[name]
|
|
43
|
+
if (!spec || typeof spec !== 'object') continue
|
|
44
|
+
for (const key of CONNECTION_KEYS) if (typeof spec[key] === 'string') out.push([spec[key], key, name])
|
|
45
|
+
const events = spec.events
|
|
46
|
+
if (events && typeof events === 'object') {
|
|
47
|
+
for (const ev of Object.keys(events)) if (typeof events[ev] === 'string') out.push([events[ev], `events.${ev}`, name])
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return out
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** instance → 'SINK\0category' → the select/errors call that read it */
|
|
55
|
+
const selected = new WeakMap<object, Map<string, string>>()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
function readingSource(component: any, sink: string, source: any) {
|
|
59
|
+
const wrapped = Object.create(source)
|
|
60
|
+
for (const method of ['select', 'errors']) {
|
|
61
|
+
if (typeof source[method] !== 'function') continue
|
|
62
|
+
wrapped[method] = (category?: any, ...rest: any[]) => {
|
|
63
|
+
if (typeof category === 'string') {
|
|
64
|
+
let reads = selected.get(component)
|
|
65
|
+
if (!reads) selected.set(component, reads = new Map())
|
|
66
|
+
reads.set(`${sink}\0${category}`, `${sink}.${method}('${category}')`)
|
|
67
|
+
}
|
|
68
|
+
return source[method](category, ...rest)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return wrapped
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function checkRequest(component: any, sink: string, req: any, modelActions: string[]) {
|
|
75
|
+
if (!req || typeof req !== 'object') return
|
|
76
|
+
const name = nameOf(component)
|
|
77
|
+
let replies = false
|
|
78
|
+
for (const [action, key, connection] of replyNames(req, !!component?.sources?.[sink]?.__sygnalRouter)) {
|
|
79
|
+
replies = true
|
|
80
|
+
let seen = replySeen.get(component)
|
|
81
|
+
if (!seen) replySeen.set(component, seen = new Set())
|
|
82
|
+
seen.add(action)
|
|
83
|
+
if (modelActions.includes(action) || !once(`SYG112:${name}:${action}`)) continue
|
|
84
|
+
const near = suggest(action, modelActions.filter(a => !a.startsWith('__')))
|
|
85
|
+
devReport('SYG112', {
|
|
86
|
+
component,
|
|
87
|
+
message: (connection
|
|
88
|
+
? `${sink} connection '${connection}' names '${action}' as its ${key} reply action, but ${name} has no model entry '${action}', so those events are dropped`
|
|
89
|
+
: `A ${sink} request names '${action}' as its ${key} reply action, but ${name} has no model entry '${action}', so the reply is dropped`) +
|
|
90
|
+
(near ? ` (did you mean '${near}'?)` : ''),
|
|
91
|
+
fix: near
|
|
92
|
+
? `Use the existing entry: ${key.startsWith('events.') ? `events: { '${key.slice(7)}': '${near}' }` : `${key}: '${near}'`}`
|
|
93
|
+
: `Add '${action}' to ${name}.model, e.g. ${action}: (state, data) => ({ ...state, ... }), or fix the name`,
|
|
94
|
+
data: {action, key, sink, ...(connection ? {connection} : {}), ...(near ? {suggestion: near} : {}), modelActions},
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// SYG508 (strict): a request without reply actions whose replies this instance reads back with select()/errors()
|
|
99
|
+
if (replies || 'abort' in req || typeof req.category !== 'string' || !isStrictEnabled()) return
|
|
100
|
+
const read = selected.get(component)?.get(`${sink}\0${req.category}`)
|
|
101
|
+
if (!read || !once(`SYG508:${name}:${sink}:${req.category}`)) return
|
|
102
|
+
// (inside a sink stream: reportSafely, so 'error' mode can't break the stream)
|
|
103
|
+
reportSafely('SYG508', {
|
|
104
|
+
severity: STRICT_CODE_SEVERITY.SYG508,
|
|
105
|
+
component,
|
|
106
|
+
message: `${name} sends a ${sink} request with category: '${req.category}' and reads the reply back with ${read}`,
|
|
107
|
+
fix: `Use reply actions instead: name them in the request, ${sink}: (state) => ({ url, ok: 'LOADED', error: 'FAILED' }), ` +
|
|
108
|
+
`handle LOADED (the parsed body) and FAILED ({ error, status, body, request }) in the model, and remove ${read} from the intent`,
|
|
109
|
+
data: {sink, category: req.category, read},
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const repliesCheck: DiagnosticCheck = {
|
|
114
|
+
id: 'replies',
|
|
115
|
+
|
|
116
|
+
sources(component, sources) {
|
|
117
|
+
if (!isStrictEnabled() || typeof Proxy != 'function') return
|
|
118
|
+
const cache = new Map<string, any>()
|
|
119
|
+
return new Proxy(sources, {
|
|
120
|
+
get(t: any, k: any) {
|
|
121
|
+
const v = t[k]
|
|
122
|
+
if (typeof k != 'string' || !v || v.__sygnalReplies !== true) return v
|
|
123
|
+
if (!cache.has(k)) cache.set(k, readingSource(component, k, v))
|
|
124
|
+
return cache.get(k)
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
},
|
|
128
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Router and declaration statics (PLAN-3 5-4b). Dev-entry-only codes (DEV_CODE_SEVERITY):
|
|
3
|
+
*
|
|
4
|
+
* SYG130 — href() names no route, or leaves out a param its pattern needs (the link is broken).
|
|
5
|
+
* (A `{ to }` command with the same problem is SYG620, reported by the router itself
|
|
6
|
+
* in production too, and not navigated.)
|
|
7
|
+
* SYG131 — href() or a `{ to, params }` command passes params the route's pattern doesn't use.
|
|
8
|
+
* SYG132 — G-167: a component declares a static the core sends to a driver (`route`,
|
|
9
|
+
* `resources`, `connections`, `head`) but never sends it: it has no model (the core
|
|
10
|
+
* wires statics in its model setup), or it is a root without initialState (a static is
|
|
11
|
+
* computed from the state stream, which a root without state never emits).
|
|
12
|
+
* SYG133 — the SPA router (makeRouter().driver without `navigate`) runs inside a Vike app,
|
|
13
|
+
* where Vike owns links and history.
|
|
14
|
+
*
|
|
15
|
+
* Mechanism: the router's href() calls `__SYGNAL_DIAGNOSTICS__.routerHref(routes, name, params)`
|
|
16
|
+
* when it exists (installRouterHooks() sets it); onModel looks at the component's sources for
|
|
17
|
+
* `__sygnalStatic` / `__sygnalRouter` markers.
|
|
18
|
+
*/
|
|
19
|
+
import type {DiagnosticCheck} from '../index'
|
|
20
|
+
import {bridge, devReport, once, nameOf, suggest} from './shared'
|
|
21
|
+
|
|
22
|
+
const PARAM = /:(\w+)/g
|
|
23
|
+
const paramsOf = (pat: string) => (pat.match(PARAM) || []).map(p => p.slice(1))
|
|
24
|
+
|
|
25
|
+
function checkHref(routes: Record<string, string>, name: string, params: any) {
|
|
26
|
+
const names = Object.keys(routes).filter(k => routes[k] != '*')
|
|
27
|
+
const pat = routes[name]
|
|
28
|
+
if (pat == null || pat == '*') {
|
|
29
|
+
if (!once(`SYG130:${name}`)) return
|
|
30
|
+
const near = suggest(String(name), names)
|
|
31
|
+
devReport('SYG130', {
|
|
32
|
+
message: `href('${name}') names no route, so the link points to '/'` + (near ? ` (did you mean '${near}'?)` : ''),
|
|
33
|
+
fix: `Use one of the route names: ${names.join(', ')}`,
|
|
34
|
+
data: {name, routes: names, ...(near ? {suggestion: near} : {})},
|
|
35
|
+
})
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
const wanted = paramsOf(pat)
|
|
39
|
+
const given = params && typeof params == 'object' ? params : {}
|
|
40
|
+
const missing = wanted.filter(k => given[k] == null)
|
|
41
|
+
const extra = Object.keys(given).filter(k => !wanted.includes(k))
|
|
42
|
+
if (missing.length && once(`SYG130:${name}:${missing}`)) {
|
|
43
|
+
devReport('SYG130', {
|
|
44
|
+
message: `href('${name}') leaves out ${missing.map(k => `'${k}'`).join(', ')} (${pat}), so that segment is empty`,
|
|
45
|
+
fix: `href('${name}', { ${wanted.join(', ')} })`,
|
|
46
|
+
data: {name, pattern: pat, missing},
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
if (extra.length && once(`SYG131:${name}:${extra}`)) {
|
|
50
|
+
devReport('SYG131', {
|
|
51
|
+
message: `Route '${name}' (${pat}) has no param ${extra.map(k => `'${k}'`).join(', ')}; it is ignored`,
|
|
52
|
+
fix: wanted.length ? `Pass only ${wanted.join(', ')}; put other values in the query: href('${name}', params, { ${extra[0]} })` : `Put it in the query: href('${name}', {}, { ${extra[0]} })`,
|
|
53
|
+
data: {name, pattern: pat, extra},
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** install the hook the router's href() calls (removed by the returned function) */
|
|
59
|
+
export function installRouterHooks(): () => void {
|
|
60
|
+
const core = bridge()
|
|
61
|
+
if (!core) return () => {}
|
|
62
|
+
core.routerHref = checkHref
|
|
63
|
+
return () => { if (core.routerHref === checkHref) core.routerHref = undefined }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export const routerCheck: DiagnosticCheck = {
|
|
67
|
+
id: 'router',
|
|
68
|
+
|
|
69
|
+
onModel(component) {
|
|
70
|
+
const sources = component?.sources
|
|
71
|
+
if (!sources || typeof sources != 'object') return
|
|
72
|
+
const name = nameOf(component)
|
|
73
|
+
for (const n of component.sourceNames || Object.keys(sources)) {
|
|
74
|
+
const src = sources[n]
|
|
75
|
+
const k = src?.__sygnalStatic
|
|
76
|
+
if (typeof k == 'string' && component.view?.[k] != null) {
|
|
77
|
+
// statics are computed from state (G-167: with or without a model): a root whose state
|
|
78
|
+
// never emits (a model but no initialState) never sends one
|
|
79
|
+
if (!component.isSubComponent && component.initialState === undefined && once(`SYG132:${name}:${k}`)) {
|
|
80
|
+
devReport('SYG132', {
|
|
81
|
+
component,
|
|
82
|
+
message: `${name} declares ${name}.${k}, but a root without initialState has no state, so ${k} is never sent to ${n}`,
|
|
83
|
+
fix: `Add ${name}.initialState (e.g. { }${k == 'route' ? ', or { route: router.current() }' : ''})`,
|
|
84
|
+
data: {static: k, source: n, reason: 'state'},
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const opts = src?.__sygnalRouter
|
|
89
|
+
const g: any = globalThis
|
|
90
|
+
if (opts && typeof opts == 'object' && !opts.navigate && g.document?.getElementById?.('vike_pageContext') && once('SYG133')) {
|
|
91
|
+
devReport('SYG133', {
|
|
92
|
+
component,
|
|
93
|
+
message: `The SPA router (${n}) runs inside a Vike app: Vike owns links and history, so both would handle a click`,
|
|
94
|
+
fix: "Pass Vike's navigate: makeRouter({ routes, navigate }) with import { navigate } from 'vike/client/router', or use Vike's routing",
|
|
95
|
+
data: {source: n},
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SYG301 — an RxJS operator was called on an xstream stream (error).
|
|
3
|
+
*
|
|
4
|
+
* Without help this fails with a bare "x.switchMap is not a function" inside
|
|
5
|
+
* the intent function. Mechanism: when 'sygnal/diagnostics' is loaded, every
|
|
6
|
+
* RxJS operator name below that xstream's Stream does NOT already have is
|
|
7
|
+
* defined on Stream.prototype (non-enumerable) as a method that throws an
|
|
8
|
+
* enriched TypeError: same "stream.<op> is not a function" shape, plus the
|
|
9
|
+
* xstream equivalent and the SYG301 docs link. The error is also reported
|
|
10
|
+
* (collected / printed per the diagnostics mode; in 'error' mode report()
|
|
11
|
+
* throws the DiagnosticError itself). It works anywhere a stream is used
|
|
12
|
+
* (intent, peers, drivers), with no hook in the core.
|
|
13
|
+
*
|
|
14
|
+
* Caveat: `typeof stream.pipe === 'function'` is true while this entry is
|
|
15
|
+
* loaded (dev only).
|
|
16
|
+
*/
|
|
17
|
+
import {report} from './shared'
|
|
18
|
+
import {docsUrlFor} from '../codes'
|
|
19
|
+
|
|
20
|
+
/** RxJS operator -> xstream equivalent. */
|
|
21
|
+
export const RXJS_HINTS: Record<string, string> = {
|
|
22
|
+
pipe: `chain xstream operators directly (.map(...).filter(...)), or use .compose(operator) for extra operators`,
|
|
23
|
+
switchMap: `.map(x => inner$).flatten()`,
|
|
24
|
+
mergeMap: `.map(x => inner$).compose(flattenConcurrently) (import { flattenConcurrently } from 'sygnal')`,
|
|
25
|
+
flatMap: `.map(x => inner$).compose(flattenConcurrently) (import { flattenConcurrently } from 'sygnal')`,
|
|
26
|
+
concatMap: `.map(x => inner$).compose(flattenSequentially) (import { flattenSequentially } from 'sygnal')`,
|
|
27
|
+
exhaustMap: `.map(x => inner$).flatten() (xstream has no exhaustMap; ignore new values while busy with .filter())`,
|
|
28
|
+
switchAll: `.flatten()`,
|
|
29
|
+
mergeAll: `.compose(flattenConcurrently) (import { flattenConcurrently } from 'sygnal')`,
|
|
30
|
+
concatAll: `.compose(flattenSequentially) (import { flattenSequentially } from 'sygnal')`,
|
|
31
|
+
debounceTime: `.compose(debounce(ms)) (import { debounce } from 'sygnal')`,
|
|
32
|
+
debounce: `.compose(debounce(ms)) (import { debounce } from 'sygnal')`,
|
|
33
|
+
throttleTime: `.compose(throttle(ms)) (import { throttle } from 'sygnal')`,
|
|
34
|
+
throttle: `.compose(throttle(ms)) (import { throttle } from 'sygnal')`,
|
|
35
|
+
auditTime: `.compose(throttle(ms)) (import { throttle } from 'sygnal')`,
|
|
36
|
+
delay: `.compose(delay(ms)) (import { delay } from 'sygnal')`,
|
|
37
|
+
distinctUntilChanged: `.compose(dropRepeats()) (import { dropRepeats } from 'sygnal')`,
|
|
38
|
+
withLatestFrom: `.compose(sampleCombine(other$)) (import { sampleCombine } from 'sygnal')`,
|
|
39
|
+
combineLatest: `xs.combine(a$, b$) (import { xs } from 'sygnal')`,
|
|
40
|
+
combineLatestWith: `xs.combine(a$, b$) (import { xs } from 'sygnal')`,
|
|
41
|
+
merge: `xs.merge(a$, b$) (import { xs } from 'sygnal')`,
|
|
42
|
+
mergeWith: `xs.merge(a$, b$) (import { xs } from 'sygnal')`,
|
|
43
|
+
concat: `concat(a$, b$) (import { concat } from 'sygnal')`,
|
|
44
|
+
scan: `.fold((acc, x) => next, seed)`,
|
|
45
|
+
reduce: `.fold((acc, x) => next, seed).last()`,
|
|
46
|
+
skip: `.drop(n)`,
|
|
47
|
+
first: `.take(1)`,
|
|
48
|
+
takeUntil: `.endWhen(other$)`,
|
|
49
|
+
tap: `.debug(fn)`,
|
|
50
|
+
do: `.debug(fn)`,
|
|
51
|
+
catchError: `.replaceError(err => fallback$)`,
|
|
52
|
+
catch: `.replaceError(err => fallback$)`,
|
|
53
|
+
pluck: `.map(x => x.field)`,
|
|
54
|
+
share: `xstream streams are already shared (hot); use .remember() to replay the latest value`,
|
|
55
|
+
shareReplay: `.remember()`,
|
|
56
|
+
publishReplay: `.remember()`,
|
|
57
|
+
finalize: `.debug({ complete: fn }) or handle completion in the subscriber`,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const SYG301_URL = docsUrlFor('SYG301')
|
|
61
|
+
|
|
62
|
+
function rxjsError(op: string): TypeError {
|
|
63
|
+
const hint = RXJS_HINTS[op]
|
|
64
|
+
const message = `stream.${op} is not a function: '${op}' is an RxJS operator, but Sygnal streams are xstream streams`
|
|
65
|
+
const fix = `Use ${hint}`
|
|
66
|
+
// In 'error' mode report() throws the DiagnosticError itself (we are about to throw anyway).
|
|
67
|
+
const d = report('SYG301', {message, fix, data: {operator: op, hint}})
|
|
68
|
+
const err: any = new TypeError(d ? d.text : `[Sygnal SYG301] ${message}. ${fix}. ${SYG301_URL}`)
|
|
69
|
+
err.diagnostic = d
|
|
70
|
+
return err
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Install the hints on xstream's Stream.prototype. Returns an uninstall function. */
|
|
74
|
+
export function installRxjsHints(proto: any): () => void {
|
|
75
|
+
const added: string[] = []
|
|
76
|
+
if (!proto) return () => {}
|
|
77
|
+
for (const op of Object.keys(RXJS_HINTS)) {
|
|
78
|
+
if (op in proto) continue
|
|
79
|
+
Object.defineProperty(proto, op, {
|
|
80
|
+
configurable: true,
|
|
81
|
+
writable: true,
|
|
82
|
+
enumerable: false,
|
|
83
|
+
value: function rxjsOperatorHint() { throw rxjsError(op) },
|
|
84
|
+
})
|
|
85
|
+
added.push(op)
|
|
86
|
+
}
|
|
87
|
+
return () => { for (const op of added) delete proto[op] }
|
|
88
|
+
}
|