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,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLAN-4 3-E (GS-10): "Copy as test". Turns a recorded session (./devtoolsActions getSession())
|
|
3
|
+
* into a Vitest + renderComponent test that replays it:
|
|
4
|
+
*
|
|
5
|
+
* renderComponent(C, { initialState?, drivers? }) the session's initial state (left out when
|
|
6
|
+
* it is the component's own initialState)
|
|
7
|
+
* await t.ready()
|
|
8
|
+
* t.simulateAction(type, data) each action caused by 'intent' | 'simulateAction' | 'behavior'
|
|
9
|
+
* await t.respond(sink, body, OK) a reply action from a makeFetchDriver source (renderComponent's
|
|
10
|
+
* await t.fail(sink, status, ...) HTTP fake answers the request the replayed actions send again)
|
|
11
|
+
* await t.settle()
|
|
12
|
+
* expect(t.state).toEqual(final)
|
|
13
|
+
*
|
|
14
|
+
* 'next' and 'built-in' actions are consequences (the replay produces them again), never replayed;
|
|
15
|
+
* nor is an intent action whose data is the component's own state (an intent over STATE.stream).
|
|
16
|
+
* A t.respond / t.fail that follows simulateAction calls waits for the request they send (G-218).
|
|
17
|
+
* Data is written as JS literals: JSON values, Date, Map, Set, NaN / Infinity / -0, bigint. A DOM
|
|
18
|
+
* event or element becomes a stub ({ type, key, target: { dataset, value, checked, id } }), with a
|
|
19
|
+
* comment. Anything else (functions, class instances, cycles) is left out with a comment.
|
|
20
|
+
*
|
|
21
|
+
* The final-state assertion is written only when the replay is complete: every state-changing
|
|
22
|
+
* action could be replayed with its data, no descendant instance changed the state on its own
|
|
23
|
+
* (a child's intent: copy the test from that instance instead), the session wasn't truncated,
|
|
24
|
+
* and the final state is JSON-safe. Otherwise a comment says why. A test written with the
|
|
25
|
+
* assertion passes unchanged (for drivers the session's app had, pass `drivers`).
|
|
26
|
+
*/
|
|
27
|
+
import type {SessionRecording, SessionAction} from './devtoolsActions'
|
|
28
|
+
import {isElement, isEvent, deepEqual} from './devtoolsActions'
|
|
29
|
+
|
|
30
|
+
export interface CopyAsTestOptions {
|
|
31
|
+
/** the import line(s) for the component, e.g. "import App from './App.jsx'" (default: `import <Name> from './<Name>.js'`) */
|
|
32
|
+
componentImport?: string
|
|
33
|
+
/** the identifier the component has in the test (default: the recorded component name) */
|
|
34
|
+
componentName?: string
|
|
35
|
+
/** more import lines (drivers, helpers) */
|
|
36
|
+
imports?: string[]
|
|
37
|
+
/** drivers for renderComponent, as source code by sink name: { DND: 'mockDragDriver().driver' } */
|
|
38
|
+
drivers?: Record<string, string>
|
|
39
|
+
/** more renderComponent options, as source code: "strict: true" */
|
|
40
|
+
renderOptions?: string
|
|
41
|
+
/** the test's name (default: '<Component> session (copied from sygnal/devtools)') */
|
|
42
|
+
testName?: string
|
|
43
|
+
/** adds a `// @vitest-environment <env>` first line (e.g. 'jsdom') */
|
|
44
|
+
environment?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface CopyAsTestResult {
|
|
48
|
+
/** the test file */
|
|
49
|
+
code: string
|
|
50
|
+
/** true when the test asserts the final state */
|
|
51
|
+
complete: boolean
|
|
52
|
+
/** what was left out, and why (also comments in the code) */
|
|
53
|
+
warnings: string[]
|
|
54
|
+
/** actions written as simulateAction / respond / fail */
|
|
55
|
+
replayed: number
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const REPLAY = new Set(['intent', 'simulateAction', 'behavior'])
|
|
59
|
+
const IDENT = /^[A-Za-z_$][\w$]*$/
|
|
60
|
+
|
|
61
|
+
class Unserializable extends Error {}
|
|
62
|
+
|
|
63
|
+
/** a single-quoted JS string literal */
|
|
64
|
+
const str = (s: string) => `'${JSON.stringify(s).slice(1, -1).replace(/\\"/g, '"').replace(/'/g, "\\'")}'`
|
|
65
|
+
|
|
66
|
+
interface Lit { code: string; stubbed: boolean }
|
|
67
|
+
|
|
68
|
+
/** `v` as a JS literal. mode 'data' turns DOM events / elements into stubs; 'state' refuses them. */
|
|
69
|
+
export function literal(v: any, mode: 'data' | 'state' = 'data', indent = ''): Lit {
|
|
70
|
+
const flag = {stubbed: false}
|
|
71
|
+
const code = lit(v, mode, indent, new Set(), flag)
|
|
72
|
+
return {code, stubbed: flag.stubbed}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function elementStub(el: any): any {
|
|
76
|
+
const out: any = {}
|
|
77
|
+
const ds = el.dataset && typeof el.dataset == 'object' ? {...el.dataset} : {}
|
|
78
|
+
if (Object.keys(ds).length) out.dataset = ds
|
|
79
|
+
if (el.id) out.id = el.id
|
|
80
|
+
const tag = String(el.tagName).toUpperCase()
|
|
81
|
+
if (/^(INPUT|SELECT|TEXTAREA)$/.test(tag) && typeof el.value == 'string') out.value = el.value
|
|
82
|
+
if (tag == 'INPUT' && /^(checkbox|radio)$/i.test(el.type || '')) out.checked = !!el.checked
|
|
83
|
+
return out
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function eventStub(e: any): any {
|
|
87
|
+
const out: any = {type: e.type}
|
|
88
|
+
if (typeof e.key == 'string') out.key = e.key
|
|
89
|
+
if (isElement(e.target)) {
|
|
90
|
+
const t = elementStub(e.target)
|
|
91
|
+
if (Object.keys(t).length) out.target = t
|
|
92
|
+
}
|
|
93
|
+
return out
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function lit(v: any, mode: 'data' | 'state', ind: string, seen: Set<any>, flag: {stubbed: boolean}): string {
|
|
97
|
+
if (v === null) return 'null'
|
|
98
|
+
switch (typeof v) {
|
|
99
|
+
case 'undefined': return 'undefined'
|
|
100
|
+
case 'boolean': return String(v)
|
|
101
|
+
case 'string': return str(v)
|
|
102
|
+
case 'number': return Object.is(v, -0) ? '-0' : String(v)
|
|
103
|
+
case 'bigint': return `${v}n`
|
|
104
|
+
case 'function': throw new Unserializable(`a function (${v.name || 'anonymous'})`)
|
|
105
|
+
case 'symbol': throw new Unserializable(`a symbol (${String(v)})`)
|
|
106
|
+
}
|
|
107
|
+
if (seen.has(v)) throw new Unserializable('a circular reference')
|
|
108
|
+
if (isEvent(v) || isElement(v)) {
|
|
109
|
+
if (mode == 'state') throw new Unserializable(isEvent(v) ? `a DOM ${v.type} event` : 'a DOM element')
|
|
110
|
+
flag.stubbed = true
|
|
111
|
+
return lit(isEvent(v) ? eventStub(v) : elementStub(v), mode, ind, seen, flag)
|
|
112
|
+
}
|
|
113
|
+
seen.add(v)
|
|
114
|
+
try {
|
|
115
|
+
const inner = ind + ' '
|
|
116
|
+
const list = (items: string[], open: string, close: string) => {
|
|
117
|
+
if (!items.length) return open + close
|
|
118
|
+
const one = open + (open == '{' ? ' ' : '') + items.join(', ') + (open == '{' ? ' ' : '') + close
|
|
119
|
+
if (one.length + ind.length <= 100 && !one.includes('\n')) return one
|
|
120
|
+
return open + '\n' + items.map(s => inner + s).join(',\n') + ',\n' + ind + close
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(v)) return list(Array.from(v, x => lit(x, mode, inner, seen, flag)), '[', ']')
|
|
123
|
+
if (v instanceof Date) return `new Date(${str(isNaN(+v) ? 'Invalid Date' : v.toISOString())})`
|
|
124
|
+
if (v instanceof Map) return `new Map(${list([...v].map(([k, x]) => list([lit(k, mode, inner + ' ', seen, flag), lit(x, mode, inner + ' ', seen, flag)], '[', ']')), '[', ']')})`
|
|
125
|
+
if (v instanceof Set) return `new Set(${list([...v].map(x => lit(x, mode, inner, seen, flag)), '[', ']')})`
|
|
126
|
+
const proto = Object.getPrototypeOf(v)
|
|
127
|
+
// G-265: a vnode of the JSX pragma (its own constructor, flagged $p) is a plain object
|
|
128
|
+
if (proto !== Object.prototype && proto !== null && !v.$p) throw new Unserializable(`a ${(v.constructor && v.constructor.name) || 'class instance'}`)
|
|
129
|
+
const items: string[] = []
|
|
130
|
+
for (const k of Object.keys(v)) {
|
|
131
|
+
if (v[k] === undefined) continue
|
|
132
|
+
items.push(`${IDENT.test(k) ? k : str(k)}: ${lit(v[k], mode, inner, seen, flag)}`)
|
|
133
|
+
}
|
|
134
|
+
return list(items, '{', '}')
|
|
135
|
+
} finally {
|
|
136
|
+
seen.delete(v)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const q = str
|
|
141
|
+
const tryLit = (v: any, mode: 'data' | 'state', ind: string): Lit | {error: string} => {
|
|
142
|
+
try { return literal(v, mode, ind) } catch (e: any) {
|
|
143
|
+
if (e instanceof Unserializable) return {error: e.message}
|
|
144
|
+
throw e
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** an error reply from makeFetchDriver: { error, request, status?, body? } for the request's `error` action */
|
|
149
|
+
const isErrorReply = (a: SessionAction) =>
|
|
150
|
+
a.data && typeof a.data == 'object' && 'error' in a.data && a.data.request && typeof a.data.request == 'object' && a.data.request.error === a.type
|
|
151
|
+
|
|
152
|
+
/** The test for a recorded session (see the module comment), with what it left out. */
|
|
153
|
+
export function sessionToTest(rec: SessionRecording, options: CopyAsTestOptions = {}): CopyAsTestResult {
|
|
154
|
+
const name = options.componentName || (IDENT.test(rec.component) ? rec.component : 'Component')
|
|
155
|
+
const warnings: string[] = []
|
|
156
|
+
const incomplete: string[] = []
|
|
157
|
+
const body: string[] = []
|
|
158
|
+
const I = ' '
|
|
159
|
+
const drivers = options.drivers || {}
|
|
160
|
+
let replayed = 0
|
|
161
|
+
|
|
162
|
+
if (rec.truncated) incomplete.push('the session lost its oldest actions (clear the log, then record a shorter session)')
|
|
163
|
+
|
|
164
|
+
// render options
|
|
165
|
+
const opts: string[] = []
|
|
166
|
+
if (rec.initialState !== undefined && !deepEqual(rec.initialState, rec.definitionInitialState)) {
|
|
167
|
+
const l = tryLit(rec.initialState, 'state', I)
|
|
168
|
+
if ('error' in l) {
|
|
169
|
+
warnings.push(`the initial state holds ${l.error}: rendering with the component's own initialState`)
|
|
170
|
+
incomplete.push('the initial state could not be written')
|
|
171
|
+
} else opts.push(`initialState: ${l.code}`)
|
|
172
|
+
}
|
|
173
|
+
const fakeable = new Set(rec.fakeable)
|
|
174
|
+
const driverNames = [...new Set([...rec.drivers.filter(n => !fakeable.has(n)), ...Object.keys(drivers)])]
|
|
175
|
+
const given = driverNames.filter(n => drivers[n] !== undefined)
|
|
176
|
+
const missing = driverNames.filter(n => drivers[n] === undefined)
|
|
177
|
+
if (given.length) opts.push(`drivers: { ${given.map(n => `${IDENT.test(n) ? n : q(n)}: ${drivers[n]}`).join(', ')} }`)
|
|
178
|
+
if (missing.length) warnings.push(`the app had ${missing.length == 1 ? 'a driver' : 'drivers'} for ${missing.join(', ')}: pass ${missing.length == 1 ? 'it' : 'them'} with copyAsTest(..., { drivers: { ${missing[0]}: '...' } }) if the component reads ${missing.length == 1 ? 'it' : 'them'}`)
|
|
179
|
+
if (options.renderOptions) opts.push(options.renderOptions)
|
|
180
|
+
|
|
181
|
+
const names = rec.actionNames && new Set(rec.actionNames)
|
|
182
|
+
// (G-218: t.respond / t.fail right after queued simulateAction calls wait for the request
|
|
183
|
+
// those calls send, so no settle() is needed before them)
|
|
184
|
+
const answer = (line: string) => body.push(line)
|
|
185
|
+
for (const a of rec.actions) {
|
|
186
|
+
const changes = a.sinks.includes('STATE') || a.sinks.includes('state')
|
|
187
|
+
if (REPLAY.has(a.cause)) {
|
|
188
|
+
// an intent over the component's own STATE stream: the replay produces it again
|
|
189
|
+
if (a.echo) continue
|
|
190
|
+
// an intent action with no model entry does nothing (renderComponent can't send it)
|
|
191
|
+
if (names && !names.has(a.type) && a.cause != 'behavior') continue
|
|
192
|
+
replayed++
|
|
193
|
+
if (a.data === undefined) { body.push(`${I}t.simulateAction(${q(a.type)})`); continue }
|
|
194
|
+
const l = tryLit(a.data, 'data', I)
|
|
195
|
+
if ('error' in l) {
|
|
196
|
+
body.push(`${I}t.simulateAction(${q(a.type)}) // data left out: it held ${l.error}`)
|
|
197
|
+
if (changes) incomplete.push(`${a.type}'s data could not be written (${l.error})`)
|
|
198
|
+
continue
|
|
199
|
+
}
|
|
200
|
+
if (l.stubbed) body.push(`${I}// ${a.type}: DOM event/element data as a stub (type, key, target dataset/value/checked/id)`)
|
|
201
|
+
body.push(`${I}t.simulateAction(${q(a.type)}, ${l.code})`)
|
|
202
|
+
} else if (a.cause == 'reply') {
|
|
203
|
+
const sink = a.replySink
|
|
204
|
+
if (!sink || a.replyKind != 'fetch' || drivers[sink] !== undefined) {
|
|
205
|
+
body.push(`${I}// reply ${a.type}${sink ? ` from ${sink}` : ''}: not replayed (renderComponent fakes makeFetchDriver sources only)`)
|
|
206
|
+
if (changes) incomplete.push(`the ${a.type} reply could not be replayed`)
|
|
207
|
+
continue
|
|
208
|
+
}
|
|
209
|
+
if (isErrorReply(a)) {
|
|
210
|
+
const err = a.data.error, status = a.data.status ?? (err && err.status), errBody = a.data.body ?? (err && err.body)
|
|
211
|
+
if (typeof status == 'number') {
|
|
212
|
+
if (errBody === undefined) answer(`${I}await t.fail(${q(sink)}, ${status}, ${q(a.type)})`)
|
|
213
|
+
else {
|
|
214
|
+
const l = tryLit(errBody, 'state', I)
|
|
215
|
+
if ('error' in l) { answer(`${I}await t.fail(${q(sink)}, ${status}, ${q(a.type)}) // body left out: it held ${l.error}`); if (changes) incomplete.push(`the ${a.type} error body could not be written`) }
|
|
216
|
+
else answer(`${I}await t.fail(${q(sink)}, ${status}, { request: (r) => r.error === ${q(a.type)}, body: ${l.code} })`)
|
|
217
|
+
}
|
|
218
|
+
} else if (a.data.issues) {
|
|
219
|
+
body.push(`${I}// reply ${a.type}: a validation failure, not replayed`)
|
|
220
|
+
if (changes) incomplete.push(`the ${a.type} validation failure could not be replayed`)
|
|
221
|
+
continue
|
|
222
|
+
} else {
|
|
223
|
+
answer(`${I}await t.fail(${q(sink)}, new Error(${str(String(err && err.message || err))}), ${q(a.type)})`)
|
|
224
|
+
}
|
|
225
|
+
replayed++
|
|
226
|
+
continue
|
|
227
|
+
}
|
|
228
|
+
const l = tryLit(a.data, 'state', I)
|
|
229
|
+
if ('error' in l) {
|
|
230
|
+
body.push(`${I}// reply ${a.type} from ${sink}: its data held ${l.error}, not replayed`)
|
|
231
|
+
if (changes) incomplete.push(`the ${a.type} reply's data could not be written`)
|
|
232
|
+
continue
|
|
233
|
+
}
|
|
234
|
+
answer(`${I}await t.respond(${q(sink)}, ${l.code}, ${q(a.type)})`)
|
|
235
|
+
replayed++
|
|
236
|
+
} else if (a.type == 'RESOURCE' && a.data && (a.data.status == 'success' || a.data.status == 'error')) {
|
|
237
|
+
body.push(`${I}// RESOURCE ${a.data.name}: ${a.data.status}, not replayed (answer it with t.respond / t.fail)`)
|
|
238
|
+
if (changes) incomplete.push(`resource '${a.data.name}' was answered`)
|
|
239
|
+
}
|
|
240
|
+
// 'next' and 'built-in': consequences, produced again by the replay
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (rec.foreign.length) {
|
|
244
|
+
const list = rec.foreign.slice(0, 5).map(f => `${f.component}#${f.instance} ${f.type}`).join(', ') + (rec.foreign.length > 5 ? ', ...' : '')
|
|
245
|
+
incomplete.push(`child components changed the state through their own actions (${list}): copy the test from that component instance instead`)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
let assertion: string[]
|
|
249
|
+
if (!incomplete.length) {
|
|
250
|
+
const l = tryLit(rec.finalState, 'state', I)
|
|
251
|
+
if ('error' in l) incomplete.push(`the final state holds ${l.error}`)
|
|
252
|
+
else assertion = [`${I}expect(t.state).toEqual(${l.code})`]
|
|
253
|
+
}
|
|
254
|
+
if (incomplete.length) {
|
|
255
|
+
warnings.push(...incomplete.map(s => 'no final-state assertion: ' + s))
|
|
256
|
+
assertion = [`${I}// No final-state assertion: ${incomplete.join('; ')}`]
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const head: string[] = []
|
|
260
|
+
if (options.environment) head.push(`// @vitest-environment ${options.environment}`)
|
|
261
|
+
head.push(`// Copied from a sygnal/devtools session of ${rec.component} (${rec.actions.length} recorded actions, ${replayed} replayed)`)
|
|
262
|
+
for (const w of warnings) head.push(`// NOTE: ${w}`)
|
|
263
|
+
head.push(`import { it, expect } from 'vitest'`, `import { renderComponent } from 'sygnal'`)
|
|
264
|
+
head.push(...(options.imports || []))
|
|
265
|
+
head.push(options.componentImport || `import ${name} from './${name}.js'`)
|
|
266
|
+
|
|
267
|
+
const testName = options.testName || `${rec.component} session (copied from sygnal/devtools)`
|
|
268
|
+
const render = opts.length ? `renderComponent(${name}, {\n${opts.map(o => `${I}${o},`).join('\n')}\n })` : `renderComponent(${name})`
|
|
269
|
+
const code = [
|
|
270
|
+
...head,
|
|
271
|
+
'',
|
|
272
|
+
`it(${q(testName)}, async () => {`,
|
|
273
|
+
` const t = ${render}`,
|
|
274
|
+
' try {',
|
|
275
|
+
`${I}await t.ready()`,
|
|
276
|
+
...body,
|
|
277
|
+
`${I}await t.settle()`,
|
|
278
|
+
...assertion!,
|
|
279
|
+
' } finally {',
|
|
280
|
+
`${I}t.dispose()`,
|
|
281
|
+
' }',
|
|
282
|
+
'})',
|
|
283
|
+
'',
|
|
284
|
+
].join('\n')
|
|
285
|
+
return {code, complete: !incomplete.length, warnings, replayed}
|
|
286
|
+
}
|
package/src/extra/devtools.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import {getDiagnostics} from './diagnostics/index';
|
|
2
|
+
import type {Diagnostic} from './diagnostics/index';
|
|
3
|
+
import {recordActions, clearActions, getActions, onAction, getSession, preview} from './devtoolsActions';
|
|
4
|
+
import type {DevtoolsAction, SessionRecording} from './devtoolsActions';
|
|
5
|
+
import {sessionToTest} from './copyAsTest';
|
|
6
|
+
import type {CopyAsTestOptions, CopyAsTestResult} from './copyAsTest';
|
|
7
|
+
|
|
8
|
+
import {addLayer, devtoolsHooks} from './devtoolsNext';
|
|
9
|
+
|
|
1
10
|
const DEVTOOLS_SOURCE = '__SYGNAL_DEVTOOLS_PAGE__';
|
|
2
11
|
const EXTENSION_SOURCE = '__SYGNAL_DEVTOOLS_EXTENSION__';
|
|
3
12
|
const DEFAULT_MAX_HISTORY = 200;
|
|
@@ -6,13 +15,10 @@ declare global {
|
|
|
6
15
|
interface Window {
|
|
7
16
|
__SYGNAL_DEVTOOLS__?: SygnalDevTools;
|
|
8
17
|
__SYGNAL_DEVTOOLS_APP__?: {
|
|
9
|
-
|
|
18
|
+
__runtime?: { setState: (id: any, fn: (s: any) => any) => void };
|
|
10
19
|
};
|
|
11
20
|
__SYGNAL_DEVTOOLS_PAGE__?: boolean;
|
|
12
21
|
SYGNAL_DEBUG?: string | false;
|
|
13
|
-
__SYGNAL_HMR_UPDATING?: boolean;
|
|
14
|
-
__SYGNAL_HMR_STATE?: unknown;
|
|
15
|
-
__SYGNAL_HMR_PERSISTED_STATE?: unknown;
|
|
16
22
|
__SYGNAL_HMR_LAST_CAPTURED_STATE?: unknown;
|
|
17
23
|
Cyclejs?: { sinks?: any };
|
|
18
24
|
CyclejsDevTool_startGraphSerializer?: (sinks: any) => void;
|
|
@@ -55,11 +61,16 @@ interface ExtensionMessage {
|
|
|
55
61
|
source?: string;
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
class SygnalDevTools {
|
|
64
|
+
export class SygnalDevTools {
|
|
59
65
|
_connected: boolean;
|
|
66
|
+
_initialized = false;
|
|
60
67
|
_components: Map<number, ComponentMeta>;
|
|
61
68
|
_stateHistory: StateHistoryEntry[];
|
|
62
69
|
_maxHistory: number;
|
|
70
|
+
/** PLAN-4 3-E: actions changed since the last ACTIONS post (flushed on a timeout) */
|
|
71
|
+
_dirty = new Set<DevtoolsAction>();
|
|
72
|
+
_flush: any = null;
|
|
73
|
+
_copyOptions: CopyAsTestOptions = {};
|
|
63
74
|
|
|
64
75
|
constructor() {
|
|
65
76
|
this._connected = false;
|
|
@@ -68,15 +79,33 @@ class SygnalDevTools {
|
|
|
68
79
|
this._maxHistory = DEFAULT_MAX_HISTORY;
|
|
69
80
|
}
|
|
70
81
|
|
|
82
|
+
/** Diagnostics collected by the shared diagnostics core (see src/extra/diagnostics). */
|
|
83
|
+
getDiagnostics(): Diagnostic[] {
|
|
84
|
+
return getDiagnostics();
|
|
85
|
+
}
|
|
86
|
+
|
|
71
87
|
get connected(): boolean {
|
|
72
88
|
return this._connected && typeof window !== 'undefined';
|
|
73
89
|
}
|
|
74
90
|
|
|
91
|
+
/** Publish the bridge on window and listen for the extension. Idempotent (D77: called once, by 'sygnal/devtools'). */
|
|
75
92
|
init(): void {
|
|
76
|
-
if (typeof window === 'undefined') return;
|
|
93
|
+
if (typeof window === 'undefined' || this._initialized) return;
|
|
94
|
+
this._initialized = true;
|
|
77
95
|
|
|
78
96
|
window.__SYGNAL_DEVTOOLS__ = this;
|
|
79
97
|
|
|
98
|
+
// PLAN-4 3-E (GS-10): the action log (recorded from now on, connected or not)
|
|
99
|
+
recordActions();
|
|
100
|
+
// PLAN-4.6 R4: the apps read this bridge's hooks (devtoolsNext.ts)
|
|
101
|
+
addLayer((api: any) => devtoolsHooks(this, api));
|
|
102
|
+
onAction((a, kind) => {
|
|
103
|
+
if (!this.connected) return;
|
|
104
|
+
if (kind == 'reset') { this._dirty.clear(); this._post('ACTIONS_RESET', {actions: []}); return; }
|
|
105
|
+
this._dirty.add(a!);
|
|
106
|
+
if (this._flush === null) this._flush = setTimeout(() => this._flushActions(), 0);
|
|
107
|
+
});
|
|
108
|
+
|
|
80
109
|
window.addEventListener('message', (event: MessageEvent) => {
|
|
81
110
|
if (event.source !== window) return;
|
|
82
111
|
if (event.data?.source === EXTENSION_SOURCE) {
|
|
@@ -91,6 +120,13 @@ class SygnalDevTools {
|
|
|
91
120
|
this._connected = true;
|
|
92
121
|
if (msg.payload?.maxHistory) this._maxHistory = msg.payload.maxHistory;
|
|
93
122
|
this._sendFullTree();
|
|
123
|
+
this._post('ACTIONS_RESET', {actions: getActions().slice(-500).map(a => this._serializeAction(a))});
|
|
124
|
+
break;
|
|
125
|
+
case 'CLEAR_ACTIONS':
|
|
126
|
+
clearActions();
|
|
127
|
+
break;
|
|
128
|
+
case 'COPY_AS_TEST':
|
|
129
|
+
this._post('COPY_AS_TEST_RESULT', this._copyAsTest(msg.payload?.instance));
|
|
94
130
|
break;
|
|
95
131
|
case 'DISCONNECT':
|
|
96
132
|
this._connected = false;
|
|
@@ -122,7 +158,7 @@ class SygnalDevTools {
|
|
|
122
158
|
hasIntent: !!instance.intent,
|
|
123
159
|
hasContext: !!instance.context,
|
|
124
160
|
hasCalculated: !!instance.calculated,
|
|
125
|
-
components:
|
|
161
|
+
components: [],
|
|
126
162
|
parentId: null,
|
|
127
163
|
children: [],
|
|
128
164
|
debug: instance._debug,
|
|
@@ -275,6 +311,42 @@ class SygnalDevTools {
|
|
|
275
311
|
});
|
|
276
312
|
}
|
|
277
313
|
|
|
314
|
+
/** PLAN-4 3-E: post the actions added or changed since the last flush */
|
|
315
|
+
_flushActions(): void {
|
|
316
|
+
this._flush = null;
|
|
317
|
+
if (!this.connected || !this._dirty.size) { this._dirty.clear(); return; }
|
|
318
|
+
const list = [...this._dirty].sort((a, b) => a.seq - b.seq).map(a => this._serializeAction(a));
|
|
319
|
+
this._dirty.clear();
|
|
320
|
+
this._post('ACTIONS', {actions: list});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
_serializeAction(a: DevtoolsAction): any {
|
|
324
|
+
return {
|
|
325
|
+
seq: a.seq, type: a.type, data: preview(a.data), component: a.component, instance: a.instance, parent: a.parent,
|
|
326
|
+
sinks: [...a.sinks], cause: a.cause, at: a.at,
|
|
327
|
+
...('after' in a ? {before: this._safeClone(a.before), after: this._safeClone(a.after)} : {}),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** "Copy as test" for the panel: the session of `instance` (default: the root) */
|
|
332
|
+
_copyAsTest(instance?: string | number): CopyAsTestResult | {error: string} {
|
|
333
|
+
try {
|
|
334
|
+
return sessionToTest(getSession(instance ?? undefined), this._copyOptions);
|
|
335
|
+
} catch (e: any) {
|
|
336
|
+
return {error: String(e?.message || e)};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** Defaults for "Copy as test" from the panel (componentImport, drivers, imports, ...) */
|
|
341
|
+
configureCopyAsTest(options: CopyAsTestOptions): void {
|
|
342
|
+
this._copyOptions = {...this._copyOptions, ...options};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** The recorded session of an instance (see sygnal/devtools getSession) */
|
|
346
|
+
getSession(target?: any): SessionRecording {
|
|
347
|
+
return getSession(target);
|
|
348
|
+
}
|
|
349
|
+
|
|
278
350
|
_setDebug({componentId, enabled}: {componentId?: number; enabled: boolean}): void {
|
|
279
351
|
if (typeof componentId === 'undefined' || componentId === null) {
|
|
280
352
|
if (typeof window !== 'undefined') window.SYGNAL_DEBUG = enabled ? 'true' : false;
|
|
@@ -286,7 +358,8 @@ class SygnalDevTools {
|
|
|
286
358
|
if (meta && meta._instanceRef) {
|
|
287
359
|
const instance = meta._instanceRef.deref();
|
|
288
360
|
if (instance) {
|
|
289
|
-
|
|
361
|
+
// (through the app's runtime API)
|
|
362
|
+
instance.__api?.setDebug(componentId, enabled);
|
|
290
363
|
meta.debug = enabled;
|
|
291
364
|
this._post('DEBUG_TOGGLED', {componentId, enabled});
|
|
292
365
|
}
|
|
@@ -303,40 +376,27 @@ class SygnalDevTools {
|
|
|
303
376
|
|
|
304
377
|
const newState = this._safeClone(state);
|
|
305
378
|
|
|
306
|
-
//
|
|
379
|
+
// Per-component time travel through the app's runtime API
|
|
307
380
|
const meta = this._components.get(componentId);
|
|
308
381
|
if (meta) {
|
|
309
382
|
const instance = meta._instanceRef?.deref();
|
|
310
383
|
if (!instance) {
|
|
311
384
|
console.warn(`[Sygnal DevTools] _timeTravel: WeakRef for component #${componentId} (${componentName}) has been GC'd`);
|
|
312
|
-
} else {
|
|
313
|
-
//
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
stateSink.shamefullySendNext(() => ({...newState}));
|
|
318
|
-
this._post('TIME_TRAVEL_APPLIED', {
|
|
319
|
-
componentId,
|
|
320
|
-
componentName,
|
|
321
|
-
state: newState,
|
|
322
|
-
});
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
console.warn(`[Sygnal DevTools] _timeTravel: component #${componentId} (${componentName}) has no STATE sink with shamefullySendNext. sinkName=${stateSinkName}, hasSinks=${!!instance.sinks}, sinkKeys=${instance.sinks ? Object.keys(instance.sinks).join(',') : 'none'}`);
|
|
385
|
+
} else if (instance.__api) {
|
|
386
|
+
// the runtime's setState (a queued action, one patch)
|
|
387
|
+
instance.__api.setState(componentId, () => ({...newState}));
|
|
388
|
+
this._post('TIME_TRAVEL_APPLIED', {componentId, componentName, state: newState});
|
|
389
|
+
return;
|
|
326
390
|
}
|
|
327
391
|
} else {
|
|
328
392
|
console.warn(`[Sygnal DevTools] _timeTravel: no meta for componentId ${componentId}`);
|
|
329
393
|
}
|
|
330
394
|
|
|
331
|
-
// Fall back to
|
|
395
|
+
// Fall back to the root's state
|
|
332
396
|
const app = window.__SYGNAL_DEVTOOLS_APP__;
|
|
333
|
-
if (app?.
|
|
334
|
-
app.
|
|
335
|
-
this._post('TIME_TRAVEL_APPLIED', {
|
|
336
|
-
componentId,
|
|
337
|
-
componentName,
|
|
338
|
-
state: newState,
|
|
339
|
-
});
|
|
397
|
+
if (app?.__runtime) {
|
|
398
|
+
app.__runtime.setState('root', () => ({...newState}));
|
|
399
|
+
this._post('TIME_TRAVEL_APPLIED', {componentId, componentName, state: newState});
|
|
340
400
|
} else {
|
|
341
401
|
console.warn(`[Sygnal DevTools] _timeTravel: no fallback root STATE sink available`);
|
|
342
402
|
}
|
|
@@ -498,9 +558,27 @@ class SygnalDevTools {
|
|
|
498
558
|
|
|
499
559
|
let instance: SygnalDevTools | null = null;
|
|
500
560
|
|
|
561
|
+
/**
|
|
562
|
+
* The bridge singleton of the 'sygnal/devtools' entry. Not part of the core
|
|
563
|
+
* (D77): the core only calls the hooks of window.__SYGNAL_DEVTOOLS__ when it exists.
|
|
564
|
+
*/
|
|
501
565
|
export function getDevTools(): SygnalDevTools {
|
|
502
566
|
if (!instance) instance = new SygnalDevTools();
|
|
503
567
|
return instance;
|
|
504
568
|
}
|
|
505
569
|
|
|
570
|
+
/**
|
|
571
|
+
* Install the bridge (what run() did before 6.0): window.__SYGNAL_DEVTOOLS__ is
|
|
572
|
+
* set and the extension can connect. A bridge already on window (e.g. a second
|
|
573
|
+
* copy of this entry) is kept. Nothing happens outside a browser.
|
|
574
|
+
*/
|
|
575
|
+
export function installDevTools(): SygnalDevTools | undefined {
|
|
576
|
+
if (typeof window === 'undefined') return undefined;
|
|
577
|
+
const existing = window.__SYGNAL_DEVTOOLS__;
|
|
578
|
+
if (existing) return existing;
|
|
579
|
+
const dt = getDevTools();
|
|
580
|
+
dt.init();
|
|
581
|
+
return dt;
|
|
582
|
+
}
|
|
583
|
+
|
|
506
584
|
export default getDevTools;
|