sygnal 5.4.0 → 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 +24 -9
- package/dist/astro/client.cjs.js +16 -9
- package/dist/astro/client.cjs.js.map +1 -1
- package/dist/astro/client.mjs +16 -9
- package/dist/astro/client.mjs.map +1 -1
- package/dist/astro/index.cjs.js +362 -91
- package/dist/astro/index.cjs.js.map +1 -1
- package/dist/astro/index.mjs +362 -92
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/server.cjs.js +3396 -165
- package/dist/astro/server.cjs.js.map +1 -1
- package/dist/astro/server.mjs +3396 -165
- package/dist/astro/server.mjs.map +1 -1
- 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 +3032 -296
- package/dist/diagnostics.cjs.js.map +1 -1
- package/dist/diagnostics.esm.js +3032 -296
- package/dist/diagnostics.esm.js.map +1 -1
- 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 +13178 -5769
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2908 -276
- package/dist/index.esm.js +13149 -5779
- package/dist/index.esm.js.map +1 -1
- package/dist/jsx-dev-runtime.cjs.js +26 -334
- package/dist/jsx-dev-runtime.cjs.js.map +1 -1
- package/dist/jsx-dev-runtime.esm.js +19 -327
- package/dist/jsx-dev-runtime.esm.js.map +1 -1
- package/dist/jsx-runtime.cjs.js +26 -334
- package/dist/jsx-runtime.cjs.js.map +1 -1
- package/dist/jsx-runtime.esm.js +19 -327
- package/dist/jsx-runtime.esm.js.map +1 -1
- package/dist/jsx.cjs.js +10 -314
- package/dist/jsx.cjs.js.map +1 -1
- package/dist/jsx.esm.js +8 -315
- package/dist/jsx.esm.js.map +1 -1
- 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 +1 -1
- package/dist/sygnal.min.js.map +1 -1
- 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 +4 -14
- package/dist/vike/ClientOnly.cjs.js.map +1 -1
- package/dist/vike/ClientOnly.mjs +4 -14
- package/dist/vike/ClientOnly.mjs.map +1 -1
- package/dist/vike/{+config.js → config/+config.js} +9 -1
- package/dist/vike/config/+config.js.map +1 -0
- package/dist/vike/config/package.json +6 -0
- package/dist/vike/onRenderClient.cjs.js +165 -95
- package/dist/vike/onRenderClient.cjs.js.map +1 -1
- package/dist/vike/onRenderClient.mjs +165 -95
- package/dist/vike/onRenderClient.mjs.map +1 -1
- package/dist/vike/onRenderHtml.cjs.js +59 -24
- package/dist/vike/onRenderHtml.cjs.js.map +1 -1
- package/dist/vike/onRenderHtml.mjs +60 -25
- package/dist/vike/onRenderHtml.mjs.map +1 -1
- package/dist/vite/plugin.cjs.js +323 -88
- package/dist/vite/plugin.cjs.js.map +1 -1
- package/dist/vite/plugin.mjs +323 -89
- package/dist/vite/plugin.mjs.map +1 -1
- 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 +166 -103
- package/package.json +98 -14
- package/src/astro/client.ts +27 -16
- package/src/astro/index.d.ts +24 -0
- package/src/astro/index.ts +69 -3
- package/src/astro/server.ts +8 -2
- package/src/collection.ts +6 -93
- 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 +9 -2
- package/src/cycle/dom/PriorityQueue.ts +4 -2
- package/src/cycle/dom/SymbolTree.ts +18 -47
- package/src/cycle/dom/classNameModule.ts +9 -7
- package/src/cycle/dom/controlledInputModule.ts +23 -6
- package/src/cycle/dom/enrichEventStream.ts +25 -47
- package/src/cycle/dom/fragment.ts +7 -0
- package/src/cycle/dom/isolate.ts +30 -27
- package/src/cycle/dom/makeDOMDriver.ts +115 -56
- package/src/cycle/dom/mockDOMSource.ts +21 -1
- package/src/cycle/dom/modules.ts +16 -3
- 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 +19 -3
- 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 +101 -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 +1 -1
- 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 +20 -11
- package/src/extra/diagnostics/checks/elementCommands.ts +175 -0
- package/src/extra/diagnostics/checks/events.ts +17 -2
- 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 +97 -4
- package/src/extra/diagnostics/checks/inspect.ts +140 -32
- 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 +10 -5
- package/src/extra/diagnostics/checks/public.d.ts +98 -7
- 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 +1 -1
- package/src/extra/diagnostics/checks/shared.ts +86 -2
- 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 +100 -7
- package/src/extra/diagnostics/checks/statics.ts +48 -0
- package/src/extra/diagnostics/checks/strict.ts +10 -67
- 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 +66 -10
- package/src/extra/diagnostics/codes.ts +268 -23
- package/src/extra/diagnostics/index.ts +34 -9
- package/src/extra/diagnostics/legacy.ts +17 -0
- package/src/extra/driverFactories.ts +83 -19
- package/src/extra/elementCommands.ts +53 -0
- package/src/extra/fetchDriver.ts +519 -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 +1 -1
- 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 +90 -217
- 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 +2433 -175
- 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/index.d.ts +2631 -117
- package/src/index.ts +25 -4
- 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 +256 -133
- 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 -121
- 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 +7 -1
- package/src/vite/globalthis-shim.ts +18 -0
- package/src/vite/globalthis.ts +56 -0
- package/src/vite/plugin.d.ts +22 -3
- package/src/vite/plugin.ts +222 -24
- package/src/zag.d.ts +64 -0
- package/src/zag.ts +238 -0
- package/dist/vike/+config.cjs.js +0 -66
- package/dist/vike/+config.cjs.js.map +0 -1
- package/dist/vike/+config.js.map +0 -1
- package/src/component.ts +0 -2301
- 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 -184
- package/src/cycle/state/index.ts +0 -5
- package/src/cycle/state/pickCombine.ts +0 -201
- package/src/cycle/state/pickMerge.ts +0 -122
- package/src/cycle/state/withState.ts +0 -47
- package/src/pragma/fn.ts +0 -55
|
@@ -52,10 +52,11 @@
|
|
|
52
52
|
* The mode is resolved at module load, on every run() call and on every
|
|
53
53
|
* configureDiagnostics() call. `configureDiagnostics({ mode: undefined })`
|
|
54
54
|
* clears the explicit mode and falls back to steps 2-3.
|
|
55
|
-
*
|
|
56
|
-
* ignore list from
|
|
57
|
-
*
|
|
58
|
-
*
|
|
55
|
+
* A run() call with the `diagnostics` option sets both the explicit mode and
|
|
56
|
+
* the ignore list from it. run() WITHOUT the option resets them to the
|
|
57
|
+
* defaults (no explicit mode, empty ignore list), so a setting from an earlier
|
|
58
|
+
* run()/configureDiagnostics() does not leak in, unless another app is still
|
|
59
|
+
* running (G-212: a second app or a custom element keeps the host's mode).
|
|
59
60
|
*
|
|
60
61
|
* --- Checks ---------------------------------------------------------------
|
|
61
62
|
*
|
|
@@ -73,7 +74,7 @@
|
|
|
73
74
|
* A check that throws (other than a DiagnosticError from 'error' mode) is
|
|
74
75
|
* isolated: the exception is reported as SYG900 and other checks still run.
|
|
75
76
|
*
|
|
76
|
-
* --- Hooks (
|
|
77
|
+
* --- Hooks (dispatched by checks/next.ts from the core's hook layer) ----
|
|
77
78
|
*
|
|
78
79
|
* onIntent(component, actionNames, selectorsUsed?)
|
|
79
80
|
* after intent is built. actionNames = keys of the intent object ([] for a
|
|
@@ -118,8 +119,9 @@
|
|
|
118
119
|
* `component` is the internal Component instance (stable identity per
|
|
119
120
|
* instance; `.name` is the component name).
|
|
120
121
|
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
122
|
+
* In mode 'off' a hook only runs the checks registered with `always: true`
|
|
123
|
+
* (internal: renderComponent's own bookkeeping, R2-3); none are registered
|
|
124
|
+
* in an app, so the hooks do nothing.
|
|
123
125
|
*
|
|
124
126
|
* Hooks NEVER throw synchronously into Sygnal's stream pipeline (a throw
|
|
125
127
|
* there would kill the state/view stream for good). Every exception from a
|
|
@@ -167,6 +169,8 @@ export interface DiagnosticsOptions {
|
|
|
167
169
|
|
|
168
170
|
export interface DiagnosticCheck {
|
|
169
171
|
id: string
|
|
172
|
+
/** internal: also runs when diagnostics are 'off' (renderComponent's harness hooks) */
|
|
173
|
+
always?: boolean
|
|
170
174
|
onIntent?: (component: any, actionNames: string[], selectorsUsed: string[] | undefined) => void
|
|
171
175
|
onModel?: (component: any, modelMap: Record<string, string[]>) => void
|
|
172
176
|
onRender?: (component: any, rootVnode: any) => void
|
|
@@ -175,6 +179,12 @@ export interface DiagnosticCheck {
|
|
|
175
179
|
onSelector?: (domSource: any, selector: string) => void
|
|
176
180
|
onBusEmit?: (type: string, emitterName?: string) => void
|
|
177
181
|
onBusSelect?: (type: string | string[] | undefined) => void
|
|
182
|
+
/**
|
|
183
|
+
* (additive, PLAN-2 E2) Called before a component's intent runs. May return a replacement
|
|
184
|
+
* for `sources` (e.g. a Proxy that reports or fakes a source with no driver); the intent
|
|
185
|
+
* gets the last one returned. See sourcesFor().
|
|
186
|
+
*/
|
|
187
|
+
sources?: (component: any, sources: any) => any
|
|
178
188
|
}
|
|
179
189
|
|
|
180
190
|
export class DiagnosticError extends Error {
|
|
@@ -282,8 +292,8 @@ export function _setAsyncThrow(fn?: (err: any) => void): void {
|
|
|
282
292
|
}
|
|
283
293
|
|
|
284
294
|
const hook = (name: HookName) => (component: any, ...args: any[]): void => {
|
|
285
|
-
if (!enabled) return
|
|
286
295
|
for (const check of checks) {
|
|
296
|
+
if (!enabled && !check.always) continue
|
|
287
297
|
try {
|
|
288
298
|
(check[name] as any)?.(component, ...args)
|
|
289
299
|
} catch (err: any) {
|
|
@@ -312,8 +322,23 @@ export const onSelector: (domSource: any, selector: string) => void = hook('onSe
|
|
|
312
322
|
export const onBusEmit: (type: string, emitterName?: string) => void = hook('onBusEmit')
|
|
313
323
|
export const onBusSelect: (type: string | string[] | undefined) => void = hook('onBusSelect')
|
|
314
324
|
|
|
325
|
+
/**
|
|
326
|
+
* (PLAN-2 E2) The sources object a component's intent is called with: `component.sources`,
|
|
327
|
+
* wrapped by every active check with a `sources` hook (the dev entry's SYG609 check,
|
|
328
|
+
* renderComponent's fake sources). Without such a check it is `component.sources` itself.
|
|
329
|
+
*/
|
|
330
|
+
export function sourcesFor(component: any): any {
|
|
331
|
+
let s = component.sources
|
|
332
|
+
for (const check of checks) {
|
|
333
|
+
if (!check.sources || (!enabled && !check.always)) continue
|
|
334
|
+
try { s = check.sources(component, s) || s } catch (_) {}
|
|
335
|
+
}
|
|
336
|
+
return s
|
|
337
|
+
}
|
|
338
|
+
|
|
315
339
|
// Bridge for the separately bundled 'sygnal/diagnostics' checks entry.
|
|
316
|
-
|
|
340
|
+
// PLAN-4.6 R4: the hook dispatchers too, for the dev entry's core adapter (checks/next.ts)
|
|
341
|
+
;(globalThis as any).__SYGNAL_DIAGNOSTICS__ = { registerCheck, report, hooks: { onIntent, onModel, onRender, onReducer, onDispose, sourcesFor, on: isDiagnosticsEnabled } }
|
|
317
342
|
|
|
318
343
|
export function _resetDiagnostics(): void {
|
|
319
344
|
explicitMode = undefined
|
|
@@ -26,6 +26,14 @@
|
|
|
26
26
|
* `.code` is the diagnostic code. It does not go through report(): the throw
|
|
27
27
|
* itself is the signal, in every mode.
|
|
28
28
|
*
|
|
29
|
+
* A call site that catches an exception and keeps running reports it with the
|
|
30
|
+
* core's caught() (src/core/runtime.ts: an error thrown by fail() under ITS code,
|
|
31
|
+
* G-027 / G-044), then the app's onError hook (callHook, PLAN-4 GS-11).
|
|
32
|
+
*
|
|
33
|
+
* `error` is also published on the core bridge
|
|
34
|
+
* (globalThis.__SYGNAL_DIAGNOSTICS__.error) for the separately bundled JSX
|
|
35
|
+
* runtime (SYG420).
|
|
36
|
+
*
|
|
29
37
|
* (This comment is attached to the type-only import below so the TypeScript
|
|
30
38
|
* emit drops it.)
|
|
31
39
|
*/
|
|
@@ -55,5 +63,14 @@ export const error = emit('error')
|
|
|
55
63
|
export function fail(code: string, component: any, message: string, fix?: string): never {
|
|
56
64
|
const err: any = new Error(formatDiagnostic(code, { component, message, fix }))
|
|
57
65
|
err.code = code
|
|
66
|
+
err.sygnal = [component, message, fix]
|
|
58
67
|
throw err
|
|
59
68
|
}
|
|
69
|
+
|
|
70
|
+
// PLAN-4 GS-11: the app's error hook (run(App, drivers, { onError })), reporting only (each app
|
|
71
|
+
// has its own). A throwing hook is logged once and swallowed
|
|
72
|
+
export const callHook = (hook: any, err: any, info: any): void => {
|
|
73
|
+
try { hook && hook(err, info) } catch (e) { console.error(e) }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
;(globalThis as any).__SYGNAL_DIAGNOSTICS__.error = error
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import xs, {Stream} from 'xstream';
|
|
2
|
+
import {senderOf, allowed, makeReplies} from './replies';
|
|
2
3
|
|
|
3
4
|
interface DriverFromAsyncOptions {
|
|
4
5
|
selector?: string;
|
|
@@ -15,7 +16,9 @@ interface DriverFromAsyncOptions {
|
|
|
15
16
|
* by `args`, after `pre`). The resolved value, after `post`, is delivered as
|
|
16
17
|
* `{ [return]: value, [selector]: request[selector] }` (defaults:
|
|
17
18
|
* `{ value, category }`) and read with `source.select(category)`. A promise
|
|
18
|
-
* that resolves to `null`/`undefined` is delivered the same way.
|
|
19
|
+
* that resolves to `null`/`undefined` is delivered the same way. Replies that
|
|
20
|
+
* resolve before any `select()` listener exists (a BOOTSTRAP request, say) are
|
|
21
|
+
* held and delivered once the first one subscribes.
|
|
19
22
|
*
|
|
20
23
|
* Errors: if the function's promise (or a promise returned by `post`)
|
|
21
24
|
* rejects, or `post` throws, the error is delivered on `source.errors()`,
|
|
@@ -31,13 +34,21 @@ interface DriverFromAsyncOptions {
|
|
|
31
34
|
*
|
|
32
35
|
* `errors(selector?)` filters like `select()`: no argument = all errors, a
|
|
33
36
|
* string matches the request's selector property, a function is a predicate on
|
|
34
|
-
* the error payload.
|
|
35
|
-
*
|
|
37
|
+
* the error payload. An error no errors() listener matches is logged with
|
|
38
|
+
* console.error. Errors that occur before the first errors() listener
|
|
39
|
+
* subscribes (a BOOTSTRAP request) are held like early replies.
|
|
40
|
+
*
|
|
41
|
+
* Reply actions (PLAN-3): a request object a component sends with `ok` /
|
|
42
|
+
* `error` action names gets its outcome as that action, on exactly the sending
|
|
43
|
+
* instance (never on select()/errors()): `ok` data is the resolved value (after
|
|
44
|
+
* `post`), `error` data `{ error, request }`. A request naming only one of them
|
|
45
|
+
* sends the other outcome down select()/errors(). A reply for a disposed
|
|
46
|
+
* instance is dropped. Requests with a `then` / `catch` key are refused (SYG610).
|
|
36
47
|
*/
|
|
37
48
|
function driverFromAsync(
|
|
38
49
|
promiseReturningFunction: (...args: any[]) => Promise<any>,
|
|
39
50
|
opts: DriverFromAsyncOptions = {}
|
|
40
|
-
): (fromApp$: Stream<any>) => {select: (selector?: any) => Stream<any>; errors: (selector?: any) => Stream<any
|
|
51
|
+
): (fromApp$: Stream<any>) => {select: (selector?: any) => Stream<any>; errors: (selector?: any) => Stream<any>; [k: string]: any} {
|
|
41
52
|
const {
|
|
42
53
|
selector: selectorProperty = 'category',
|
|
43
54
|
args: functionArgs = 'value',
|
|
@@ -65,13 +76,41 @@ function driverFromAsync(
|
|
|
65
76
|
}
|
|
66
77
|
|
|
67
78
|
return (fromApp$: Stream<any>) => {
|
|
79
|
+
// G-069: a reply can resolve before anything has subscribed to select() (e.g. a
|
|
80
|
+
// BOOTSTRAP request to an instantly-resolving promise under run()). Such replies are
|
|
81
|
+
// buffered (at most EARLY_REPLY_LIMIT, oldest dropped) until the first select()
|
|
82
|
+
// listener subscribes, then delivered in order on a microtask, so every select()
|
|
83
|
+
// wired in the same pass sees them. After that, a reply that arrives while no one
|
|
84
|
+
// listens is dropped, as with any xstream source.
|
|
85
|
+
const EARLY_REPLY_LIMIT = 100;
|
|
68
86
|
let sendFn: ((val: any) => void) | null = null;
|
|
87
|
+
let started = false;
|
|
88
|
+
let pending: any[] = [];
|
|
89
|
+
const flush = () => {
|
|
90
|
+
const queued = pending;
|
|
91
|
+
pending = [];
|
|
92
|
+
queued.forEach(reply => sendFn?.(reply));
|
|
93
|
+
};
|
|
94
|
+
const deliver = (reply: any) => {
|
|
95
|
+
if (!started || pending.length > 0) {
|
|
96
|
+
pending.push(reply);
|
|
97
|
+
if (pending.length > EARLY_REPLY_LIMIT) pending.shift();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
sendFn?.(reply);
|
|
101
|
+
};
|
|
69
102
|
|
|
70
103
|
const toApp$ = xs.create<any>({
|
|
71
104
|
start: (listener) => {
|
|
72
105
|
sendFn = listener.next.bind(listener);
|
|
106
|
+
if (!started) {
|
|
107
|
+
started = true;
|
|
108
|
+
if (pending.length > 0) queueMicrotask(flush);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
stop: () => {
|
|
112
|
+
sendFn = null;
|
|
73
113
|
},
|
|
74
|
-
stop: () => {},
|
|
75
114
|
});
|
|
76
115
|
|
|
77
116
|
// Active errors(selector) streams (1H-6): an error goes to the ones it matches, and is
|
|
@@ -82,13 +121,43 @@ function driverFromAsync(
|
|
|
82
121
|
(typeof selector === 'function' ? selector(val) : val?.[selectorProperty] === selector);
|
|
83
122
|
const filterBy = (stream: Stream<any>, selector?: any) =>
|
|
84
123
|
selector === undefined ? stream : stream.filter((val: any) => matches(selector, val));
|
|
124
|
+
const errMsg = `Error in driver created using driverFromAsync(${functionName})`;
|
|
125
|
+
const dispatchError = (val: any) => {
|
|
126
|
+
let handled = false;
|
|
127
|
+
errorSubs.forEach(sub => {
|
|
128
|
+
let hit = false;
|
|
129
|
+
try { hit = matches(sub.selector, val); } catch (_) {}
|
|
130
|
+
if (hit) {
|
|
131
|
+
handled = true;
|
|
132
|
+
sub.listener.next(val);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
if (!handled) console.error(`${errMsg}: ${val.error}`);
|
|
136
|
+
};
|
|
137
|
+
// G-092: like early replies, errors that occur before the first errors() listener
|
|
138
|
+
// subscribes are held (same limit) and dispatched on a microtask once it does. If
|
|
139
|
+
// nothing has subscribed by the next macrotask, they are dispatched (i.e. logged) then.
|
|
140
|
+
let errStarted = false;
|
|
141
|
+
let pendingErrors: any[] = [];
|
|
142
|
+
const flushErrors = () => {
|
|
143
|
+
errStarted = true;
|
|
144
|
+
const queued = pendingErrors;
|
|
145
|
+
pendingErrors = [];
|
|
146
|
+
queued.forEach(dispatchError);
|
|
147
|
+
};
|
|
85
148
|
|
|
86
149
|
// 3E/R11: set by the source's dispose(), which Cycle's engine calls on teardown just
|
|
87
150
|
// before it completes the sink proxies; that completion is expected, not worth a warning.
|
|
88
151
|
let disposing = false;
|
|
152
|
+
const {replies, reply} = makeReplies();
|
|
89
153
|
|
|
90
154
|
fromApp$.addListener({
|
|
91
155
|
next: (incoming: any) => {
|
|
156
|
+
const isObject = typeof incoming === 'object' && incoming !== null;
|
|
157
|
+
if (isObject && !allowed(incoming, `driverFromAsync(${functionName})`)) return;
|
|
158
|
+
const sender = isObject ? senderOf(incoming) : undefined;
|
|
159
|
+
const ok = sender !== undefined && incoming.ok;
|
|
160
|
+
const error = sender !== undefined && incoming.error;
|
|
92
161
|
const preProcessed = preFunction(incoming);
|
|
93
162
|
let argArr: any[] = [];
|
|
94
163
|
if (typeof preProcessed === 'object' && preProcessed !== null) {
|
|
@@ -103,7 +172,6 @@ function driverFromAsync(
|
|
|
103
172
|
argArr = functionArgs.map((arg: string) => preProcessed[arg]);
|
|
104
173
|
}
|
|
105
174
|
}
|
|
106
|
-
const errMsg = `Error in driver created using driverFromAsync(${functionName})`;
|
|
107
175
|
const constructReply = (rawVal: any) => {
|
|
108
176
|
let outgoing: any;
|
|
109
177
|
if (returnProperty === undefined) {
|
|
@@ -130,17 +198,11 @@ function driverFromAsync(
|
|
|
130
198
|
// Rejections (of the function, a thenable it resolves to, or post()) go to errors();
|
|
131
199
|
// an error no active errors() selector matches is logged, as before.
|
|
132
200
|
const reportError = (err: any) => {
|
|
201
|
+
if (error) return reply(sender, error, {error: err, request: incoming});
|
|
133
202
|
const val = {error: err, [selectorProperty]: incoming?.[selectorProperty], request: incoming};
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
try { hit = matches(sub.selector, val); } catch (_) {}
|
|
138
|
-
if (hit) {
|
|
139
|
-
handled = true;
|
|
140
|
-
sub.listener.next(val);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
if (!handled) console.error(`${errMsg}: ${err}`);
|
|
203
|
+
if (errStarted) return dispatchError(val);
|
|
204
|
+
if (pendingErrors.push(val) === 1) setTimeout(flushErrors);
|
|
205
|
+
if (pendingErrors.length > EARLY_REPLY_LIMIT) pendingErrors.shift();
|
|
144
206
|
};
|
|
145
207
|
const isThenable = (val: any) => val != null && typeof val.then === 'function';
|
|
146
208
|
promiseReturningFunction(...argArr)
|
|
@@ -149,10 +211,10 @@ function driverFromAsync(
|
|
|
149
211
|
? innerVal.then((innerOutgoing: any) => postFunction(innerOutgoing, incoming))
|
|
150
212
|
: postFunction(innerVal, incoming)
|
|
151
213
|
)
|
|
152
|
-
.then(constructReply)
|
|
153
|
-
.then((
|
|
214
|
+
.then((val: any) => (ok ? val : constructReply(val)))
|
|
215
|
+
.then((outgoing: any) => {
|
|
154
216
|
try {
|
|
155
|
-
|
|
217
|
+
ok ? reply(sender, ok, outgoing) : deliver(outgoing);
|
|
156
218
|
} catch (err) {
|
|
157
219
|
console.error(`${errMsg}: ${err}`);
|
|
158
220
|
}
|
|
@@ -177,11 +239,13 @@ function driverFromAsync(
|
|
|
177
239
|
disposing = true;
|
|
178
240
|
},
|
|
179
241
|
select: (selector?: any) => filterBy(toApp$, selector),
|
|
242
|
+
...replies,
|
|
180
243
|
errors: (selector?: any) => {
|
|
181
244
|
let sub: any;
|
|
182
245
|
return xs.create<any>({
|
|
183
246
|
start: (listener) => {
|
|
184
247
|
errorSubs.add((sub = {listener, selector}));
|
|
248
|
+
if (!errStarted) queueMicrotask(flushErrors);
|
|
185
249
|
},
|
|
186
250
|
stop: () => {
|
|
187
251
|
errorSubs.delete(sub);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* PLAN-4 GS-2: element commands, the built-in ELEMENT sink.
|
|
3
|
+
*
|
|
4
|
+
* SUBMIT: { STATE: validate, ELEMENT: (s) => (s.errors.email ? { focus: Email } : ABORT) },
|
|
5
|
+
* OPEN_HELP: { ELEMENT: { showModal: HelpDialog } },
|
|
6
|
+
*
|
|
7
|
+
* A command is `{ <method>: target, ...options }`: the FIRST key is the method, the other keys
|
|
8
|
+
* are its options (D118); an array sends several, in order. The target (a control or a
|
|
9
|
+
* selector) is resolved in the SENDING instance's own DOM source, so isolation applies (a
|
|
10
|
+
* Collection item reaches only its own elements; a parent doesn't reach its children's).
|
|
11
|
+
*
|
|
12
|
+
* The method: D102 first, a control whose spec object declares `commands` runs
|
|
13
|
+
* `commands[method](element, options)` (it overrides a native method of the same name; only the
|
|
14
|
+
* control itself carries a spec, a template-string selector doesn't). A widget host
|
|
15
|
+
* (src/extra/widget.ts) carries its widget's commands, which win the same way for a selector
|
|
16
|
+
* target (PLAN-5 D196: `{ focus: '.editor' }` runs the widget's `focus`). Then the element's own
|
|
17
|
+
* method: `close` gets `returnValue` as its argument (D118), `togglePopover` its `force` (a
|
|
18
|
+
* boolean, or no argument: a browser that only takes a boolean treats an object as true), every
|
|
19
|
+
* other method the options object (focus { preventScroll }, scrollIntoView { block, inline,
|
|
20
|
+
* behavior }, blur, select, click, showModal, show, showPopover, hidePopover; also play, pause, showPicker,
|
|
21
|
+
* requestSubmit, reset...). The core runs any method the element has (no list: it
|
|
22
|
+
* would cost ~60 B); the dev entry reports the ones that change the DOM Sygnal renders (SYG641).
|
|
23
|
+
*
|
|
24
|
+
* Timing: a command runs once the render it may depend on is on the page. The instance's DOM
|
|
25
|
+
* source emits after every DOM mutation of the app (a patch), so the command runs in a microtask
|
|
26
|
+
* after the next patch at which its target exists. When no patch comes (the action changed
|
|
27
|
+
* nothing that renders), it runs at the first 16 ms check instead; the checks repeat every 16 ms
|
|
28
|
+
* while the target is missing, about 1 s, then it is given up (SYG640 in dev). A disposed
|
|
29
|
+
* instance's pending commands are dropped. Without a DOM source nothing runs (SSR renders views
|
|
30
|
+
* without instances); a null command is skipped (ABORT is the way to send nothing).
|
|
31
|
+
*
|
|
32
|
+
* Failures (no match: SYG640; neither a spec command nor an element method: SYG641) go to the
|
|
33
|
+
* 'sygnal/diagnostics' dev entry through the core bridge (`elementCommand`), which words them;
|
|
34
|
+
* the core carries no message text. In production a failed command does nothing.
|
|
35
|
+
*/
|
|
36
|
+
export function runElementCommands(c: any, cmds: any): void {
|
|
37
|
+
const dom = c.sources[c.DOMSourceName];
|
|
38
|
+
if (dom) for (const cmd of ([] as any[]).concat(cmds)) if (cmd) {
|
|
39
|
+
const m = Object.keys(cmd)[0], {[m]: t, ...o} = cmd, s = dom.select('' + t).elements();
|
|
40
|
+
let els: any, n = 0, k = 0, l: any;
|
|
41
|
+
const run = (last?: any) => {
|
|
42
|
+
const e = !c._disposed && els?.[0], f = t?.spec?.commands?.[m] || e?.__sw?.w.commands[m];
|
|
43
|
+
if (!l || !e && !last) return;
|
|
44
|
+
s.removeListener(l);
|
|
45
|
+
clearInterval(i);
|
|
46
|
+
l = 0;
|
|
47
|
+
e && (f || e[m]) ? f ? f(e, o) : e[m](m == 'close' ? o.returnValue : m == 'togglePopover' ? o.force : o)
|
|
48
|
+
: (globalThis as any).__SYGNAL_DIAGNOSTICS__?.elementCommand?.(c, cmd, e);
|
|
49
|
+
}, i = setInterval(() => run(++k > 62), 16);
|
|
50
|
+
// the first value (the DOM as it is now) is skipped; the next ones follow a patch
|
|
51
|
+
s.addListener(l = {next: (x: any) => { els = x; n++ && queueMicrotask(run); }});
|
|
52
|
+
}
|
|
53
|
+
}
|