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,573 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLAN-4.6 core: the app runtime, one per run() (03-proposal §3, 04 §2.4: the flush contract).
|
|
3
|
+
*
|
|
4
|
+
* - dispatch(): one FIFO queue, run to completion. An action dispatched while the queue drains
|
|
5
|
+
* (a driver answering synchronously during sink delivery, an EFFECT, a PARENT, a hook) is
|
|
6
|
+
* appended, never run nested; one dispatched while the flush renders is drained in that flush.
|
|
7
|
+
* - commit(): a state write schedules one flush (a microtask).
|
|
8
|
+
* - flush(): repeat { STATE streams, render top-down (dirty-checked), statics (R3's hook point),
|
|
9
|
+
* drain the queue } until nothing changed; then ONE vnode to the DOM driver (one patch), then
|
|
10
|
+
* the disposed instances' streams are stopped, and BOOTSTRAP goes to the instances created in
|
|
11
|
+
* it a microtask later (D165). No startup timers, gates or holds.
|
|
12
|
+
* - loop guard (G-260 / G-283 / G-284): at most 100 passes per flush; a flush caused by the one
|
|
13
|
+
* before it (a commit while it ran or emitted: a loop that never settles) is chained, and past
|
|
14
|
+
* 100 chained flushes, or 1,000 flushes of any kind without a macrotask, the next one waits for
|
|
15
|
+
* a MessageChannel message. No setTimeout anywhere.
|
|
16
|
+
* - teardown (D165 / Q18): while an instance unsubscribes, Stream.prototype._remove queues the
|
|
17
|
+
* streams it leaves without listeners instead of arming xstream's stop timer, and the queue is
|
|
18
|
+
* stopped at the first macrotask after the drain / flush (G-302). The swap is scoped to dispose().
|
|
19
|
+
*/
|
|
20
|
+
import xs from '../extra/xstreamCompat'
|
|
21
|
+
import {makeDOMDriver} from '../cycle/dom/index'
|
|
22
|
+
import eventBusDriver from '../extra/eventDriver'
|
|
23
|
+
import logDriver from '../extra/logDriver'
|
|
24
|
+
import {NOT_SINK, isAbort} from '../shared'
|
|
25
|
+
import {error as logError, callHook} from '../extra/diagnostics/legacy'
|
|
26
|
+
import type {ComponentFn, DefSource, Hooks, RuntimeAPI, ActionCause} from './hooks'
|
|
27
|
+
import {CoreDef, defOf, rootDef} from './define'
|
|
28
|
+
import {rootCell} from './cell'
|
|
29
|
+
import {Inst} from './instance'
|
|
30
|
+
import {viewOf} from './view'
|
|
31
|
+
import {stopQueued, INST, SET, SEED} from './teardown'
|
|
32
|
+
import {scanSources} from './statics'
|
|
33
|
+
// the public ClientOnly is a separate bundle (sygnal/vike/ClientOnly) with no core of its own, so
|
|
34
|
+
// its marker handler registers with the core (the other markers register from their modules)
|
|
35
|
+
import './markers/clientonly'
|
|
36
|
+
|
|
37
|
+
const LOOP = 100, HARD = 1000
|
|
38
|
+
// the macrotask, captured at load (a test's fake timers don't hold the ping): Node drains a
|
|
39
|
+
// MessagePort's messages back to back (timers starve), so its setImmediate is the macrotask
|
|
40
|
+
// there; browsers have no setImmediate and get a MessageChannel
|
|
41
|
+
const G: any = globalThis
|
|
42
|
+
const SI: ((f: () => void) => void) | undefined = typeof G.setImmediate == 'function' ? G.setImmediate.bind(G) : undefined
|
|
43
|
+
const MC: any = typeof G.MessageChannel == 'function' ? G.MessageChannel : undefined
|
|
44
|
+
const ERR_FIX = 'See the attached error'
|
|
45
|
+
|
|
46
|
+
export interface StartOptions {
|
|
47
|
+
mountPoint?: string | Element
|
|
48
|
+
/** no effect (3-Q: the DOM driver flattens fragments) */
|
|
49
|
+
fragments?: boolean
|
|
50
|
+
useDefaultDrivers?: boolean
|
|
51
|
+
onError?: (error: any, info: any) => void
|
|
52
|
+
uid?: string
|
|
53
|
+
/** internal: this app's hooks (testing, devtools, diagnostics) */
|
|
54
|
+
__hooks?: Hooks
|
|
55
|
+
/** internal: the root's state at start (an HMR swap) */
|
|
56
|
+
__state?: any
|
|
57
|
+
/** internal: an HMR swap: the instances its first flush makes get no BOOTSTRAP (as today's `__hmr`) */
|
|
58
|
+
__swap?: boolean
|
|
59
|
+
/** internal: renderComponent's root (test intent, initial state, name) */
|
|
60
|
+
__override?: Partial<DefSource> & {name?: string; testActions?: string[]}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class App {
|
|
64
|
+
state: any = undefined
|
|
65
|
+
queue: any[] = []
|
|
66
|
+
draining = false
|
|
67
|
+
rendering = false
|
|
68
|
+
flushing = false
|
|
69
|
+
scheduled = false
|
|
70
|
+
dirty = false
|
|
71
|
+
/** in a flush's emission (a commit there chains the next flush) */
|
|
72
|
+
tail = false
|
|
73
|
+
chained = false
|
|
74
|
+
chain = 0
|
|
75
|
+
flushes = 0
|
|
76
|
+
/** bumped on every state write (context memo) */
|
|
77
|
+
ver = 0
|
|
78
|
+
/** render epoch: bumped when a render threw (G-311: every instance injects its kids again once) */
|
|
79
|
+
ep = 0
|
|
80
|
+
root!: Inst
|
|
81
|
+
last: any
|
|
82
|
+
/** P46-P: a view ran but kept its last vnode (the flush still emits, as when it changed) */
|
|
83
|
+
ran = false
|
|
84
|
+
vdomL: any
|
|
85
|
+
vdom$: any
|
|
86
|
+
sources: Record<string, any> = {}
|
|
87
|
+
proxies: Record<string, any> = {}
|
|
88
|
+
bus: Record<string, any> = {}
|
|
89
|
+
busL: Record<string, any> = {}
|
|
90
|
+
/** G-296: before the first flush emitted, the sink values sent, per sink */
|
|
91
|
+
early = true
|
|
92
|
+
log: Record<string, any[]> = {}
|
|
93
|
+
ex: Record<string, any> = {}
|
|
94
|
+
exL: Record<string, any> = {}
|
|
95
|
+
hooks: Hooks
|
|
96
|
+
/** run()'s hooks, then each addHooks() layer (G-297) */
|
|
97
|
+
layers: Hooks[]
|
|
98
|
+
born: Inst[] = []
|
|
99
|
+
watchers = new Set<Inst>()
|
|
100
|
+
/** streams the disposed instances left without listeners, stopped when the drain / flush ends */
|
|
101
|
+
dq: any[] = []
|
|
102
|
+
stopping = false
|
|
103
|
+
disposed = false
|
|
104
|
+
initState: any
|
|
105
|
+
/** D168 hook point (onContextSkip): a view context tracking skipped, with the context before and after */
|
|
106
|
+
ctxSkip: ((inst: Inst, prev: any, next: any) => void) | null = null
|
|
107
|
+
/** the statics step, recomputed after each render pass (statics.ts; set when a driver takes a static) */
|
|
108
|
+
afterRender: (() => void) | null = null
|
|
109
|
+
/** [sink, static] of the drivers that take a static; the reply-capable sources (statics.ts) */
|
|
110
|
+
stat: Array<[string, string]> = []
|
|
111
|
+
rep: string[] = []
|
|
112
|
+
/** a definition's [sink, static] pairs (this app's drivers) */
|
|
113
|
+
stc = new WeakMap<CoreDef, Array<[string, string]> | null>()
|
|
114
|
+
/** the live instances that declare a static */
|
|
115
|
+
statics = new Set<Inst>()
|
|
116
|
+
defs = new WeakMap<ComponentFn, CoreDef>()
|
|
117
|
+
waiters: Array<() => void> = []
|
|
118
|
+
scopeN = 0
|
|
119
|
+
mc: any
|
|
120
|
+
hops: Array<() => void> = []
|
|
121
|
+
ping = false
|
|
122
|
+
desc: any[] = []
|
|
123
|
+
|
|
124
|
+
constructor(public opts: StartOptions = {}) {
|
|
125
|
+
this.hooks = {...opts.__hooks}
|
|
126
|
+
this.layers = [opts.__hooks || {}]
|
|
127
|
+
this.initState = opts.__state
|
|
128
|
+
this.compose()
|
|
129
|
+
// the dev entries' hooks ('sygnal/diagnostics', 'sygnal/devtools'), one layer each (04 §2.2:
|
|
130
|
+
// what the bridge holds becomes hooks, read once per app); none without a dev entry
|
|
131
|
+
const L = G.__SYGNAL_DIAGNOSTICS__?.layers
|
|
132
|
+
// G-330: a throwing layer factory (a dev-entry bug) is reported and skipped; the app starts
|
|
133
|
+
if (L) for (const f of L) {
|
|
134
|
+
let h
|
|
135
|
+
try { h = f(this.api()) } catch (e) { logError('SYG900', 'run', 'A dev-tools hook layer threw while the app started; it is skipped', 'Report this as a Sygnal bug', e); continue }
|
|
136
|
+
this.addHooks(h)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
def(view: ComponentFn, override?: StartOptions['__override']) {
|
|
141
|
+
return defOf(view, this.hooks.transformDef, this.defs, override)
|
|
142
|
+
}
|
|
143
|
+
/** a child's isolation scope (one for every channel of the instance, as isolate() gave) */
|
|
144
|
+
scope() { return 's' + ++this.scopeN }
|
|
145
|
+
|
|
146
|
+
// ---------------------------------------------------------------- queue
|
|
147
|
+
/** `src`: for a reply, the source that delivered it (the action log's replySink) */
|
|
148
|
+
dispatch(inst: Inst, type: any, data: any, cause: ActionCause = 'intent', src?: string) {
|
|
149
|
+
if (this.disposed) return
|
|
150
|
+
this.queue.push(inst, type, data, cause, src)
|
|
151
|
+
if (!this.draining && !this.rendering) this.drain()
|
|
152
|
+
}
|
|
153
|
+
drain() {
|
|
154
|
+
this.draining = true
|
|
155
|
+
const q = this.queue
|
|
156
|
+
try {
|
|
157
|
+
for (let i = 0; i < q.length; i += 5) {
|
|
158
|
+
const inst: Inst = q[i]
|
|
159
|
+
if (inst.disposed) continue
|
|
160
|
+
// G-331: one item that throws (a setState function from devtools / Vike / sygnal/element,
|
|
161
|
+
// a hook) is reported; the rest of the queue still runs
|
|
162
|
+
try {
|
|
163
|
+
if (q[i + 1] === SET) {
|
|
164
|
+
const f = q[i + 2], c = inst.cell
|
|
165
|
+
const v = typeof f == 'function' ? f(c.get()) : f
|
|
166
|
+
// (ABORT: no change, as from a reducer)
|
|
167
|
+
if (v !== c.get() && !isAbort(v)) c.set(v)
|
|
168
|
+
} else if (q[i + 1] === SEED) {
|
|
169
|
+
// G-309: decided when it is applied: a parent write queued before it keeps the slice (D174)
|
|
170
|
+
const d = q[i + 2], b = d.b
|
|
171
|
+
if (d.r || !(b.has ? b.has() : b.get() !== undefined)) inst.cell.set(d.v)
|
|
172
|
+
else this.hooks.onStateSeed?.(viewOf(inst), b.get(), d.v)
|
|
173
|
+
}
|
|
174
|
+
else inst.handle(q[i + 1], q[i + 2], q[i + 3], q[i + 4])
|
|
175
|
+
} catch (e) {
|
|
176
|
+
this.caught(inst, 'SYG216', `${typeof q[i + 1] == 'string' ? `Action '${q[i + 1]}'` : 'A state update'} threw; the actions queued after it still run`, e, 'reducer', typeof q[i + 1] == 'string' ? q[i + 1] : undefined)
|
|
177
|
+
}
|
|
178
|
+
if (this.watchers.size) this.notify()
|
|
179
|
+
}
|
|
180
|
+
} finally {
|
|
181
|
+
q.length = 0
|
|
182
|
+
this.draining = false
|
|
183
|
+
if (!this.flushing && this.dq.length) this.stopLater()
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** STATE.stream: the instances whose state changed emit it */
|
|
187
|
+
notify() {
|
|
188
|
+
for (const i of this.watchers) i.notify()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ---------------------------------------------------------------- flush
|
|
192
|
+
commit() {
|
|
193
|
+
this.ver++
|
|
194
|
+
if (this.flushing) { this.dirty = true; return }
|
|
195
|
+
if (this.tail) this.chained = true
|
|
196
|
+
if (this.scheduled || this.disposed) return
|
|
197
|
+
this.scheduled = true
|
|
198
|
+
const chained = this.chained
|
|
199
|
+
this.chained = false
|
|
200
|
+
this.chain = chained ? this.chain + 1 : 0
|
|
201
|
+
// the first flush since the last macrotask pings one, which resets the count
|
|
202
|
+
if (!this.flushes++) this.macro()
|
|
203
|
+
if (this.chain > LOOP || this.flushes > HARD) this.macro(() => { this.chain = 0; this.flush() })
|
|
204
|
+
else queueMicrotask(() => this.flush())
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* A macrotask without a timer: one MessageChannel per app, at most one message in flight (it
|
|
208
|
+
* resets the flush count, then runs the hops waiting for it)
|
|
209
|
+
*/
|
|
210
|
+
macro(f?: () => void) {
|
|
211
|
+
if (f) this.hops.push(f)
|
|
212
|
+
if (this.ping) return
|
|
213
|
+
this.ping = true
|
|
214
|
+
if (SI) return void SI(() => this.pong())
|
|
215
|
+
if (!MC) return void setTimeout(() => this.pong())
|
|
216
|
+
if (!this.mc) {
|
|
217
|
+
this.mc = new MC()
|
|
218
|
+
this.mc.port1.onmessage = () => this.pong()
|
|
219
|
+
this.mc.port1.unref?.()
|
|
220
|
+
}
|
|
221
|
+
this.mc.port2.postMessage(0)
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* G-302: the streams disposed instances left without listeners stop at the first macrotask, as
|
|
225
|
+
* xstream's own deferred stop does (a shared stream unmounted in one flush and mounted again in
|
|
226
|
+
* the next keeps running), but through macro(): no setTimeout per stream
|
|
227
|
+
*/
|
|
228
|
+
stopLater() {
|
|
229
|
+
if (this.stopping) return
|
|
230
|
+
this.stopping = true
|
|
231
|
+
this.macro(() => { this.stopping = false; stopQueued(this.dq, this.stopErr) })
|
|
232
|
+
}
|
|
233
|
+
pong() {
|
|
234
|
+
this.ping = false
|
|
235
|
+
this.flushes = 0
|
|
236
|
+
for (const g of this.hops.splice(0)) g()
|
|
237
|
+
}
|
|
238
|
+
flush() {
|
|
239
|
+
this.scheduled = false
|
|
240
|
+
if (this.disposed) return this.release()
|
|
241
|
+
try { this.flushOnce() } finally { this.release() }
|
|
242
|
+
}
|
|
243
|
+
/** G-301: flushed() resolves once nothing is scheduled, after a throwing flush, or on dispose */
|
|
244
|
+
release() {
|
|
245
|
+
if (this.waiters.length && (!this.scheduled || this.disposed)) for (const w of this.waiters.splice(0)) w()
|
|
246
|
+
}
|
|
247
|
+
flushOnce() {
|
|
248
|
+
this.flushing = true
|
|
249
|
+
let v: any, n = 0
|
|
250
|
+
try {
|
|
251
|
+
do {
|
|
252
|
+
this.dirty = false
|
|
253
|
+
if (this.watchers.size) this.notify()
|
|
254
|
+
this.rendering = true
|
|
255
|
+
try { v = this.root.render() } catch (e) {
|
|
256
|
+
// G-298: an error that escaped every boundary (a hook, a host): reported; the DOM keeps
|
|
257
|
+
// its last render and the flush still ends (BOOTSTRAP, teardown, waiters)
|
|
258
|
+
this.rendering = false
|
|
259
|
+
this.caught(this.root, 'SYG406', 'Render threw; the page keeps its last render', e, 'view')
|
|
260
|
+
v = this.last
|
|
261
|
+
// G-311: the instances that rendered before the throw are injected at the next render
|
|
262
|
+
this.ep++
|
|
263
|
+
// G-320: the statics still follow the state (a throwing render doesn't freeze them)
|
|
264
|
+
try { this.afterRender?.() } catch (_) { /* reported by the statics step */ }
|
|
265
|
+
break
|
|
266
|
+
}
|
|
267
|
+
this.afterRender?.()
|
|
268
|
+
this.rendering = false
|
|
269
|
+
if (this.queue.length) this.drain()
|
|
270
|
+
} while (this.dirty && ++n < LOOP)
|
|
271
|
+
} finally {
|
|
272
|
+
this.rendering = false
|
|
273
|
+
this.flushing = false
|
|
274
|
+
}
|
|
275
|
+
this.tail = true
|
|
276
|
+
try {
|
|
277
|
+
// G-311 (c): what the throwing pass queued (a new child's INITIALIZE / seed) is still applied
|
|
278
|
+
if (this.queue.length) this.drain()
|
|
279
|
+
if (v !== this.last || this.ran) {
|
|
280
|
+
this.ran = false
|
|
281
|
+
this.last = v
|
|
282
|
+
this.hooks.onPatch?.(v)
|
|
283
|
+
this.vdomL?.next(v)
|
|
284
|
+
}
|
|
285
|
+
if (this.dq.length) this.stopLater()
|
|
286
|
+
} finally {
|
|
287
|
+
// G-313: a throwing patch / onPatch still ends the startup log and dispatches BOOTSTRAP
|
|
288
|
+
this.tail = false
|
|
289
|
+
// an HMR swap: what the start rendered was already bootstrapped in the old app
|
|
290
|
+
if (this.opts.__swap) { this.opts.__swap = false; this.born.length = 0 }
|
|
291
|
+
if (this.born.length) {
|
|
292
|
+
const b = this.born.splice(0)
|
|
293
|
+
queueMicrotask(() => { for (const i of b) if (!i.disposed) this.dispatch(i, 'BOOTSTRAP', undefined, 'built-in') })
|
|
294
|
+
}
|
|
295
|
+
if (this.dirty) { this.chained = true; this.dirty = false; this.commit() }
|
|
296
|
+
if (this.early) { this.early = false; this.log = {} }
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ---------------------------------------------------------------- sinks
|
|
301
|
+
/** the app's stream for one sink (every instance's values; drivers get them synchronously) */
|
|
302
|
+
sink(n: string) {
|
|
303
|
+
return this.bus[n] ||= xs.create({start: (l: any) => { this.busL[n] = l }, stop: () => { delete this.busL[n] }})
|
|
304
|
+
}
|
|
305
|
+
out(n: string, v: any) {
|
|
306
|
+
// G-296: until the first flush has emitted, a sink's values are kept for a listener added
|
|
307
|
+
// right after run() / renderComponent (sinks[n], as Cycle's run buffered its sinks)
|
|
308
|
+
if (this.early) (this.log[n] ||= []).push(v)
|
|
309
|
+
const l = this.busL[n]
|
|
310
|
+
if (!l) return
|
|
311
|
+
try { l.next(v) } catch (e) {
|
|
312
|
+
// GS-11: a driver that throws handling a sink value goes to the app's onError ('driver'),
|
|
313
|
+
// and to the hooks' onError (G-303)
|
|
314
|
+
const info: any = {phase: 'driver', driver: n}
|
|
315
|
+
callHook(this.opts.onError, e, info)
|
|
316
|
+
callHook(this.hooks.onError, e, info)
|
|
317
|
+
queueMicrotask(() => { throw e })
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/** run()'s sinks[n]: the bus, with the values sent before the first flush replayed to its first listener (G-296) */
|
|
321
|
+
exposed(n: string) {
|
|
322
|
+
return this.ex[n] ||= xs.create({
|
|
323
|
+
start: (l: any) => {
|
|
324
|
+
const early = this.early ? this.log[n] : undefined
|
|
325
|
+
if (early) for (const v of early.slice()) l.next(v)
|
|
326
|
+
this.sink(n).addListener(this.exL[n] = {next: (v: any) => l.next(v), error: (e: any) => l.error(e), complete: () => l.complete()})
|
|
327
|
+
},
|
|
328
|
+
stop: () => { this.sink(n).removeListener(this.exL[n]) },
|
|
329
|
+
})
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* The value-level fallback (a source without isolateValue): one stream per instance and key
|
|
333
|
+
* through `build` (isolateSink at each level), delivered to `to` synchronously.
|
|
334
|
+
*/
|
|
335
|
+
pipe(inst: Inst, key: string, v: any, build: (s$: any) => any, to: (x: any) => void) {
|
|
336
|
+
const fb = inst.fb ||= {}
|
|
337
|
+
let p = fb[key]
|
|
338
|
+
if (!p) {
|
|
339
|
+
const in$ = xs.create()
|
|
340
|
+
p = fb[key] = in$
|
|
341
|
+
inst.subs.push(build(in$).subscribe({next: to}))
|
|
342
|
+
}
|
|
343
|
+
p.shamefullySendNext(v)
|
|
344
|
+
}
|
|
345
|
+
/** an instance's vnode, scoped by its parent's DOM source (isolateValue, else a pipe) */
|
|
346
|
+
scopeValue(parent: Inst, _: string, v: any, inst: Inst) {
|
|
347
|
+
const d = parent.dom
|
|
348
|
+
if (!d || inst.scope === undefined) return v
|
|
349
|
+
if (typeof d.isolateValue == 'function') return d.isolateValue(v, inst.scope)
|
|
350
|
+
let r: any
|
|
351
|
+
this.pipe(inst, '\u0000DOM', v, (s$) => d.isolateSink(s$, inst.scope), (x) => { r = x })
|
|
352
|
+
return r
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ---------------------------------------------------------------- errors
|
|
356
|
+
/** G-342/G-267: a stream's stop() threw while an instance's streams were torn down */
|
|
357
|
+
stopErr = (e: any, inst: any) => {
|
|
358
|
+
console.error(`[Sygnal] ${inst ? inst.def.name + ': ' : ''}a stream's stop() threw while it was torn down; the other streams stopped`, e)
|
|
359
|
+
inst ? this.appError(inst, e, 'dispose') : callHook(this.opts.onError, e, {phase: 'dispose'})
|
|
360
|
+
}
|
|
361
|
+
appError(inst: Inst, e: any, phase: string, action?: string) {
|
|
362
|
+
const info: any = {componentName: inst.def.name, action, phase}
|
|
363
|
+
callHook(this.opts.onError, e, info)
|
|
364
|
+
// 3-W G-546: a hook layer's onError that throws is logged (as the app's is)
|
|
365
|
+
callHook(this.hooks.onError, e, info)
|
|
366
|
+
}
|
|
367
|
+
/** an error caught at a call site that keeps running (as legacy caught(): a fail()'s own code) */
|
|
368
|
+
caught(inst: Inst, code: string, message: string, err: any, phase: string, action?: string) {
|
|
369
|
+
const s = err && err.sygnal
|
|
370
|
+
s ? logError(err.code, s[0], `${s[1]} (${message})`, s[2], err) : logError(code, inst, message, ERR_FIX, err)
|
|
371
|
+
this.appError(inst, err, phase, action)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// ---------------------------------------------------------------- intent sources
|
|
375
|
+
/** the getters of an instance's intent sources (one descriptor map per app and kind) */
|
|
376
|
+
srcDesc(child: boolean) {
|
|
377
|
+
const k = +child
|
|
378
|
+
if (this.desc[k]) return this.desc[k]
|
|
379
|
+
const d: any = {}
|
|
380
|
+
const get = (f: (i: Inst) => any) => ({get(this: any) { return f(this[INST]) }, enumerable: true, configurable: true})
|
|
381
|
+
for (const n in this.sources) if (n !== 'DOM') d[n] = get(i => i.src(n))
|
|
382
|
+
d.DOM = get(i => i.dom && (i.wd ||= wrapDOM(i.dom)))
|
|
383
|
+
d.STATE = get(i => i.stateSource())
|
|
384
|
+
d.CHILD = get(i => i.childSource())
|
|
385
|
+
d.dispose$ = get(i => i.dispose$())
|
|
386
|
+
if (child) {
|
|
387
|
+
d.props$ = get(i => i.props$ || seeded(i, 'props$', i.props))
|
|
388
|
+
d.children$ = get(i => i.children$ || seeded(i, 'children$', i.children))
|
|
389
|
+
// a Command passed as a prop (createCommand): its messages, as today's `commands$`
|
|
390
|
+
d.commands$ = get(i => i.commands())
|
|
391
|
+
}
|
|
392
|
+
return (this.desc[k] = d)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ---------------------------------------------------------------- the runtime API (04 §2.3)
|
|
396
|
+
api(): RuntimeAPI {
|
|
397
|
+
const app = this
|
|
398
|
+
const find = (t: any): Inst | undefined => t === 'root' ? app.root : typeof t == 'number' ? byId(app.root, t) : t && byId(app.root, t.id)
|
|
399
|
+
return {
|
|
400
|
+
get root() { return viewOf(app.root) },
|
|
401
|
+
get: (id) => { const i = byId(app.root, id); return i && viewOf(i) },
|
|
402
|
+
getState: () => app.state,
|
|
403
|
+
setState: (t, s) => { const i = find(t); if (i) app.dispatch(i, SET, s, 'setState') },
|
|
404
|
+
dispatch: (t, type, data, cause = 'simulateAction') => { const i = find(t); if (i) app.dispatch(i, type, data, cause) },
|
|
405
|
+
addHooks: (h) => app.addHooks(h),
|
|
406
|
+
setDebug: (t, on) => { const i = find(t); if (i) i.debug = on },
|
|
407
|
+
flushed: () => new Promise<void>(r => (app.scheduled || app.flushing ? app.waiters.push(r) : r())),
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* A layer of hooks on top of run()'s own (devtools connecting late, a dev entry). G-297: the
|
|
412
|
+
* remover drops only its own layer; the hooks are composed again from the layers left.
|
|
413
|
+
*/
|
|
414
|
+
addHooks(h: Hooks) {
|
|
415
|
+
this.layers.push(h)
|
|
416
|
+
this.compose()
|
|
417
|
+
return () => {
|
|
418
|
+
const i = this.layers.indexOf(h)
|
|
419
|
+
if (i >= 0) { this.layers.splice(i, 1); this.compose() }
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
compose() {
|
|
423
|
+
const H: any = this.hooks
|
|
424
|
+
for (const k in H) delete H[k]
|
|
425
|
+
this.ctxSkip = null
|
|
426
|
+
for (const layer of this.layers) {
|
|
427
|
+
const added: any = layer
|
|
428
|
+
for (const k in added) {
|
|
429
|
+
const a = added[k], b = H[k]
|
|
430
|
+
if (!a) continue
|
|
431
|
+
// G-312: a wrapping layer that returns nothing keeps what the layers below gave
|
|
432
|
+
H[k] = !b ? a
|
|
433
|
+
: k == 'wrapHandler' ? (i: any, t: any, s: any, f: any) => { const x = b(i, t, s, f) || f; return a(i, t, s, x) || x }
|
|
434
|
+
: k == 'wrapSources' ? (i: any, s: any) => { const x = b(i, s) || s; return a(i, x) || x }
|
|
435
|
+
: k == 'transformDef' ? (src: any, v: any) => { const x = b(src, v) || src; return a(x, v) || x }
|
|
436
|
+
// onElementCommand: false (not run) from either layer
|
|
437
|
+
: k == 'onElementCommand' ? (i: any, c: any) => { const x = b(i, c); return a(i, c) === false || x === false ? false : undefined }
|
|
438
|
+
: (...args: any[]) => { b(...args); a(...args) }
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// D168's safety net (R4): a view context tracking skipped, offered for a re-run check
|
|
442
|
+
const cs = H.onContextSkip
|
|
443
|
+
if (cs) this.ctxSkip = (i, a, b) => cs(viewOf(i), (c: any) => i.def.view({...i.props, state: i.cell.get(), children: i.children, slots: i.slots, context: c, uid: i.uid}), a, b, [...(i.keys || [])])
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
dispose() {
|
|
447
|
+
if (this.disposed) return
|
|
448
|
+
try { this.root?.dispose() } finally {
|
|
449
|
+
stopQueued(this.dq, this.stopErr)
|
|
450
|
+
this.disposed = true
|
|
451
|
+
for (const n in this.sources) try { this.sources[n]?.dispose?.() } catch (_) {}
|
|
452
|
+
for (const n in this.proxies) try { this.proxies[n]._c() } catch (_) {}
|
|
453
|
+
this.mc?.port1.close()
|
|
454
|
+
this.release()
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function wrapDOM(dom: any) {
|
|
460
|
+
return new Proxy(dom, {get: (t, p, r) => (typeof p == 'symbol' || p in t ? Reflect.get(t, p, r) : (sel: any) => t.select(sel).events(p))})
|
|
461
|
+
}
|
|
462
|
+
/** props$ / children$: a memory stream with the current value, fed by setProps */
|
|
463
|
+
function seeded(i: any, k: string, v: any) {
|
|
464
|
+
const s = xs.createWithMemory()
|
|
465
|
+
s.shamefullySendNext(v)
|
|
466
|
+
return (i[k] = s)
|
|
467
|
+
}
|
|
468
|
+
function byId(i: Inst, id: number): Inst | undefined {
|
|
469
|
+
if (i.id === id) return i
|
|
470
|
+
for (const k of i.kids.values()) {
|
|
471
|
+
if (k instanceof Inst) { const f = byId(k, id); if (f) return f }
|
|
472
|
+
else if (k.insts) for (const j of k.insts()) { const f = byId(j, id); if (f) return f }
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* The root shim a root setup (persist, GS-5: `Root.persist.setup(component)`, root only as today)
|
|
478
|
+
* runs on before the root exists (04 §3.2, §4 #1): the instance fields it reads and writes today.
|
|
479
|
+
* It may restore into `initialState` and rewrite `model` (both read back into the root's Def);
|
|
480
|
+
* `sources.STATE.stream`, `_dispose$` and `vdom$` are the root's, linked once it exists (the
|
|
481
|
+
* returned function); `action$.shamefullySendNext` dispatches to the root (RESTORE).
|
|
482
|
+
*/
|
|
483
|
+
function rootShim(app: App, Root: ComponentFn, src: DefSource): [any, () => void] {
|
|
484
|
+
const st$ = xs.create(), dsp$ = xs.create()
|
|
485
|
+
const shim = {
|
|
486
|
+
name: (Root as any).componentName || Root.name || 'FUNCTION_COMPONENT', view: Root,
|
|
487
|
+
model: src.model, initialState: src.initialState, calculated: src.calculated, stateSourceName: 'STATE',
|
|
488
|
+
sources: {...app.sources, STATE: {stream: st$}},
|
|
489
|
+
vdom$: app.vdom$, _dispose$: dsp$,
|
|
490
|
+
action$: {shamefullySendNext: (a: any) => { if (app.root) app.dispatch(app.root, a.type, a.data, 'built-in') }},
|
|
491
|
+
}
|
|
492
|
+
return [shim, () => {
|
|
493
|
+
const r = app.root
|
|
494
|
+
r.stateSource().stream.addListener({next: (v: any) => st$.shamefullySendNext(v), error: () => {}})
|
|
495
|
+
r.dispose$().addListener({next: (v: any) => dsp$.shamefullySendNext(v), error: () => {}})
|
|
496
|
+
}]
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export interface Started {
|
|
500
|
+
app: App
|
|
501
|
+
sources: Record<string, any>
|
|
502
|
+
sinks: Record<string, any>
|
|
503
|
+
dispose: () => void
|
|
504
|
+
api: RuntimeAPI
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Start an app: the drivers (run()'s defaults unless useDefaultDrivers is
|
|
509
|
+
* false), the root instance, the first flush. Returns run()'s shape (sources with the root's
|
|
510
|
+
* STATE, sinks: one stream per driver sink, DOM and the root's PARENT) plus the runtime API.
|
|
511
|
+
*/
|
|
512
|
+
export function start(Root: ComponentFn, drivers: Record<string, any> = {}, opts: StartOptions = {}): Started {
|
|
513
|
+
const app = new App(opts)
|
|
514
|
+
const {mountPoint = '#root', useDefaultDrivers = true} = opts
|
|
515
|
+
// G-537 (D223): a DOM patch that throws goes to onError (phase 'patch') and the hooks' onError;
|
|
516
|
+
// it is logged only without an onError. G-545: given to any DOM driver (one passed in drivers,
|
|
517
|
+
// makeViewTransitionDOMDriver) through its source's isolate module
|
|
518
|
+
const patchErr = (e: any, i: any = {phase: 'patch'}) => {
|
|
519
|
+
opts.onError ? callHook(opts.onError, e, i) : console.error(e)
|
|
520
|
+
callHook(app.hooks.onError, e, i)
|
|
521
|
+
}
|
|
522
|
+
const all: Record<string, any> = {
|
|
523
|
+
...(useDefaultDrivers && {EVENTS: eventBusDriver, DOM: makeDOMDriver(mountPoint as any), LOG: logDriver, __m: () => mountPoint}),
|
|
524
|
+
...drivers,
|
|
525
|
+
}
|
|
526
|
+
// the Cycle run loop, reduced: a proxy sink per driver, the driver's source, then the app's
|
|
527
|
+
// streams imitated (values reach a driver synchronously; the DOM gets one vnode per flush)
|
|
528
|
+
for (const n in all) {
|
|
529
|
+
const p = app.proxies[n] = xs.create()
|
|
530
|
+
const src = app.sources[n] = all[n](p, n)
|
|
531
|
+
if (src && typeof src == 'object') try { src._isCycleSource = n } catch (_) {}
|
|
532
|
+
if (src?._isolateModule) src._isolateModule.rep = patchErr
|
|
533
|
+
}
|
|
534
|
+
scanSources(app)
|
|
535
|
+
app.vdom$ = xs.create({start: (l: any) => { app.vdomL = l; if (app.last) l.next(app.last) }, stop: () => { app.vdomL = null }})
|
|
536
|
+
for (const n in all) {
|
|
537
|
+
if (n == 'DOM') app.proxies.DOM.imitate(app.vdom$)
|
|
538
|
+
// a PARENT driver (sygnal/element) gets the root's PARENT values, as under Cycle's run
|
|
539
|
+
else if (!NOT_SINK.test(n) || n == 'PARENT') app.proxies[n].imitate(app.sink(n))
|
|
540
|
+
}
|
|
541
|
+
const was = app.draining
|
|
542
|
+
app.draining = true
|
|
543
|
+
let link: (() => void) | undefined
|
|
544
|
+
try {
|
|
545
|
+
const setup = (Root as any).persist?.setup
|
|
546
|
+
const def = rootDef(Root, app.hooks.transformDef, opts.__override, typeof setup == 'function' ? (src) => {
|
|
547
|
+
const [shim, l] = rootShim(app, Root, src)
|
|
548
|
+
link = l
|
|
549
|
+
setup(shim)
|
|
550
|
+
return {...src, model: shim.model, initialState: shim.initialState}
|
|
551
|
+
} : undefined)
|
|
552
|
+
app.root = new Inst(app, def, null, rootCell(app), app.sources.DOM, {}, [], undefined,
|
|
553
|
+
opts.uid ? opts.uid.replace(/[^\w-]+/g, '_') : 'u', 'root')
|
|
554
|
+
link?.()
|
|
555
|
+
} catch (e) {
|
|
556
|
+
app.draining = was
|
|
557
|
+
app.dispose()
|
|
558
|
+
throw e
|
|
559
|
+
}
|
|
560
|
+
app.draining = was
|
|
561
|
+
if (app.queue.length) app.drain()
|
|
562
|
+
app.commit()
|
|
563
|
+
let dom$: any
|
|
564
|
+
const sinks: Record<string, any> = {__dispose: () => app.dispose()}
|
|
565
|
+
for (const n of new Set([...Object.keys(all), 'PARENT'])) {
|
|
566
|
+
if (n == 'DOM') Object.defineProperty(sinks, n, {get: () => dom$ ||= app.vdom$.remember(), enumerable: true})
|
|
567
|
+
else if (!NOT_SINK.test(n) || n == 'PARENT') Object.defineProperty(sinks, n, {get: () => app.exposed(n), enumerable: true})
|
|
568
|
+
}
|
|
569
|
+
const sources: Record<string, any> = {...app.sources}
|
|
570
|
+
Object.defineProperty(sources, 'STATE', {get: () => app.root.stateSource(), enumerable: true})
|
|
571
|
+
return {app, sources, sinks, dispose: () => app.dispose(), api: app.api()}
|
|
572
|
+
}
|
|
573
|
+
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLAN-4.6 core: declaration statics and reply actions (04-hooks-contract §2.1 "statics",
|
|
3
|
+
* §3.7; spike 0-S proto/statics.ts). The core names no static: a driver source marked
|
|
4
|
+
* `__sygnalStatic: 'timers'` (makeTimerDriver; 'resources' makeFetchDriver, 'connections'
|
|
5
|
+
* makeSocketDriver, 'route' the router, 'head' makeHeadDriver, a testing fake) takes that static
|
|
6
|
+
* of every component that declares it.
|
|
7
|
+
*
|
|
8
|
+
* - A declaring instance's value of `view[static]` (a function of the calculated state, an object
|
|
9
|
+
* of such functions, or a constant such as a `route` string) is recomputed when its state or its
|
|
10
|
+
* shown flag changed: in the flush (after each render pass, so a new instance and a parent's
|
|
11
|
+
* write are covered) and synchronously after each of its own actions (G-158: before that
|
|
12
|
+
* action's own values on any driver sink; D170/Q21).
|
|
13
|
+
* - Sent as `{ [static]: value }`, without deep-equal repeats (objIsEqual); while the instance is
|
|
14
|
+
* on a hidden Switchable page, an object value keeps only its `background: true` entries.
|
|
15
|
+
* Stamped and scoped like any sink value (actions.ts send()). A throwing declaration is SYG216
|
|
16
|
+
* ('declaration'; nothing sent).
|
|
17
|
+
* - Reply actions: an instance listens to `replies(id)` of each reply-capable source
|
|
18
|
+
* (`__sygnalReplies: true`) it sends to or declares a static to, from its creation; a reply is
|
|
19
|
+
* dispatched to it with cause 'reply'. Disposing completes them at once, so the driver stops the
|
|
20
|
+
* instance's timers / aborts its requests (G-144), after DISPOSE and dispose$ ran.
|
|
21
|
+
*/
|
|
22
|
+
import type {Inst} from './instance'
|
|
23
|
+
import type {App} from './runtime'
|
|
24
|
+
import type {CoreDef} from './define'
|
|
25
|
+
import {objIsEqual} from '../cycle/state/objIsEqual'
|
|
26
|
+
import {send} from './actions'
|
|
27
|
+
import {viewOf} from './view'
|
|
28
|
+
|
|
29
|
+
/** the drivers' statics and reply-capable sources (once per app, at start) */
|
|
30
|
+
export function scanSources(app: App) {
|
|
31
|
+
for (const n in app.sources) {
|
|
32
|
+
const s = app.sources[n]
|
|
33
|
+
if (!s || typeof s != 'object') continue
|
|
34
|
+
if (typeof s.__sygnalStatic == 'string') app.stat.push([n, s.__sygnalStatic])
|
|
35
|
+
if (s.__sygnalReplies === true && typeof s.replies == 'function') app.rep.push(n)
|
|
36
|
+
}
|
|
37
|
+
if (app.stat.length) app.afterRender = () => { for (const i of app.statics) checkStatics(i) }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** the [sink, static] pairs this definition declares (null: none), cached per app */
|
|
41
|
+
export function staticsOf(app: App, def: CoreDef): Array<[string, string]> | null {
|
|
42
|
+
let s = app.stc.get(def)
|
|
43
|
+
if (s === undefined) {
|
|
44
|
+
const v = def.view, l = app.stat.filter(([, k]) => v[k] != null)
|
|
45
|
+
app.stc.set(def, (s = l.length ? l : null))
|
|
46
|
+
}
|
|
47
|
+
return s
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** at creation (before the intent): the statics it declares and the replies it listens to */
|
|
51
|
+
export function attach(inst: Inst) {
|
|
52
|
+
const app = inst.app, def = inst.def
|
|
53
|
+
const st = inst.st = app.stat.length ? staticsOf(app, def) : null
|
|
54
|
+
// (made in a flush's render: that pass's statics step computes it, G-323; else a flush does)
|
|
55
|
+
if (st) { inst.sv = {}; app.statics.add(inst); if (!app.flushing) app.commit() }
|
|
56
|
+
for (const n of app.rep) {
|
|
57
|
+
if (!def.sinks.has(n) && !st?.some(s => s[0] == n)) continue
|
|
58
|
+
const src = inst.src(n), r$ = (typeof src?.replies == 'function' ? src : app.sources[n]).replies(inst.id)
|
|
59
|
+
;(inst.rep ||= []).push(r$)
|
|
60
|
+
r$.subscribe({next: (a: any) => a && app.dispatch(inst, a.type, a.data, 'reply', n)})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** on dispose: no more declarations; the replies end now (the drivers drop the instance) */
|
|
65
|
+
export function detach(inst: Inst) {
|
|
66
|
+
if (inst.st) inst.app.statics.delete(inst)
|
|
67
|
+
const r = inst.rep
|
|
68
|
+
if (r) { inst.rep = null; for (const r$ of r) try { r$.shamefullySendComplete() } catch (_) {} }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const shownOf = (inst: Inst) => {
|
|
72
|
+
for (let i: Inst | null = inst; i; i = i.parent) if (!i.shown) return false
|
|
73
|
+
return true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** recompute the instance's declarations when its state or shown flag changed; send the changed ones */
|
|
77
|
+
export function checkStatics(inst: Inst) {
|
|
78
|
+
if (inst.disposed) return
|
|
79
|
+
const s = inst.cell.get(), shown = shownOf(inst)
|
|
80
|
+
// no state yet: nothing declared (as the state stream, which skips undefined)
|
|
81
|
+
if (s === undefined || (s === inst.sS && shown === inst.sH)) return
|
|
82
|
+
inst.sS = s; inst.sH = shown
|
|
83
|
+
const view = inst.def.view, sv = inst.sv!
|
|
84
|
+
for (const [n, k] of inst.st!) {
|
|
85
|
+
const f = view[k]
|
|
86
|
+
let v: any = f
|
|
87
|
+
try {
|
|
88
|
+
if (typeof f == 'function') v = f(s)
|
|
89
|
+
else if (f && typeof f == 'object') { v = {}; for (const r in f) v[r] = f[r](s) }
|
|
90
|
+
} catch (e) {
|
|
91
|
+
// as today: nothing sent; the next value is sent even when equal to the last one
|
|
92
|
+
inst.app.caught(inst, 'SYG216', `${k} threw; nothing sent`, e, 'declaration')
|
|
93
|
+
sv[n] = undefined
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
// D85: a hidden page keeps only its background entries (an object value)
|
|
97
|
+
if (!shown && v && typeof v == 'object') { const w = v; v = {}; for (const r in w) if (w[r]?.background) v[r] = w[r] }
|
|
98
|
+
const out = {[k]: v}
|
|
99
|
+
if (n in sv && objIsEqual(out, sv[n])) continue
|
|
100
|
+
sv[n] = out
|
|
101
|
+
inst.app.hooks.onSink?.(viewOf(inst), null, n, out)
|
|
102
|
+
send(inst, n, out)
|
|
103
|
+
}
|
|
104
|
+
}
|