sygnal 7.0.0 → 7.2.0-preview.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 +632 -0
- package/dist/agent-ui.cjs.js +592 -0
- package/dist/agent-ui.cjs.js.map +1 -0
- package/dist/agent-ui.esm.js +582 -0
- package/dist/agent-ui.esm.js.map +1 -0
- package/dist/api.cjs.js +63 -0
- package/dist/api.cjs.js.map +1 -0
- package/dist/api.esm.js +2 -0
- package/dist/api.esm.js.map +1 -0
- package/dist/astro/index.cjs.js +968 -4
- package/dist/astro/index.cjs.js.map +1 -1
- package/dist/astro/index.mjs +968 -4
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/server.cjs.js +73 -45
- package/dist/astro/server.cjs.js.map +1 -1
- package/dist/astro/server.mjs +73 -45
- package/dist/astro/server.mjs.map +1 -1
- package/dist/canvas.cjs.js +2919 -0
- package/dist/canvas.cjs.js.map +1 -0
- package/dist/canvas.esm.js +2866 -0
- package/dist/canvas.esm.js.map +1 -0
- package/dist/devtools.cjs.js +38 -6
- package/dist/devtools.cjs.js.map +1 -1
- package/dist/devtools.esm.js +38 -6
- package/dist/devtools.esm.js.map +1 -1
- package/dist/diagnostics.cjs.js +224 -20
- package/dist/diagnostics.cjs.js.map +1 -1
- package/dist/diagnostics.esm.js +224 -20
- package/dist/diagnostics.esm.js.map +1 -1
- package/dist/guide/accessibility.md +1 -1
- package/dist/guide/ai-compose.md +150 -0
- package/dist/guide/behaviors.md +2 -1
- package/dist/guide/collaborative-state.md +265 -0
- package/dist/guide/element-commands.md +36 -2
- package/dist/guide/forms-reference.md +5 -5
- package/dist/guide/forms.md +3 -2
- package/dist/guide/http.md +28 -1
- package/dist/guide/islands.md +240 -0
- package/dist/guide/recipes/code-editor.md +1 -1
- package/dist/guide/renderers/agent-ui.md +138 -0
- package/dist/guide/renderers/custom-targets.md +126 -0
- package/dist/guide/renderers/overview.md +120 -0
- package/dist/guide/renderers/react-native.md +128 -0
- package/dist/guide/renderers/terminal.md +306 -0
- package/dist/guide/renderers/three.md +332 -0
- package/dist/guide/renderers/write-once.md +137 -0
- package/dist/guide/resources.md +2 -0
- package/dist/guide/sandboxed-components.md +223 -0
- package/dist/guide/timers.md +32 -5
- package/dist/guide/typed-apis.md +442 -0
- package/dist/guide/ui/accordion.md +11 -0
- package/dist/guide/ui/combobox.md +112 -8
- package/dist/guide/ui/dialog.md +96 -20
- package/dist/guide/ui/menu.md +57 -5
- package/dist/guide/ui/overview.md +8 -4
- package/dist/guide/ui/popover.md +69 -9
- package/dist/guide/ui/select.md +54 -4
- package/dist/guide/ui/tabs.md +1 -1
- package/dist/guide/ui/tooltip.md +7 -5
- package/dist/guide/virtual-collections.md +3 -0
- package/dist/guide/widgets.md +90 -1
- package/dist/host.cjs.js +2181 -0
- package/dist/host.cjs.js.map +1 -0
- package/dist/host.esm.js +2146 -0
- package/dist/host.esm.js.map +1 -0
- package/dist/index.cjs.js +3948 -586
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +584 -44
- package/dist/index.esm.js +3918 -586
- package/dist/index.esm.js.map +1 -1
- package/dist/primitives.cjs.js +143 -0
- package/dist/primitives.cjs.js.map +1 -0
- package/dist/primitives.esm.js +133 -0
- package/dist/primitives.esm.js.map +1 -0
- package/dist/react-native.cjs.js +248 -0
- package/dist/react-native.cjs.js.map +1 -0
- package/dist/react-native.esm.js +244 -0
- package/dist/react-native.esm.js.map +1 -0
- package/dist/replay.cjs.js +39 -0
- package/dist/replay.cjs.js.map +1 -0
- package/dist/replay.esm.js +2 -0
- package/dist/replay.esm.js.map +1 -0
- package/dist/sandbox-build.cjs.js +140 -0
- package/dist/sandbox-build.cjs.js.map +1 -0
- package/dist/sandbox-build.esm.js +137 -0
- package/dist/sandbox-build.esm.js.map +1 -0
- package/dist/sandbox-kernel.esm.js +212 -0
- package/dist/sandbox-kernel.esm.js.map +1 -0
- package/dist/sandbox-worker.cjs.js +857 -0
- package/dist/sandbox-worker.cjs.js.map +1 -0
- package/dist/sandbox-worker.esm.js +854 -0
- package/dist/sandbox-worker.esm.js.map +1 -0
- package/dist/sandbox.cjs.js +1425 -0
- package/dist/sandbox.cjs.js.map +1 -0
- package/dist/sandbox.esm.js +1406 -0
- package/dist/sandbox.esm.js.map +1 -0
- package/dist/sygnal.min.js +1 -1
- package/dist/sygnal.min.js.map +1 -1
- package/dist/terminal.cjs.js +1314 -0
- package/dist/terminal.cjs.js.map +1 -0
- package/dist/terminal.esm.js +1304 -0
- package/dist/terminal.esm.js.map +1 -0
- package/dist/three.cjs.js +1111 -0
- package/dist/three.cjs.js.map +1 -0
- package/dist/three.esm.js +1079 -0
- package/dist/three.esm.js.map +1 -0
- package/dist/ui-combobox.cjs.js +256 -18
- package/dist/ui-combobox.cjs.js.map +1 -1
- package/dist/ui-combobox.esm.js +256 -18
- package/dist/ui-combobox.esm.js.map +1 -1
- package/dist/ui-menu.cjs.js +150 -16
- package/dist/ui-menu.cjs.js.map +1 -1
- package/dist/ui-menu.esm.js +151 -17
- package/dist/ui-menu.esm.js.map +1 -1
- package/dist/ui-select.cjs.js +95 -9
- package/dist/ui-select.cjs.js.map +1 -1
- package/dist/ui-select.esm.js +95 -9
- package/dist/ui-select.esm.js.map +1 -1
- package/dist/ui.cjs.js +679 -173
- package/dist/ui.cjs.js.map +1 -1
- package/dist/ui.esm.js +679 -175
- package/dist/ui.esm.js.map +1 -1
- package/dist/vike/config/package.json +1 -1
- package/dist/vite/plugin.cjs.js +968 -4
- package/dist/vite/plugin.cjs.js.map +1 -1
- package/dist/vite/plugin.mjs +968 -4
- package/dist/vite/plugin.mjs.map +1 -1
- package/dist/worker-page.cjs.js +669 -0
- package/dist/worker-page.cjs.js.map +1 -0
- package/dist/worker-page.esm.js +665 -0
- package/dist/worker-page.esm.js.map +1 -0
- package/dist/worker.cjs.js +377 -0
- package/dist/worker.cjs.js.map +1 -0
- package/dist/worker.esm.js +374 -0
- package/dist/worker.esm.js.map +1 -0
- package/dist/yjs-testing.cjs.js +98 -0
- package/dist/yjs-testing.cjs.js.map +1 -0
- package/dist/yjs-testing.esm.js +77 -0
- package/dist/yjs-testing.esm.js.map +1 -0
- package/dist/yjs.cjs.js +923 -0
- package/dist/yjs.cjs.js.map +1 -0
- package/dist/yjs.esm.js +900 -0
- package/dist/yjs.esm.js.map +1 -0
- package/dist/zag.cjs.js +11 -1
- package/dist/zag.cjs.js.map +1 -1
- package/dist/zag.esm.js +11 -1
- package/dist/zag.esm.js.map +1 -1
- package/llms/ai.md +65 -0
- package/llms/collaboration.md +5 -0
- package/llms/graphics.md +10 -0
- package/llms/renderers.md +7 -0
- package/llms/replay.md +5 -0
- package/llms/sandbox.md +5 -0
- package/llms/typed-apis.md +5 -0
- package/llms/ui.md +10 -0
- package/llms-full.txt +444 -0
- package/llms.txt +37 -35
- package/package.json +139 -3
- package/src/agent-ui/mcp.ts +152 -0
- package/src/agent-ui/snapshot.ts +212 -0
- package/src/agent-ui.d.ts +106 -0
- package/src/agent-ui.ts +249 -0
- package/src/ai.d.ts +95 -2
- package/src/api.d.ts +303 -0
- package/src/api.ts +9 -0
- package/src/canvas/Canvas.ts +585 -0
- package/src/canvas/backend.ts +627 -0
- package/src/canvas/geometry.ts +187 -0
- package/src/canvas/gesture.ts +23 -0
- package/src/canvas/image.ts +197 -0
- package/src/canvas/kit.ts +130 -0
- package/src/canvas/paint.ts +260 -0
- package/src/canvas/pointer.ts +25 -0
- package/src/canvas/snap.ts +68 -0
- package/src/canvas/svg.ts +99 -0
- package/src/canvas.d.ts +373 -0
- package/src/canvas.ts +46 -0
- package/src/core/actions.ts +14 -3
- package/src/core/cell.ts +12 -4
- package/src/core/debug.ts +8 -3
- package/src/core/hooks.ts +16 -2
- package/src/core/hosts/collection.ts +1 -1
- package/src/core/hosts/switchable.ts +26 -8
- package/src/core/instance.ts +98 -45
- package/src/core/markers/portal.ts +4 -0
- package/src/core/registry.ts +4 -0
- package/src/core/runtime.ts +34 -25
- package/src/core/statics.ts +5 -5
- package/src/cycle/dom/EventDelegator.ts +37 -33
- package/src/cycle/dom/MainDOMSource.ts +16 -14
- package/src/cycle/dom/enrichEventStream.ts +13 -9
- package/src/cycle/dom/fromEvent.ts +6 -10
- package/src/cycle/dom/index.ts +11 -7
- package/src/cycle/dom/isolate.ts +13 -6
- package/src/cycle/dom/makeDOMDriver.ts +6 -2
- package/src/cycle/dom/utils.ts +9 -2
- package/src/cycle/run/adapt.ts +4 -12
- package/src/devtools.d.ts +9 -0
- package/src/devtools.ts +2 -1
- package/src/extra/ai/compose/catalog.ts +2 -1
- package/src/extra/ai/compose/decisions.ts +1 -2
- package/src/extra/ai/compose/generated.ts +234 -0
- package/src/extra/ai/compose/index.ts +6 -2
- package/src/extra/ai/compose/layout.ts +3 -1
- package/src/extra/ai/compose/llm.ts +7 -3
- package/src/extra/ai/compose/types.ts +2 -2
- package/src/extra/ai/transports/shared.ts +3 -1
- package/src/extra/api/contract.ts +485 -0
- package/src/extra/api/index.ts +26 -0
- package/src/extra/behaviors.ts +3 -3
- package/src/extra/controlled.ts +44 -0
- package/src/extra/copyAsTest.ts +22 -0
- package/src/extra/devtools.ts +5 -3
- package/src/extra/devtoolsActions.ts +8 -0
- package/src/extra/devtoolsNext.ts +1 -1
- package/src/extra/diagnostics/checks/actionLog.ts +8 -3
- package/src/extra/diagnostics/checks/behaviors.ts +25 -0
- package/src/extra/diagnostics/checks/elementCommands.ts +17 -5
- package/src/extra/diagnostics/checks/index.ts +1 -1
- package/src/extra/diagnostics/checks/next.ts +78 -4
- package/src/extra/diagnostics/checks/props.ts +6 -1
- package/src/extra/diagnostics/checks/replies.ts +11 -7
- package/src/extra/diagnostics/checks/timers.ts +8 -2
- package/src/extra/diagnostics/checks/virtual.ts +11 -0
- package/src/extra/diagnostics/codes.ts +51 -1
- package/src/extra/elementCommands.ts +20 -4
- package/src/extra/fetchDriver.ts +4 -1
- package/src/extra/fetchEventSource.ts +6 -2
- package/src/extra/field.ts +29 -0
- package/src/extra/form.ts +42 -16
- package/src/extra/frame.ts +54 -0
- package/src/extra/keymap.ts +41 -0
- package/src/extra/pager.ts +2 -0
- package/src/extra/persist.ts +2 -1
- package/src/extra/pointer.ts +262 -0
- package/src/extra/processForm.ts +34 -1
- package/src/extra/replay/address.ts +49 -0
- package/src/extra/replay/entropy.ts +99 -0
- package/src/extra/replay/format.ts +263 -0
- package/src/extra/replay/index.ts +8 -0
- package/src/extra/replay/inert.ts +22 -0
- package/src/extra/replay/recorder.ts +247 -0
- package/src/extra/replay/replayer.ts +234 -0
- package/src/extra/replay/report.ts +126 -0
- package/src/extra/replay/schema.ts +52 -0
- package/src/extra/replay/version.ts +3 -0
- package/src/extra/router.ts +7 -3
- package/src/extra/selection.ts +2 -0
- package/src/extra/socketDriver.ts +51 -6
- package/src/extra/ssr.ts +128 -42
- package/src/extra/testing.ts +295 -39
- package/src/extra/timers.ts +96 -7
- package/src/extra/undo.ts +2 -0
- package/src/extra/virtual.ts +25 -2
- package/src/extra/widget.ts +106 -17
- package/src/host/actions.ts +383 -0
- package/src/host/delegation.ts +101 -0
- package/src/host/dom.ts +553 -0
- package/src/host/driver.ts +185 -0
- package/src/host/selector.ts +119 -0
- package/src/host/style.ts +51 -0
- package/src/host.d.ts +298 -0
- package/src/host.ts +46 -0
- package/src/index.d.ts +302 -38
- package/src/index.ts +15 -3
- package/src/pragma/blocks.ts +211 -0
- package/src/pragma/index.ts +44 -10
- package/src/primitives.d.ts +54 -0
- package/src/primitives.ts +136 -0
- package/src/react-native.d.ts +57 -0
- package/src/react-native.ts +285 -0
- package/src/react-peers.d.ts +21 -3
- package/src/replay.d.ts +185 -0
- package/src/replay.ts +6 -0
- package/src/sandbox/build.ts +119 -0
- package/src/sandbox/create.ts +187 -0
- package/src/sandbox/engine.ts +267 -0
- package/src/sandbox/generated.ts +195 -0
- package/src/sandbox/kernel.ts +180 -0
- package/src/sandbox/mirror.ts +269 -0
- package/src/sandbox/protocol.ts +128 -0
- package/src/sandbox/quickjs.ts +74 -0
- package/src/sandbox/ssr.ts +41 -0
- package/src/sandbox/widget.ts +60 -0
- package/src/sandbox/worker-entry.ts +59 -0
- package/src/sandbox/worker.ts +137 -0
- package/src/sandbox-build.d.ts +12 -0
- package/src/sandbox-build.ts +6 -0
- package/src/sandbox.d.ts +299 -0
- package/src/sandbox.ts +25 -0
- package/src/shared.ts +12 -0
- package/src/terminal/paint.ts +712 -0
- package/src/terminal.d.ts +167 -0
- package/src/terminal.ts +530 -0
- package/src/three/assets.ts +170 -0
- package/src/three/host.ts +36 -0
- package/src/three/scene.ts +217 -0
- package/src/three/target.ts +252 -0
- package/src/three.d.ts +221 -0
- package/src/three.ts +30 -0
- package/src/ui/accordion.ts +21 -7
- package/src/ui/dialog.ts +192 -90
- package/src/ui/disclosure.ts +2 -0
- package/src/ui/dom.ts +30 -0
- package/src/ui/layers.ts +132 -0
- package/src/ui/popover.ts +120 -21
- package/src/ui/shared.ts +98 -12
- package/src/ui/stack.ts +18 -0
- package/src/ui/tabs.ts +2 -0
- package/src/ui/toaster.ts +16 -15
- package/src/ui/tooltip.ts +24 -8
- package/src/ui/tooltips.ts +26 -14
- package/src/ui/zag/combobox.ts +180 -13
- package/src/ui/zag/menu.ts +97 -17
- package/src/ui/zag/select.ts +15 -4
- package/src/ui/zag/shared.ts +60 -8
- package/src/ui-combobox.d.ts +16 -1
- package/src/ui-menu.d.ts +22 -7
- package/src/ui-zag-types.d.ts +13 -5
- package/src/ui.d.ts +62 -7
- package/src/ui.ts +2 -2
- package/src/vite/api.ts +186 -0
- package/src/vite/compile.ts +280 -0
- package/src/vite/plugin.d.ts +34 -0
- package/src/vite/plugin.ts +78 -4
- package/src/vite/sandbox.ts +16 -0
- package/src/worker/page.ts +396 -0
- package/src/worker/protocol.ts +48 -0
- package/src/worker/target.ts +282 -0
- package/src/worker-page.d.ts +42 -0
- package/src/worker-page.ts +12 -0
- package/src/worker.d.ts +65 -0
- package/src/worker.ts +16 -0
- package/src/yjs/driver.ts +253 -0
- package/src/yjs/mapping.ts +389 -0
- package/src/yjs/shared.ts +138 -0
- package/src/yjs/testing.ts +91 -0
- package/src/yjs-testing.d.ts +34 -0
- package/src/yjs-testing.ts +7 -0
- package/src/yjs.d.ts +109 -0
- package/src/yjs.ts +18 -0
- package/src/zag.d.ts +2 -0
- package/src/zag.ts +8 -1
package/dist/diagnostics.cjs.js
CHANGED
|
@@ -137,6 +137,10 @@ const CODE_TITLES = {
|
|
|
137
137
|
SYG111: 'Controlled input has no input listener',
|
|
138
138
|
SYG115: 'Unknown DOM event shorthand',
|
|
139
139
|
SYG116: 'EVENTS value has no string type',
|
|
140
|
+
// PLAN-13 N-1: dev entry (checks/next.ts onNext) and sygnal-check
|
|
141
|
+
SYG117: 'next() names an action with no model entry',
|
|
142
|
+
// ui-sweep R1: dev entry (checks/behaviors.ts onIntent) and sygnal-check
|
|
143
|
+
SYG118: 'Host intent replaces a behavior trigger',
|
|
140
144
|
SYG124: 'Component used as a control or selector',
|
|
141
145
|
SYG125: 'Control given .intent, .model or .initialState',
|
|
142
146
|
SYG126: 'Control rendered but never listened to',
|
|
@@ -172,11 +176,15 @@ const CODE_TITLES = {
|
|
|
172
176
|
// PLAN-5 V-1: VirtualCollection (checks/virtual.ts)
|
|
173
177
|
SYG430: 'VirtualCollection has no bounded height',
|
|
174
178
|
SYG431: 'VirtualCollection items without ids',
|
|
179
|
+
// PLAN-13 F-3: dev entry (checks/next.ts onRender) and sygnal-check
|
|
180
|
+
SYG429: 'Switchable state map names no page',
|
|
175
181
|
SYG432: 'VirtualCollection item does not render one element',
|
|
176
182
|
SYG433: 'VirtualCollection scroll target not in the list',
|
|
177
183
|
SYG434: 'Invalid VirtualCollection estimateSize or overscan',
|
|
178
184
|
// PLAN-5 3-H (G-449): sortable over a list shown in another order (checks/sortable.ts)
|
|
179
185
|
SYG435: 'Sortable list shown filtered or in another order',
|
|
186
|
+
// Temper feedback 2026-10: VirtualCollection (checks/virtual.ts, through the core bridge)
|
|
187
|
+
SYG436: 'VirtualCollection estimateSize far from the measured row heights',
|
|
180
188
|
// PLAN-6 A-1 (sygnal/ai agent layer)
|
|
181
189
|
SYG440: 'Two agent declarations with the same name',
|
|
182
190
|
SYG441: 'Agent Collection items without unique ids',
|
|
@@ -185,8 +193,14 @@ const CODE_TITLES = {
|
|
|
185
193
|
// SYG2xx state & reducers (1A)
|
|
186
194
|
SYG201: 'STATE reducer dropped keys from the previous state',
|
|
187
195
|
SYG202: 'STATE reducer returned undefined',
|
|
196
|
+
// sandbox research §6 (dev entry, checks/next.ts)
|
|
197
|
+
SYG204: 'Root component has no state, so its view never renders',
|
|
188
198
|
// SYG3xx streams & intent construction (1A)
|
|
189
199
|
SYG301: 'RxJS operator used on an xstream stream',
|
|
200
|
+
// PLAN-13 X-1: static only (sygnal-check)
|
|
201
|
+
SYG302: "Default import from 'xstream' or 'xstream/extra/*'",
|
|
202
|
+
// Temper feedback 2026-10: static only (sygnal-check; warn under --strict)
|
|
203
|
+
SYG303: 'STATE.stream with dropRepeats() in an intent',
|
|
190
204
|
// SYG4xx collections / switchable / sub-components / context (1A)
|
|
191
205
|
SYG401: "Collection 'from' field is missing or not an array",
|
|
192
206
|
// SYG5xx strict mode / canonical forms (2A; dev-plans/PLAN-1-canonical-forms.md)
|
|
@@ -198,6 +212,7 @@ const CODE_TITLES = {
|
|
|
198
212
|
SYG506: "Removed in 6.0: CHILD.select() with a component name",
|
|
199
213
|
SYG507: 'Prop drilled through more than 2 component levels',
|
|
200
214
|
SYG508: 'select()/errors() round trip where reply actions would do',
|
|
215
|
+
SYG509: 'Clock or randomness read from a global in a handler',
|
|
201
216
|
// SYG9xx internal (0B)
|
|
202
217
|
SYG900: 'A diagnostics check threw',
|
|
203
218
|
// ---- 1E retrofit codes (SYG206-299, 402-499, 601-699, 901-999) ----
|
|
@@ -364,12 +379,23 @@ const CODE_TITLES = {
|
|
|
364
379
|
SYG254: 'Catalog descriptions or options overlap',
|
|
365
380
|
SYG256: 'Output guard rejects the updated state',
|
|
366
381
|
SYG257: 'Output reads a field its action changed (7.0 timing audit)',
|
|
382
|
+
// record/replay: static only (sygnal-check rules/syg258-clock-in-reducer.js; SYG509 under --strict)
|
|
383
|
+
SYG258: 'Clock or randomness read from a global in a STATE reducer',
|
|
384
|
+
// sygnal/yjs (research: dev-plans/research/crdt-state): shared() reports them (dev), SYG260 statically too
|
|
385
|
+
SYG260: 'undo() or undoable() on a key shared() syncs',
|
|
386
|
+
SYG261: 'shared() inferred the kind of an empty array',
|
|
387
|
+
// sygnal/canvas (research: dev-plans/research/canvas-apps): the Canvas reports it (dev)
|
|
388
|
+
SYG262: 'Canvas data layer rebuilt with equal nodes',
|
|
367
389
|
SYG454: 'State binding changed without a new component identity',
|
|
368
390
|
// PLAN-9 H: chat client tools and ask (src/extra/ai/chat/behavior.ts, compose/ask.ts, compose/render.ts)
|
|
369
391
|
SYG160: 'Result part rendered outside a card',
|
|
370
392
|
SYG684: 'Client tool shadows an agent tool',
|
|
371
393
|
SYG685: 'Answer for a tool call that is not open',
|
|
372
394
|
SYG686: 'Ask catalog without a result part',
|
|
395
|
+
// sygnal/api (dev preview)
|
|
396
|
+
SYG690: 'API contract skew',
|
|
397
|
+
SYG691: 'Unknown API endpoint',
|
|
398
|
+
SYG692: 'Sandbox event dropped',
|
|
373
399
|
// PLAN-4 GS-3: a11y lane (static only, sygnal-check)
|
|
374
400
|
SYG701: 'Click listener on a non-interactive element',
|
|
375
401
|
SYG702: 'Form field without an accessible label',
|
|
@@ -404,6 +430,7 @@ const STRICT_CODE_SEVERITY = {
|
|
|
404
430
|
SYG506: 'error',
|
|
405
431
|
SYG507: 'info',
|
|
406
432
|
SYG508: 'warn',
|
|
433
|
+
SYG509: 'warn',
|
|
407
434
|
};
|
|
408
435
|
// PLAN-3 1-G (G-143): codes only the 'sygnal/diagnostics' dev entry reports. Like the strict
|
|
409
436
|
// table, kept OUT of CODE_SEVERITY (zero bytes in the main bundle); the entry registers them
|
|
@@ -415,6 +442,10 @@ const DEV_CODE_SEVERITY = {
|
|
|
415
442
|
SYG112: 'error',
|
|
416
443
|
SYG115: 'warn',
|
|
417
444
|
SYG116: 'error',
|
|
445
|
+
SYG117: 'warn',
|
|
446
|
+
SYG118: 'warn',
|
|
447
|
+
// sandbox research §6: a root that has no state after it starts (checks/next.ts)
|
|
448
|
+
SYG204: 'warn',
|
|
418
449
|
// PLAN-4 1-D: controls, static (sygnal-check)
|
|
419
450
|
SYG124: 'error',
|
|
420
451
|
SYG125: 'error',
|
|
@@ -426,6 +457,12 @@ const DEV_CODE_SEVERITY = {
|
|
|
426
457
|
SYG129: 'warn',
|
|
427
458
|
// PLAN-10 X-1: static only (sygnal-check)
|
|
428
459
|
SYG170: 'warn',
|
|
460
|
+
// PLAN-13 X-1: static only (sygnal-check)
|
|
461
|
+
SYG302: 'info',
|
|
462
|
+
// Temper feedback 2026-10: static only (sygnal-check); its strict copy reports it at warn
|
|
463
|
+
SYG303: 'info',
|
|
464
|
+
// PLAN-13 F-3 (checks/next.ts)
|
|
465
|
+
SYG429: 'warn',
|
|
429
466
|
SYG171: 'warn',
|
|
430
467
|
SYG221: 'error',
|
|
431
468
|
// PLAN-4 GS-4 (state.ts)
|
|
@@ -549,6 +586,11 @@ const DEV_CODE_SEVERITY = {
|
|
|
549
586
|
SYG684: 'warn',
|
|
550
587
|
SYG685: 'warn',
|
|
551
588
|
SYG686: 'warn',
|
|
589
|
+
// sygnal/api: contractFetch() (SYG690); static only: sygnal-check (SYG691)
|
|
590
|
+
SYG690: 'warn',
|
|
591
|
+
SYG691: 'warn',
|
|
592
|
+
// sygnal/sandbox: an EVENTS type not declared in emits, or data its schema rejects (createSandbox, <Sandboxed>)
|
|
593
|
+
SYG692: 'warn',
|
|
552
594
|
// PLAN-5 V-1: VirtualCollection (checks/virtual.ts, through the core bridge)
|
|
553
595
|
SYG430: 'warn',
|
|
554
596
|
SYG431: 'warn',
|
|
@@ -557,6 +599,8 @@ const DEV_CODE_SEVERITY = {
|
|
|
557
599
|
SYG434: 'warn',
|
|
558
600
|
// PLAN-5 3-H (G-449): sortable (checks/sortable.ts)
|
|
559
601
|
SYG435: 'warn',
|
|
602
|
+
// Temper feedback 2026-10: VirtualCollection (checks/virtual.ts)
|
|
603
|
+
SYG436: 'warn',
|
|
560
604
|
// PLAN-6 A-1 (sygnal/ai agent layer)
|
|
561
605
|
SYG440: 'warn',
|
|
562
606
|
SYG441: 'warn',
|
|
@@ -597,6 +641,12 @@ const DEV_CODE_SEVERITY = {
|
|
|
597
641
|
SYG256: 'warn',
|
|
598
642
|
// 7.0: the opt-in timing audit (checks/next.ts; configureTimingAudit)
|
|
599
643
|
SYG257: 'info',
|
|
644
|
+
// record/replay: static only (sygnal-check); listed so the entry knows its title
|
|
645
|
+
SYG258: 'warn',
|
|
646
|
+
// sygnal/yjs: reported with the severity passed (shared() in dev; sygnal-check: SYG260)
|
|
647
|
+
SYG260: 'warn',
|
|
648
|
+
SYG261: 'warn',
|
|
649
|
+
SYG262: 'warn',
|
|
600
650
|
// PLAN-12: static only for legacy (6.x) binding contracts. Sygnal 7 remounts known targets.
|
|
601
651
|
SYG454: 'warn',
|
|
602
652
|
};
|
|
@@ -1147,7 +1197,9 @@ const propsCheck = {
|
|
|
1147
1197
|
data: { prop: key },
|
|
1148
1198
|
});
|
|
1149
1199
|
}
|
|
1150
|
-
|
|
1200
|
+
// PLAN-13 F-3 follow-up: a Switchable page gets the Switchable's props, whose `state` is the
|
|
1201
|
+
// host's binding (a per-page map too), consumed by the host: not a prop meant for the page
|
|
1202
|
+
if ('state' in props && component.__next?.kind !== 'page') {
|
|
1151
1203
|
const value = props.state;
|
|
1152
1204
|
const isLens = typeof value === 'string' || (value && typeof value === 'object' && typeof value.get === 'function');
|
|
1153
1205
|
if (!isLens && value !== undefined && once(`SYG106:${name}:state`)) {
|
|
@@ -1774,8 +1826,21 @@ const notACommand = (component, name, cmd) => send('SYG641', {
|
|
|
1774
1826
|
fix: `Send { <method>: control or selector, ...options }, e.g. { focus: Email }, or an array of them`,
|
|
1775
1827
|
data: { command: cmd },
|
|
1776
1828
|
});
|
|
1777
|
-
/**
|
|
1778
|
-
|
|
1829
|
+
/** does the page have an element matching `target` (a selector or a control) anywhere? */
|
|
1830
|
+
const onPage = (target) => {
|
|
1831
|
+
try {
|
|
1832
|
+
return !!globalThis.document?.querySelector(String(target));
|
|
1833
|
+
}
|
|
1834
|
+
catch (_) {
|
|
1835
|
+
return false;
|
|
1836
|
+
}
|
|
1837
|
+
};
|
|
1838
|
+
/**
|
|
1839
|
+
* SYG640 (no `el`) or SYG641 (an element, but no command of that name) for `cmd` sent by
|
|
1840
|
+
* `component`. `elsewhere`: the target matches outside the sender's own view (renderComponent
|
|
1841
|
+
* passes it from the rendered tree; otherwise the page is asked)
|
|
1842
|
+
*/
|
|
1843
|
+
function reportElementCommand(component, cmd, el, elsewhere) {
|
|
1779
1844
|
// a disposed instance's pending commands are dropped silently
|
|
1780
1845
|
if (component && component._disposed)
|
|
1781
1846
|
return;
|
|
@@ -1789,15 +1854,18 @@ function reportElementCommand(component, cmd, el) {
|
|
|
1789
1854
|
const data = { method, command: cmd, ...(control ? { control } : { selector: target == null ? target : String(target) }) };
|
|
1790
1855
|
if (!el) {
|
|
1791
1856
|
const missing = target == null || target === '';
|
|
1857
|
+
// Temper 2026-10: say when the element is on the page, only not in the sender's view
|
|
1858
|
+
const outside = !missing && (elsewhere ?? onPage(target));
|
|
1792
1859
|
return send('SYG640', {
|
|
1793
1860
|
component,
|
|
1794
1861
|
message: missing
|
|
1795
1862
|
? `${what} in ${name} has no target (${String(target)}), so the command was dropped`
|
|
1796
|
-
: `${what} in ${name} matched no element: nothing in ${name}'s own view matches ${control ? `the control ${control}` : `'${String(target)}'`} (waited 1 s), so the command was dropped
|
|
1863
|
+
: `${what} in ${name} matched no element: nothing in ${name}'s own view matches ${control ? `the control ${control}` : `'${String(target)}'`} (waited 1 s), so the command was dropped` +
|
|
1864
|
+
(outside ? `. A matching element is rendered outside ${name}'s view (by an ancestor, a child component or another part of the page), and a command reaches only its sender's own elements` : ''),
|
|
1797
1865
|
fix: missing
|
|
1798
1866
|
? `Give the method a control or a selector: { ${method}: Email }`
|
|
1799
|
-
: `Render the element in ${name}'s view, or send the command from the component that renders it: a child component's elements are isolated from its parent, and a command reaches only its sender's own elements (in a Collection, each item sends its own)`,
|
|
1800
|
-
data,
|
|
1867
|
+
: `Render the element in ${name}'s view, or send the command from the component that renders it: a child component's elements are isolated from its parent, and a command reaches only its sender's own elements (in a Collection, each item sends its own). For an element an ancestor renders, hand it off: emit EVENTS: event('FOCUS_NAME') from ${name} (or send PARENT) and send ELEMENT: { ${method}: … } from the ancestor's entry for it; for one inside a child, the parent can use { ${method}: focusWithin('…') }`,
|
|
1868
|
+
data: outside ? { ...data, outside: true } : data,
|
|
1801
1869
|
});
|
|
1802
1870
|
}
|
|
1803
1871
|
// PLAN-5 W-1: the matched element is a widget's host (selector or control target): SYG142 names
|
|
@@ -1995,6 +2063,8 @@ function installWidgetHooks() {
|
|
|
1995
2063
|
* 432 an item renders a fragment or text, not one element (once per host)
|
|
1996
2064
|
* 433 scrollToIndex / scrollToId for an index or id not in the (filtered) list
|
|
1997
2065
|
* 434 estimateSize / overscan not a positive number (or a function, for estimateSize)
|
|
2066
|
+
* 436 a numeric estimateSize far from the measured row heights (the median of the first 10
|
|
2067
|
+
* measurements is off by more than 1.3×; once per host)
|
|
1998
2068
|
*/
|
|
1999
2069
|
const tag = (owner) => `<VirtualCollection> in ${nameOf(owner)}`;
|
|
2000
2070
|
function reportVirtual(code, owner, x = {}) {
|
|
@@ -2045,6 +2115,15 @@ function reportVirtual(code, owner, x = {}) {
|
|
|
2045
2115
|
data: x,
|
|
2046
2116
|
});
|
|
2047
2117
|
}
|
|
2118
|
+
if (code == 436) {
|
|
2119
|
+
const low = x.measured > x.estimate;
|
|
2120
|
+
return devReport('SYG436', {
|
|
2121
|
+
component,
|
|
2122
|
+
message: `${tag(owner)}: estimateSize={${x.estimate}}, but its rows measure about ${x.measured} px (the median of the first 10 measured)${low ? ', so the rows not rendered yet count as too short: a fast scroll to the end shows blank space until they are measured, and the scrollbar jumps' : ', so the scrollbar shrinks and jumps as rows are measured'}`,
|
|
2123
|
+
fix: `Pass estimateSize={${x.measured}} (the rows' real height; when unsure, err high), or (item, index) => px when rows differ predictably`,
|
|
2124
|
+
data: x,
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2048
2127
|
}
|
|
2049
2128
|
/** Publish the VirtualCollection hook on the core bridge. Returns an uninstall function. */
|
|
2050
2129
|
function installVirtualHooks() {
|
|
@@ -2062,6 +2141,30 @@ const what$1 = (v) => v === null || v === undefined ? String(v)
|
|
|
2062
2141
|
: `a ${typeof v}`;
|
|
2063
2142
|
const behaviorsCheck = {
|
|
2064
2143
|
id: 'behaviors',
|
|
2144
|
+
// SYG118 (ui-sweep R1): a host intent action named like a behavior action replaces the trigger
|
|
2145
|
+
// the behavior built from an option the host passed (`triggers: { ACTION: 'option' }` on the
|
|
2146
|
+
// definition). The intent combine (extra/behaviors.ts) removes a host-replaced action from
|
|
2147
|
+
// `_behaviorActions`, so after the intent ran that map tells which ones the host took
|
|
2148
|
+
onIntent(component, names) {
|
|
2149
|
+
const view = component?.view, uses = view?.uses;
|
|
2150
|
+
if (!uses || typeof uses != 'object' || !once('SYG118:' + nameOf(component)))
|
|
2151
|
+
return;
|
|
2152
|
+
const owned = component._behaviorActions || {};
|
|
2153
|
+
for (const key of Object.keys(uses)) {
|
|
2154
|
+
const b = uses[key], tr = b?.triggers;
|
|
2155
|
+
for (const a in tr) {
|
|
2156
|
+
const opt = tr[a], name = key + '.' + a;
|
|
2157
|
+
if (b.options?.[opt] == null || !names.includes(name) || name in owned)
|
|
2158
|
+
continue;
|
|
2159
|
+
devReport('SYG118', {
|
|
2160
|
+
component,
|
|
2161
|
+
message: `${nameOf(component)}'s intent action '${name}' replaces the trigger the behavior builds from its '${opt}' option, so the elements '${opt}' names no longer dispatch '${name}'`,
|
|
2162
|
+
fix: `List every element in the option instead (${opt}: '${String(b.options[opt])}, .other'), dispatch it from another action with next('${name}'), or leave the '${opt}' option out to replace the trigger on purpose`,
|
|
2163
|
+
data: { key, action: a, option: opt },
|
|
2164
|
+
});
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
},
|
|
2065
2168
|
onModel(component) {
|
|
2066
2169
|
const view = component?.view, uses = view?.uses;
|
|
2067
2170
|
if (!uses || typeof uses != 'object' || !once('SYG127:' + nameOf(component)))
|
|
@@ -2506,7 +2609,7 @@ function readingSource(component, sink, source) {
|
|
|
2506
2609
|
}
|
|
2507
2610
|
return wrapped;
|
|
2508
2611
|
}
|
|
2509
|
-
function checkRequest(component, sink, req, modelActions) {
|
|
2612
|
+
function checkRequest(component, sink, req, modelActions, handledAbove) {
|
|
2510
2613
|
if (!req || typeof req !== 'object')
|
|
2511
2614
|
return;
|
|
2512
2615
|
const name = nameOf(component);
|
|
@@ -2519,17 +2622,21 @@ function checkRequest(component, sink, req, modelActions) {
|
|
|
2519
2622
|
seen.add(action);
|
|
2520
2623
|
if (modelActions.includes(action) || !once(`SYG112:${name}:${action}`))
|
|
2521
2624
|
continue;
|
|
2522
|
-
|
|
2625
|
+
// Temper 2026-10: a reply goes only to its sender; an ancestor's entry of that name never runs
|
|
2626
|
+
const up = handledAbove?.(action);
|
|
2627
|
+
const near = up ? undefined : suggest(action, modelActions.filter(a => !a.startsWith('__')));
|
|
2523
2628
|
devReport('SYG112', {
|
|
2524
2629
|
component,
|
|
2525
2630
|
message: (connection
|
|
2526
2631
|
? `${sink} connection '${connection}' names '${action}' as its ${key} reply action, but ${name} has no model entry '${action}', so those events are dropped`
|
|
2527
2632
|
: `A ${sink} request names '${action}' as its ${key} reply action, but ${name} has no model entry '${action}', so the reply is dropped`) +
|
|
2528
|
-
(near ? ` (did you mean '${near}'?)` : ''),
|
|
2529
|
-
fix:
|
|
2530
|
-
? `
|
|
2531
|
-
:
|
|
2532
|
-
|
|
2633
|
+
(up ? `: replies go only to the component that sent the request, never to ${up}, which handles '${action}'` : near ? ` (did you mean '${near}'?)` : ''),
|
|
2634
|
+
fix: up
|
|
2635
|
+
? `Let ${up} send it: emit EVENTS: event('...') from ${name} and send the request from ${up}'s entry for it, so the reply comes back to ${up}. For data several components load, declare it once as a resource in ${up} (resources: { name: (state) => ({ url, tags: ['name'] }) }) and re-request with ${sink}: { invalidate: 'name' } from anywhere. Or handle the reply here: add '${action}' to ${name}.model`
|
|
2636
|
+
: near
|
|
2637
|
+
? `Use the existing entry: ${key.startsWith('events.') ? `events: { '${key.slice(7)}': '${near}' }` : `${key}: '${near}'`}`
|
|
2638
|
+
: `Add '${action}' to ${name}.model, e.g. ${action}: (state, data) => ({ ...state, ... }), or fix the name`,
|
|
2639
|
+
data: { action, key, sink, ...(connection ? { connection } : {}), ...(up ? { handledBy: up } : {}), ...(near ? { suggestion: near } : {}), modelActions },
|
|
2533
2640
|
});
|
|
2534
2641
|
}
|
|
2535
2642
|
// SYG508 (strict): a request without reply actions whose replies this instance reads back with select()/errors()
|
|
@@ -2808,8 +2915,15 @@ catch (_) {
|
|
|
2808
2915
|
function timerSpecProblem(s) {
|
|
2809
2916
|
if (!s || typeof s != 'object')
|
|
2810
2917
|
return 'is not an object ({ every, action }, { after, action } or { frame: action })';
|
|
2811
|
-
if ('frame' in s && s.frame)
|
|
2812
|
-
|
|
2918
|
+
if ('frame' in s && s.frame) {
|
|
2919
|
+
if (typeof s.frame != 'string')
|
|
2920
|
+
return 'frame must be the action name (a string)';
|
|
2921
|
+
if (s.step != null && !(s.step > 0))
|
|
2922
|
+
return `step must be a positive number of ms (got ${brief$2(s.step)})`;
|
|
2923
|
+
if (s.keys != null && typeof s.keys != 'object')
|
|
2924
|
+
return `keys must be an object of button: key codes (got ${brief$2(s.keys)})`;
|
|
2925
|
+
return '';
|
|
2926
|
+
}
|
|
2813
2927
|
if (!s.action || typeof s.action != 'string')
|
|
2814
2928
|
return 'has no action (a string)';
|
|
2815
2929
|
if (s.every == null && s.after == null)
|
|
@@ -3282,6 +3396,9 @@ function installRxjsHints(proto) {
|
|
|
3282
3396
|
* sends nothing.
|
|
3283
3397
|
*/
|
|
3284
3398
|
const BUILT_IN = /^(BOOTSTRAP|INITIALIZE|DISPOSE|RESOURCE)$/;
|
|
3399
|
+
// a behavior's plumbing actions (sygnal/yjs shared(): 'board.LOCAL', 'board.REMOTE') mark their
|
|
3400
|
+
// handlers, so they are 'built-in' too (DevTools hides them with INITIALIZE)
|
|
3401
|
+
const MARK = Symbol.for('sygnal.builtIn');
|
|
3285
3402
|
/** now on the test's fake clock when there is one (vi.useFakeTimers()), else Date.now() */
|
|
3286
3403
|
const clockNow = () => {
|
|
3287
3404
|
const c = setTimeout.clock;
|
|
@@ -3291,7 +3408,8 @@ const clockNow = () => {
|
|
|
3291
3408
|
* PLAN-4.6 R4: the same log on the core, from its hooks (04 §3.3) instead of instance
|
|
3292
3409
|
* patches: onAction opens the record (the core passes the cause: 'intent', 'next', 'reply',
|
|
3293
3410
|
* 'built-in', 'simulateAction'), wrapHandler sees which sinks produced a value. Same records,
|
|
3294
|
-
* same cause rules: a built-in type
|
|
3411
|
+
* same cause rules: a built-in type (or one whose handlers carry Symbol.for('sygnal.builtIn')) is
|
|
3412
|
+
* 'built-in' unless simulated; an intent action a behavior
|
|
3295
3413
|
* owns is 'behavior'. `only(inst)`: the instances to record (renderComponent: its own tree).
|
|
3296
3414
|
*/
|
|
3297
3415
|
function actionHooks(listener, only) {
|
|
@@ -3313,7 +3431,8 @@ function actionHooks(listener, only) {
|
|
|
3313
3431
|
addSink(listener, rec, ['STATE'], 'STATE', undefined);
|
|
3314
3432
|
},
|
|
3315
3433
|
onAction(inst, a) {
|
|
3316
|
-
|
|
3434
|
+
// 7.2: a runtime setState (time travel, HMR, element props) isn't an action of the log
|
|
3435
|
+
if ((only && !only(inst)) || a.cause == 'setState')
|
|
3317
3436
|
return;
|
|
3318
3437
|
const type = String(a.type), owned = inst.def.behaviorActions;
|
|
3319
3438
|
const hs = inst.def.handlers.get(type) || [], order = hs.map((h) => h[0]);
|
|
@@ -3323,7 +3442,7 @@ function actionHooks(listener, only) {
|
|
|
3323
3442
|
cur.set(inst, [opened, order]);
|
|
3324
3443
|
return;
|
|
3325
3444
|
}
|
|
3326
|
-
const cause = a.cause == 'simulateAction' ? a.cause : BUILT_IN.test(type) ? 'built-in'
|
|
3445
|
+
const cause = a.cause == 'simulateAction' ? a.cause : BUILT_IN.test(type) || hs.some((h) => h[1]?.[MARK]) ? 'built-in'
|
|
3327
3446
|
: a.cause == 'next' || a.cause == 'reply' || a.cause == 'agent' ? a.cause
|
|
3328
3447
|
: owned && Object.prototype.hasOwnProperty.call(owned, type) ? 'behavior' : 'intent';
|
|
3329
3448
|
const rec = { type, data: a.data, component: inst.name, instance: String(inst.id), sinks: [], cause, time: clockNow(), ...(a.source !== undefined && { source: a.source }) };
|
|
@@ -3875,7 +3994,9 @@ const audited = (f, type, sink, h) => (s, d, nx, p, ...r) => {
|
|
|
3875
3994
|
* page after a patch, G-460: on rendered elements), SYG612 (D173: a form
|
|
3876
3995
|
* 6.0 removed, met at runtime; once per form and component, with a link to the migration
|
|
3877
3996
|
* guide), SYG453 (PLAN-9, D470: two children with one identity share one instance), SYG426
|
|
3878
|
-
* (PLAN-9, D471: a lowercase <collection> / <switchable> tag, deprecated in 6.3.0)
|
|
3997
|
+
* (PLAN-9, D471: a lowercase <collection> / <switchable> tag, deprecated in 6.3.0), SYG117
|
|
3998
|
+
* (PLAN-13 N-1: next() to an action the component has no model entry for), SYG204 (sandbox
|
|
3999
|
+
* research §6: a root that has no state after it started, so its view never runs).
|
|
3879
4000
|
*
|
|
3880
4001
|
* Only while diagnostics are on (the checks' own rule); production builds never load this.
|
|
3881
4002
|
*/
|
|
@@ -4071,6 +4192,36 @@ function lowercaseTags(iv) {
|
|
|
4071
4192
|
};
|
|
4072
4193
|
walk(raw.tmpl, 0);
|
|
4073
4194
|
}
|
|
4195
|
+
/**
|
|
4196
|
+
* PLAN-13 F-3 SYG429: a Switchable's per-page `state={{ page: … }}` with a key that names no page
|
|
4197
|
+
* (read from the owner's template, as SYG426; 0 production bytes). Once per component and key
|
|
4198
|
+
*/
|
|
4199
|
+
function switchableMaps(iv) {
|
|
4200
|
+
const raw = iv.i;
|
|
4201
|
+
if (!raw?.kids?.size || !raw.tmpl)
|
|
4202
|
+
return;
|
|
4203
|
+
const walk = (n, d) => {
|
|
4204
|
+
if (!n || typeof n != 'object' || n.$p || d > 300)
|
|
4205
|
+
return;
|
|
4206
|
+
const p = n.sel == 'switchable' && n.data?.props, st = p?.state;
|
|
4207
|
+
if (st && isPlainObject(st) && typeof st.get != 'function' && isPlainObject(p.of)) {
|
|
4208
|
+
const pages = Object.keys(p.of);
|
|
4209
|
+
for (const k of Object.keys(st))
|
|
4210
|
+
if (!(k in p.of) && once(`SYG429:${iv.name}:${k}`))
|
|
4211
|
+
devReport('SYG429', {
|
|
4212
|
+
component: iv.name,
|
|
4213
|
+
message: `${iv.name}'s Switchable state map binds '${k}', which is not one of its pages (${pages.join(', ')}), so that binding is never used${didYouMean(k, pages)}`,
|
|
4214
|
+
fix: 'Use the page name from of={{ … }} as the key, or remove the entry',
|
|
4215
|
+
data: { key: k, pages },
|
|
4216
|
+
});
|
|
4217
|
+
}
|
|
4218
|
+
const kids = n.children;
|
|
4219
|
+
if (Array.isArray(kids))
|
|
4220
|
+
for (const c of kids)
|
|
4221
|
+
walk(c, d + 1);
|
|
4222
|
+
};
|
|
4223
|
+
walk(raw.tmpl, 0);
|
|
4224
|
+
}
|
|
4074
4225
|
/** the instance fields the checks read from today's Component instances */
|
|
4075
4226
|
class Facade {
|
|
4076
4227
|
constructor(__next) {
|
|
@@ -4267,6 +4418,31 @@ function vtReport(dup) {
|
|
|
4267
4418
|
}
|
|
4268
4419
|
}
|
|
4269
4420
|
/** The hooks one app gets (installChecks publishes this on the bridge). */
|
|
4421
|
+
/**
|
|
4422
|
+
* SYG204 (sandbox research §6): the core calls no view while its instance's state is undefined,
|
|
4423
|
+
* and a root gets state only from initialState (or the state an HMR swap keeps), a model-less root's `true`, or
|
|
4424
|
+
* a reducer. Checked once per app after the start settles: the first flush (INITIALIZE included)
|
|
4425
|
+
* and the flush BOOTSTRAP's reducers cause; a root still without state is reported (0 core bytes:
|
|
4426
|
+
* the runtime API's flushed(), no timers).
|
|
4427
|
+
*/
|
|
4428
|
+
function statelessRoot(api, iv, on) {
|
|
4429
|
+
if (!api?.flushed || iv.state !== undefined)
|
|
4430
|
+
return;
|
|
4431
|
+
const settled = () => api.flushed();
|
|
4432
|
+
// the first flush, then BOOTSTRAP (a microtask after it) and the flush it schedules
|
|
4433
|
+
Promise.resolve().then(settled).then(() => new Promise(r => queueMicrotask(r))).then(settled).then(() => {
|
|
4434
|
+
if (iv.disposed || iv.state !== undefined || !on())
|
|
4435
|
+
return;
|
|
4436
|
+
const name = iv.name, h = iv.def.handlers;
|
|
4437
|
+
const later = [...h.keys()].filter((k) => k != 'INITIALIZE' && k != 'BOOTSTRAP' && h.get(k).some((x) => x[0] == 'STATE'));
|
|
4438
|
+
devReport('SYG204', {
|
|
4439
|
+
component: name,
|
|
4440
|
+
message: `${name} is the root component and has no state after it started (no initialState, and no INITIALIZE or BOOTSTRAP reducer set one), so its view is not called and the page stays empty${later.length ? ` until one of its reducers (${later.slice(0, 3).join(', ')}${later.length > 3 ? ', …' : ''}) sets the state` : ''}`,
|
|
4441
|
+
fix: `Give ${name} an initialState (${name}.initialState = {} for a component with no data of its own), or return the first state from an INITIALIZE reducer`,
|
|
4442
|
+
data: { reducers: later },
|
|
4443
|
+
});
|
|
4444
|
+
}).catch(() => { });
|
|
4445
|
+
}
|
|
4270
4446
|
function nextHooks(_api) {
|
|
4271
4447
|
const core = bridge(), H = core.hooks;
|
|
4272
4448
|
const on = () => H.on();
|
|
@@ -4293,9 +4469,18 @@ function nextHooks(_api) {
|
|
|
4293
4469
|
const vtCheck = vtChecker(() => vtp.size && on());
|
|
4294
4470
|
// SYG428: the live instances by id, to read a child's parent state
|
|
4295
4471
|
const live = new Map();
|
|
4472
|
+
// SYG117, SYG112: the nearest ancestor whose model has an entry `type`
|
|
4473
|
+
const handlerAbove = (iv, type) => {
|
|
4474
|
+
let up = live.get(iv.parentId);
|
|
4475
|
+
while (up && !up.def.handlers.has(type))
|
|
4476
|
+
up = live.get(up.parentId);
|
|
4477
|
+
return up;
|
|
4478
|
+
};
|
|
4296
4479
|
return {
|
|
4297
4480
|
onCreate(iv) {
|
|
4298
4481
|
live.set(iv.id, iv);
|
|
4482
|
+
if (iv.isRoot)
|
|
4483
|
+
statelessRoot(_api, iv, on);
|
|
4299
4484
|
if (!on())
|
|
4300
4485
|
return;
|
|
4301
4486
|
log.onCreate(iv);
|
|
@@ -4347,6 +4532,7 @@ function nextHooks(_api) {
|
|
|
4347
4532
|
removedTags(f, vnode);
|
|
4348
4533
|
try {
|
|
4349
4534
|
lowercaseTags(iv);
|
|
4535
|
+
switchableMaps(iv);
|
|
4350
4536
|
}
|
|
4351
4537
|
catch (_) { /* a check never breaks the app */ }
|
|
4352
4538
|
// SYG453: only a view with children can render one twice
|
|
@@ -4383,12 +4569,30 @@ function nextHooks(_api) {
|
|
|
4383
4569
|
if (sink == 'EVENTS')
|
|
4384
4570
|
eventEmitted(f, v);
|
|
4385
4571
|
if (src && src.__sygnalReplies === true)
|
|
4386
|
-
checkRequest(f, sink, v, Object.keys(f.modelMap));
|
|
4572
|
+
checkRequest(f, sink, v, Object.keys(f.modelMap), (a) => handlerAbove(iv, a)?.name);
|
|
4387
4573
|
if (src && src.__sygnalStatic === 'resources')
|
|
4388
4574
|
check(f, sink, v);
|
|
4389
4575
|
}
|
|
4390
4576
|
catch (_) { /* a check never breaks the app */ }
|
|
4391
4577
|
},
|
|
4578
|
+
// SYG117 (PLAN-13 N-1): next('X') goes to the component's own model only; with no entry
|
|
4579
|
+
// for X it does nothing (an ancestor's entry of that name never runs)
|
|
4580
|
+
onNext(iv, type) {
|
|
4581
|
+
if (!on() || iv.def.handlers.has(type))
|
|
4582
|
+
return;
|
|
4583
|
+
const name = iv.name;
|
|
4584
|
+
if (!once(`SYG117:${name}:${type}`))
|
|
4585
|
+
return;
|
|
4586
|
+
const up = handlerAbove(iv, type);
|
|
4587
|
+
devReport('SYG117', {
|
|
4588
|
+
component: name,
|
|
4589
|
+
message: `${name} calls next('${type}'), but ${name} has no model entry '${type}', so the action does nothing${up ? `: next() never reaches ${up.name}, which handles '${type}'` : ''}${didYouMean(type, iv.def.handlers.keys())}`,
|
|
4590
|
+
fix: up
|
|
4591
|
+
? `next() stays in its own component. Send it up instead: give the entry that calls it a PARENT sink (PARENT: (state) => ({ … })) and add ${type}: CHILD.select(${name}) to ${up.name}.intent, or emit EVENTS: event('${type}', …) and select it there`
|
|
4592
|
+
: `Add the entry to ${name}.model (${type}: (state, data) => …), or fix the name to match an existing one`,
|
|
4593
|
+
data: { action: type, ...(up ? { handledBy: up.name } : {}) },
|
|
4594
|
+
});
|
|
4595
|
+
},
|
|
4392
4596
|
onElementCommand(iv, cmd) {
|
|
4393
4597
|
if (!on())
|
|
4394
4598
|
return;
|