sygnal 5.3.6 → 5.4.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/README.md +53 -38
- package/dist/astro/client.cjs.js +18 -8106
- package/dist/astro/client.cjs.js.map +1 -0
- package/dist/astro/client.mjs +17 -8105
- package/dist/astro/client.mjs.map +1 -0
- package/dist/astro/index.cjs.js +768 -9
- package/dist/astro/index.cjs.js.map +1 -0
- package/dist/astro/index.mjs +768 -9
- package/dist/astro/index.mjs.map +1 -0
- package/dist/astro/server.cjs.js +4 -1
- package/dist/astro/server.cjs.js.map +1 -0
- package/dist/astro/server.mjs +4 -1
- package/dist/astro/server.mjs.map +1 -0
- package/dist/diagnostics.cjs.js +1297 -0
- package/dist/diagnostics.cjs.js.map +1 -0
- package/dist/diagnostics.esm.js +1267 -0
- package/dist/diagnostics.esm.js.map +1 -0
- package/dist/index.cjs.js +2981 -1386
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +848 -111
- package/dist/index.esm.js +2997 -1411
- package/dist/index.esm.js.map +1 -0
- package/dist/jsx-dev-runtime.cjs.js +15 -9
- package/dist/jsx-dev-runtime.cjs.js.map +1 -0
- package/dist/jsx-dev-runtime.esm.js +15 -9
- package/dist/jsx-dev-runtime.esm.js.map +1 -0
- package/dist/jsx-runtime.cjs.js +15 -9
- package/dist/jsx-runtime.cjs.js.map +1 -0
- package/dist/jsx-runtime.esm.js +15 -9
- package/dist/jsx-runtime.esm.js.map +1 -0
- package/dist/jsx.cjs.js +15 -9
- package/dist/jsx.cjs.js.map +1 -0
- package/dist/jsx.esm.js +15 -9
- package/dist/jsx.esm.js.map +1 -0
- package/dist/sygnal.min.js +2 -1
- package/dist/sygnal.min.js.map +1 -0
- package/dist/vike/+config.cjs.js +1 -0
- package/dist/vike/+config.cjs.js.map +1 -0
- package/dist/vike/+config.js +1 -0
- package/dist/vike/+config.js.map +1 -0
- package/dist/vike/ClientOnly.cjs.js +1 -0
- package/dist/vike/ClientOnly.cjs.js.map +1 -0
- package/dist/vike/ClientOnly.mjs +1 -0
- package/dist/vike/ClientOnly.mjs.map +1 -0
- package/dist/vike/onRenderClient.cjs.js +1 -0
- package/dist/vike/onRenderClient.cjs.js.map +1 -0
- package/dist/vike/onRenderClient.mjs +1 -0
- package/dist/vike/onRenderClient.mjs.map +1 -0
- package/dist/vike/onRenderHtml.cjs.js +1 -0
- package/dist/vike/onRenderHtml.cjs.js.map +1 -0
- package/dist/vike/onRenderHtml.mjs +1 -0
- package/dist/vike/onRenderHtml.mjs.map +1 -0
- package/dist/vite/plugin.cjs.js +680 -47
- package/dist/vite/plugin.cjs.js.map +1 -0
- package/dist/vite/plugin.mjs +680 -47
- package/dist/vite/plugin.mjs.map +1 -0
- package/llms.txt +250 -0
- package/package.json +15 -6
- package/src/astro/client.ts +13 -2
- package/src/astro/index.d.ts +24 -1
- package/src/astro/index.ts +40 -9
- package/src/astro/server.ts +3 -1
- package/src/collection.ts +5 -2
- package/src/component.ts +375 -330
- package/src/cycle/dom/MainDOMSource.ts +4 -0
- package/src/cycle/dom/classNameModule.ts +74 -0
- package/src/cycle/dom/controlledInputModule.ts +33 -0
- package/src/cycle/dom/enrichEventStream.ts +9 -3
- package/src/cycle/dom/makeDOMDriver.ts +1 -2
- package/src/cycle/dom/mockDOMSource.ts +62 -8
- package/src/cycle/dom/modules.ts +5 -1
- package/src/cycle/state/Collection.ts +13 -3
- package/src/cycle/state/StateSource.ts +1 -1
- package/src/cycle/state/pickCombine.ts +35 -1
- package/src/cycle/state/withState.ts +1 -1
- package/src/extra/devtools.ts +8 -0
- package/src/extra/diagnostics/checks/collections.ts +67 -0
- package/src/extra/diagnostics/checks/dom.ts +241 -0
- package/src/extra/diagnostics/checks/events.ts +68 -0
- package/src/extra/diagnostics/checks/index.ts +109 -0
- package/src/extra/diagnostics/checks/inspect.ts +319 -0
- package/src/extra/diagnostics/checks/props.ts +51 -0
- package/src/extra/diagnostics/checks/public.d.ts +211 -0
- package/src/extra/diagnostics/checks/rxjsHints.ts +88 -0
- package/src/extra/diagnostics/checks/shared.ts +119 -0
- package/src/extra/diagnostics/checks/state.ts +55 -0
- package/src/extra/diagnostics/checks/strict.ts +112 -0
- package/src/extra/diagnostics/checks/wiring.ts +65 -0
- package/src/extra/diagnostics/codes.ts +232 -0
- package/src/extra/diagnostics/index.ts +328 -0
- package/src/extra/diagnostics/legacy.ts +59 -0
- package/src/extra/driverFactories.ts +106 -58
- package/src/extra/eventDriver.ts +4 -0
- package/src/extra/flatten.ts +75 -0
- package/src/extra/reducers.ts +12 -5
- package/src/extra/run.ts +11 -0
- package/src/extra/testing.ts +890 -180
- package/src/extra/xstreamExtras.ts +269 -0
- package/src/index.d.ts +428 -17
- package/src/index.ts +7 -6
- package/src/pragma/index.ts +14 -9
- package/src/switchable.ts +10 -8
- package/src/vike/types.ts +21 -4
- package/src/vite/plugin.d.ts +95 -2
- package/src/vite/plugin.ts +730 -55
- 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
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sygnal diagnostics core.
|
|
3
|
+
*
|
|
4
|
+
* ===========================================================================
|
|
5
|
+
* FROZEN INTERFACE (PLAN-1 workstream 0B). Changes after the 0B merge go
|
|
6
|
+
* through the coordinator. Later workstreams add checks (registerCheck) and
|
|
7
|
+
* codes (./codes.ts, inside their reserved range) — they do not change these
|
|
8
|
+
* signatures.
|
|
9
|
+
* ===========================================================================
|
|
10
|
+
*
|
|
11
|
+
* --- Reporting ------------------------------------------------------------
|
|
12
|
+
*
|
|
13
|
+
* report(code: string, details: {
|
|
14
|
+
* component?: string | { name?: string } // component name or instance
|
|
15
|
+
* message: string // what is wrong
|
|
16
|
+
* fix?: string // how to fix it
|
|
17
|
+
* data?: any // structured payload
|
|
18
|
+
* severity?: 'error' | 'warn' | 'info' // override the code's default
|
|
19
|
+
* }): Diagnostic | undefined
|
|
20
|
+
*
|
|
21
|
+
* - mode 'off' → no-op, returns undefined (nothing collected/printed)
|
|
22
|
+
* - code in ignore list → no-op, returns undefined
|
|
23
|
+
* - mode 'collect' → collected, no console output
|
|
24
|
+
* - mode 'warn' → collected; 'warn' → console.warn, 'error' → console.error,
|
|
25
|
+
* 'info' → collected only
|
|
26
|
+
* - mode 'error' → collected; 'warn'/'error' severities throw a
|
|
27
|
+
* DiagnosticError (err.diagnostic holds the Diagnostic);
|
|
28
|
+
* 'info' collected only. Called directly this throws
|
|
29
|
+
* synchronously; raised during a hook (i.e. from a
|
|
30
|
+
* check) it is rethrown asynchronously — see Hooks.
|
|
31
|
+
*
|
|
32
|
+
* `diagnostic.text` is formatted lazily (getter) on first read.
|
|
33
|
+
*
|
|
34
|
+
* formatDiagnostic(code, details): string
|
|
35
|
+
* → `[Sygnal SYG123] <Component>: <message>. <fix> <docsUrl>`
|
|
36
|
+
* (works in every mode; for call sites that must keep printing in 'off')
|
|
37
|
+
*
|
|
38
|
+
* getDiagnostics(): Diagnostic[] // copy of the collected list
|
|
39
|
+
* clearDiagnostics(): void
|
|
40
|
+
* onDiagnostic(cb: (d: Diagnostic) => void): () => void // returns unsubscribe
|
|
41
|
+
*
|
|
42
|
+
* --- Configuration --------------------------------------------------------
|
|
43
|
+
*
|
|
44
|
+
* configureDiagnostics({ mode?: DiagnosticsMode | undefined, ignore?: string[] }): void
|
|
45
|
+
* getDiagnosticsMode(): 'off' | 'collect' | 'warn' | 'error'
|
|
46
|
+
* isDiagnosticsEnabled(): boolean
|
|
47
|
+
*
|
|
48
|
+
* Mode resolution — first defined wins:
|
|
49
|
+
* 1. explicit mode: run(App, drivers, { diagnostics }) or configureDiagnostics({ mode })
|
|
50
|
+
* 2. globalThis.__SYGNAL_DEV__ (=== true → 'warn'; injected by the Vite plugin in serve)
|
|
51
|
+
* 3. 'off'
|
|
52
|
+
* The mode is resolved at module load, on every run() call and on every
|
|
53
|
+
* configureDiagnostics() call. `configureDiagnostics({ mode: undefined })`
|
|
54
|
+
* clears the explicit mode and falls back to steps 2-3.
|
|
55
|
+
* Every run() call is authoritative: it sets both the explicit mode and the
|
|
56
|
+
* ignore list from its `diagnostics` option, and run() WITHOUT the option
|
|
57
|
+
* resets them to the defaults (no explicit mode, empty ignore list), so a
|
|
58
|
+
* setting from an earlier run()/configureDiagnostics() does not leak in.
|
|
59
|
+
*
|
|
60
|
+
* --- Checks ---------------------------------------------------------------
|
|
61
|
+
*
|
|
62
|
+
* registerCheck(check: DiagnosticCheck): () => void // returns unregister
|
|
63
|
+
*
|
|
64
|
+
* interface DiagnosticCheck {
|
|
65
|
+
* id: string
|
|
66
|
+
* onIntent?(component, actionNames: string[], selectorsUsed: string[] | undefined): void
|
|
67
|
+
* onModel?(component, modelMap: Record<string, string[]>): void
|
|
68
|
+
* onRender?(component, rootVnode: any): void
|
|
69
|
+
* onReducer?(component, action: string, prevState: any, nextState: any, sinkName: string): void
|
|
70
|
+
* onDispose?(component): void
|
|
71
|
+
* }
|
|
72
|
+
*
|
|
73
|
+
* A check that throws (other than a DiagnosticError from 'error' mode) is
|
|
74
|
+
* isolated: the exception is reported as SYG900 and other checks still run.
|
|
75
|
+
*
|
|
76
|
+
* --- Hooks (called from src/component.ts, marked `// [diagnostics hook]`) ----
|
|
77
|
+
*
|
|
78
|
+
* onIntent(component, actionNames, selectorsUsed?)
|
|
79
|
+
* after intent is built. actionNames = keys of the intent object ([] for a
|
|
80
|
+
* single-stream intent). selectorsUsed is currently always undefined —
|
|
81
|
+
* TODO(1A): collect via MainDOMSource.select instrumentation in src/cycle/dom.
|
|
82
|
+
* onModel(component, modelMap)
|
|
83
|
+
* after the model is normalized; modelMap = action → sink names, shorthand
|
|
84
|
+
* ('A | SINK') expanded, plain functions mapped to the state sink,
|
|
85
|
+
* includes built-ins such as INITIALIZE.
|
|
86
|
+
* onRender(component, rootVnode)
|
|
87
|
+
* after each render of the component (fully injected vnode, before the DOM
|
|
88
|
+
* driver patches it; `rootVnode.elm` is populated after the patch, so DOM
|
|
89
|
+
* checks should defer, e.g. with a microtask/timeout).
|
|
90
|
+
* onReducer(component, action, prevState, nextState, sinkName)
|
|
91
|
+
* after each STATE reducer returns (not for ABORT, not when the reducer
|
|
92
|
+
* throws). nextState is the raw reducer return value (before calculated
|
|
93
|
+
* field cleanup). Currently only fired for the state sink; sinkName is the
|
|
94
|
+
* component's state source name.
|
|
95
|
+
* onDispose(component)
|
|
96
|
+
* at the start of component disposal.
|
|
97
|
+
*
|
|
98
|
+
* onSelector(domSource, selector) (additive, 1A)
|
|
99
|
+
* from MainDOMSource.select() in src/cycle/dom, for every CSS selector
|
|
100
|
+
* (not 'document' / 'body' / ':root'). domSource is the source select()
|
|
101
|
+
* was called on (its `.namespace` holds the isolation scopes and any
|
|
102
|
+
* earlier selectors; `._isolateModule` the DOM driver's isolate module).
|
|
103
|
+
* onBusEmit(type, emitterName?) (additive, 1A)
|
|
104
|
+
* from the EVENTS driver (src/extra/eventDriver.ts) for every bus event.
|
|
105
|
+
* onBusSelect(type) (additive, 1A)
|
|
106
|
+
* from EVENTS.select(type); type is the raw argument (string, string[]
|
|
107
|
+
* or undefined for "all events").
|
|
108
|
+
*
|
|
109
|
+
* Checks live in the separate 'sygnal/diagnostics' entry
|
|
110
|
+
* (src/extra/diagnostics/checks). That bundle reaches THIS module instance
|
|
111
|
+
* through globalThis.__SYGNAL_DIAGNOSTICS__ = { registerCheck, report }
|
|
112
|
+
* (set below), so it never carries a second copy of the core state.
|
|
113
|
+
*
|
|
114
|
+
* onIntent and onModel are each called exactly once per component instance,
|
|
115
|
+
* in that order, during construction (also when the component has no
|
|
116
|
+
* intent → actionNames [], or no model → modelMap {}).
|
|
117
|
+
*
|
|
118
|
+
* `component` is the internal Component instance (stable identity per
|
|
119
|
+
* instance; `.name` is the component name).
|
|
120
|
+
*
|
|
121
|
+
* Every hook starts with a single boolean check and returns immediately
|
|
122
|
+
* when the mode is 'off'.
|
|
123
|
+
*
|
|
124
|
+
* Hooks NEVER throw synchronously into Sygnal's stream pipeline (a throw
|
|
125
|
+
* there would kill the state/view stream for good). Every exception from a
|
|
126
|
+
* check is caught. In 'error' mode a DiagnosticError raised during a hook —
|
|
127
|
+
* by a check's report() call, or by the SYG900 report for a check that
|
|
128
|
+
* threw a plain Error — is rethrown ASYNCHRONOUSLY (queueMicrotask, falling
|
|
129
|
+
* back to setTimeout), so test runners still see an uncaught error while
|
|
130
|
+
* the app's streams keep running. The remaining checks still run.
|
|
131
|
+
* `_setAsyncThrow(fn?)` replaces the async rethrow (test seam; no argument
|
|
132
|
+
* restores the default; `_resetDiagnostics()` also restores it).
|
|
133
|
+
*
|
|
134
|
+
* (This comment is attached to the type-only import below so the TypeScript
|
|
135
|
+
* emit drops it — keeps it out of the published bundle and the size gate.)
|
|
136
|
+
*/
|
|
137
|
+
import type { DiagnosticSeverity } from './codes'
|
|
138
|
+
import { CODE_SEVERITY, docsUrlFor } from './codes'
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
export type DiagnosticsMode = 'off' | 'collect' | 'warn' | 'error'
|
|
142
|
+
|
|
143
|
+
export interface DiagnosticDetails {
|
|
144
|
+
component?: string | { name?: string }
|
|
145
|
+
message: string
|
|
146
|
+
fix?: string
|
|
147
|
+
data?: any
|
|
148
|
+
severity?: DiagnosticSeverity
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface Diagnostic {
|
|
152
|
+
code: string
|
|
153
|
+
severity: DiagnosticSeverity
|
|
154
|
+
component?: string
|
|
155
|
+
message: string
|
|
156
|
+
fix?: string
|
|
157
|
+
data?: any
|
|
158
|
+
docsUrl: string
|
|
159
|
+
text: string
|
|
160
|
+
timestamp: number
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface DiagnosticsOptions {
|
|
164
|
+
mode?: DiagnosticsMode
|
|
165
|
+
ignore?: string[]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface DiagnosticCheck {
|
|
169
|
+
id: string
|
|
170
|
+
onIntent?: (component: any, actionNames: string[], selectorsUsed: string[] | undefined) => void
|
|
171
|
+
onModel?: (component: any, modelMap: Record<string, string[]>) => void
|
|
172
|
+
onRender?: (component: any, rootVnode: any) => void
|
|
173
|
+
onReducer?: (component: any, action: string, prevState: any, nextState: any, sinkName: string) => void
|
|
174
|
+
onDispose?: (component: any) => void
|
|
175
|
+
onSelector?: (domSource: any, selector: string) => void
|
|
176
|
+
onBusEmit?: (type: string, emitterName?: string) => void
|
|
177
|
+
onBusSelect?: (type: string | string[] | undefined) => void
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export class DiagnosticError extends Error {
|
|
181
|
+
diagnostic: Diagnostic
|
|
182
|
+
constructor(diagnostic: Diagnostic) {
|
|
183
|
+
super(diagnostic.text)
|
|
184
|
+
this.diagnostic = diagnostic
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let explicitMode: DiagnosticsMode | undefined
|
|
189
|
+
let mode: DiagnosticsMode = 'off'
|
|
190
|
+
let enabled = false
|
|
191
|
+
let ignored = new Set<string>()
|
|
192
|
+
let collected: Diagnostic[] = []
|
|
193
|
+
let listeners: Array<(d: Diagnostic) => void> = []
|
|
194
|
+
let checks: DiagnosticCheck[] = []
|
|
195
|
+
|
|
196
|
+
export function resolveDiagnosticsMode(): DiagnosticsMode {
|
|
197
|
+
mode = explicitMode || ((globalThis as any).__SYGNAL_DEV__ === true ? 'warn' : 'off')
|
|
198
|
+
enabled = mode !== 'off'
|
|
199
|
+
return mode
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function configureDiagnostics(options: DiagnosticsOptions = {}): void {
|
|
203
|
+
if ('mode' in options) explicitMode = options.mode
|
|
204
|
+
if (options.ignore) ignored = new Set(options.ignore)
|
|
205
|
+
resolveDiagnosticsMode()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Internal: the explicit configuration (not the resolved mode), so it can be restored exactly. */
|
|
209
|
+
export function _getDiagnosticsConfig(): { mode: DiagnosticsMode | undefined, ignore: string[] } {
|
|
210
|
+
return { mode: explicitMode, ignore: [...ignored] }
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function getDiagnosticsMode(): DiagnosticsMode {
|
|
214
|
+
return mode
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function isDiagnosticsEnabled(): boolean {
|
|
218
|
+
return enabled
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const nameOf = (c: DiagnosticDetails['component']) => (c && typeof c === 'object' ? c.name : c) || undefined
|
|
222
|
+
const sentence = (s: string) => /[.!?]$/.test(s) ? s : s + '.'
|
|
223
|
+
|
|
224
|
+
export function formatDiagnostic(code: string, details: DiagnosticDetails): string {
|
|
225
|
+
const name = nameOf(details.component)
|
|
226
|
+
return `[Sygnal ${code}] ${name ? name + ': ' : ''}${sentence(details.message)}` +
|
|
227
|
+
(details.fix ? ' ' + sentence(details.fix) : '') + ' ' + docsUrlFor(code)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function report(code: string, details: DiagnosticDetails): Diagnostic | undefined {
|
|
231
|
+
if (!enabled || ignored.has(code)) return
|
|
232
|
+
const severity = details.severity || CODE_SEVERITY[code] || 'warn'
|
|
233
|
+
const d: Diagnostic = {
|
|
234
|
+
code,
|
|
235
|
+
severity,
|
|
236
|
+
component: nameOf(details.component),
|
|
237
|
+
message: details.message,
|
|
238
|
+
fix: details.fix,
|
|
239
|
+
data: details.data,
|
|
240
|
+
docsUrl: docsUrlFor(code),
|
|
241
|
+
get text() { return formatDiagnostic(code, this) },
|
|
242
|
+
timestamp: Date.now(),
|
|
243
|
+
}
|
|
244
|
+
if (collected.push(d) > 500) collected.shift()
|
|
245
|
+
listeners.forEach(cb => { try { cb(d) } catch (_) {} })
|
|
246
|
+
if (severity !== 'info') {
|
|
247
|
+
if (mode === 'error') throw new DiagnosticError(d)
|
|
248
|
+
if (mode === 'warn') console[severity](d.text)
|
|
249
|
+
}
|
|
250
|
+
return d
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function getDiagnostics(): Diagnostic[] {
|
|
254
|
+
return collected.slice()
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function clearDiagnostics(): void {
|
|
258
|
+
collected = []
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function onDiagnostic(cb: (d: Diagnostic) => void): () => void {
|
|
262
|
+
listeners = [...listeners, cb]
|
|
263
|
+
return () => { listeners = listeners.filter(l => l !== cb) }
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
export function registerCheck(check: DiagnosticCheck): () => void {
|
|
267
|
+
checks = [...checks, check]
|
|
268
|
+
return () => { checks = checks.filter(c => c !== check) }
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
type HookName = Exclude<keyof DiagnosticCheck, 'id'>
|
|
272
|
+
|
|
273
|
+
const defaultAsyncThrow = (err: any): void => {
|
|
274
|
+
const raise = () => { throw err }
|
|
275
|
+
typeof queueMicrotask === 'function' ? queueMicrotask(raise) : setTimeout(raise)
|
|
276
|
+
}
|
|
277
|
+
let asyncThrow = defaultAsyncThrow
|
|
278
|
+
|
|
279
|
+
/** Test seam: replace the async rethrow used for 'error'-mode diagnostics raised in hooks. */
|
|
280
|
+
export function _setAsyncThrow(fn?: (err: any) => void): void {
|
|
281
|
+
asyncThrow = fn || defaultAsyncThrow
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const hook = (name: HookName) => (component: any, ...args: any[]): void => {
|
|
285
|
+
if (!enabled) return
|
|
286
|
+
for (const check of checks) {
|
|
287
|
+
try {
|
|
288
|
+
(check[name] as any)?.(component, ...args)
|
|
289
|
+
} catch (err: any) {
|
|
290
|
+
// Never throw into the stream pipeline: 'error'-mode diagnostics (also a
|
|
291
|
+
// SYG900 escalated by 'error' mode) are rethrown asynchronously.
|
|
292
|
+
try {
|
|
293
|
+
if (err instanceof DiagnosticError) throw err
|
|
294
|
+
report('SYG900', {
|
|
295
|
+
component,
|
|
296
|
+
message: `Diagnostics check '${check.id}' threw in ${name}: ${err?.message}`,
|
|
297
|
+
data: { check: check.id, hook: name, error: err },
|
|
298
|
+
})
|
|
299
|
+
} catch (e) {
|
|
300
|
+
asyncThrow(e)
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export const onIntent: (component: any, actionNames: string[], selectorsUsed?: string[]) => void = hook('onIntent')
|
|
307
|
+
export const onModel: (component: any, modelMap: Record<string, string[]>) => void = hook('onModel')
|
|
308
|
+
export const onRender: (component: any, rootVnode: any) => void = hook('onRender')
|
|
309
|
+
export const onReducer: (component: any, action: string, prevState: any, nextState: any, sinkName: string) => void = hook('onReducer')
|
|
310
|
+
export const onDispose: (component: any) => void = hook('onDispose')
|
|
311
|
+
export const onSelector: (domSource: any, selector: string) => void = hook('onSelector')
|
|
312
|
+
export const onBusEmit: (type: string, emitterName?: string) => void = hook('onBusEmit')
|
|
313
|
+
export const onBusSelect: (type: string | string[] | undefined) => void = hook('onBusSelect')
|
|
314
|
+
|
|
315
|
+
// Bridge for the separately bundled 'sygnal/diagnostics' checks entry.
|
|
316
|
+
;(globalThis as any).__SYGNAL_DIAGNOSTICS__ = { registerCheck, report }
|
|
317
|
+
|
|
318
|
+
export function _resetDiagnostics(): void {
|
|
319
|
+
explicitMode = undefined
|
|
320
|
+
ignored = new Set()
|
|
321
|
+
collected = []
|
|
322
|
+
listeners = []
|
|
323
|
+
checks = []
|
|
324
|
+
asyncThrow = defaultAsyncThrow
|
|
325
|
+
resolveDiagnosticsMode()
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
resolveDiagnosticsMode()
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coded console output for Sygnal's pre-existing warnings and errors
|
|
3
|
+
* (PLAN-1 workstream 1E).
|
|
4
|
+
*
|
|
5
|
+
* warn(code, component, message, fix?, extra?)
|
|
6
|
+
* error(code, component, message, fix?, extra?)
|
|
7
|
+
* fail(code, component, message, fix?): never
|
|
8
|
+
*
|
|
9
|
+
* `component` is a component instance (anything with `.name`), a name string,
|
|
10
|
+
* or undefined. `extra` (an Error, the offending value, ...) is passed to the
|
|
11
|
+
* console after the formatted text, as the old messages did.
|
|
12
|
+
*
|
|
13
|
+
* Unlike report(), warn()/error() keep printing when diagnostics are 'off',
|
|
14
|
+
* so production apps see the same messages as before (now with a code):
|
|
15
|
+
* - 'off' → console.warn/error(formatDiagnostic(...), extra?)
|
|
16
|
+
* - 'collect' → report(): collected; error severity is also printed with
|
|
17
|
+
* console.error (so exceptions stay visible in tests), warn isn't
|
|
18
|
+
* - 'warn' → report(): collected and printed once by report(); `extra`
|
|
19
|
+
* (e.g. the caught Error with its stack) printed after it
|
|
20
|
+
* - 'error' → report() throws a DiagnosticError; it is rethrown
|
|
21
|
+
* asynchronously so the stream pipeline that hit the problem
|
|
22
|
+
* keeps running (same contract as the hooks in ./index)
|
|
23
|
+
* - code in the ignore list → nothing printed
|
|
24
|
+
*
|
|
25
|
+
* fail() always throws an Error whose message is the formatted text and whose
|
|
26
|
+
* `.code` is the diagnostic code. It does not go through report(): the throw
|
|
27
|
+
* itself is the signal, in every mode.
|
|
28
|
+
*
|
|
29
|
+
* (This comment is attached to the type-only import below so the TypeScript
|
|
30
|
+
* emit drops it.)
|
|
31
|
+
*/
|
|
32
|
+
import type { DiagnosticSeverity } from './codes'
|
|
33
|
+
import { report, formatDiagnostic, getDiagnosticsMode } from './index'
|
|
34
|
+
|
|
35
|
+
const emit = (severity: Extract<DiagnosticSeverity, 'warn' | 'error'>) =>
|
|
36
|
+
(code: string, component: any, message: string, fix?: string, ...extra: any[]): void => {
|
|
37
|
+
const details = { component, message, fix, severity, data: extra[0] }
|
|
38
|
+
const mode = getDiagnosticsMode()
|
|
39
|
+
if (mode === 'off') {
|
|
40
|
+
console[severity](formatDiagnostic(code, details), ...extra)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const d = report(code, details)
|
|
45
|
+
if (d && mode === 'collect' && severity === 'error') console.error(d.text, ...extra)
|
|
46
|
+
else if (d && mode === 'warn' && extra.length) console[severity](...extra)
|
|
47
|
+
} catch (err) {
|
|
48
|
+
setTimeout(() => { throw err })
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const warn = emit('warn')
|
|
53
|
+
export const error = emit('error')
|
|
54
|
+
|
|
55
|
+
export function fail(code: string, component: any, message: string, fix?: string): never {
|
|
56
|
+
const err: any = new Error(formatDiagnostic(code, { component, message, fix }))
|
|
57
|
+
err.code = code
|
|
58
|
+
throw err
|
|
59
|
+
}
|
|
@@ -8,10 +8,36 @@ interface DriverFromAsyncOptions {
|
|
|
8
8
|
post?: (val: any, incoming?: any) => any;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Create a driver from a promise-returning function.
|
|
13
|
+
*
|
|
14
|
+
* Each value the app sends to the driver calls the function (arguments picked
|
|
15
|
+
* by `args`, after `pre`). The resolved value, after `post`, is delivered as
|
|
16
|
+
* `{ [return]: value, [selector]: request[selector] }` (defaults:
|
|
17
|
+
* `{ value, category }`) and read with `source.select(category)`. A promise
|
|
18
|
+
* that resolves to `null`/`undefined` is delivered the same way.
|
|
19
|
+
*
|
|
20
|
+
* Errors: if the function's promise (or a promise returned by `post`)
|
|
21
|
+
* rejects, or `post` throws, the error is delivered on `source.errors()`,
|
|
22
|
+
* never on `select()`:
|
|
23
|
+
*
|
|
24
|
+
* ```js
|
|
25
|
+
* // payload: { error, category: request.category, request }
|
|
26
|
+
* intent: ({ QUOTE }) => ({
|
|
27
|
+
* GOT_QUOTE: QUOTE.select('quote'),
|
|
28
|
+
* QUOTE_ERROR: QUOTE.errors('quote'), // or .errors() / .errors(e => ...)
|
|
29
|
+
* })
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* `errors(selector?)` filters like `select()`: no argument = all errors, a
|
|
33
|
+
* string matches the request's selector property, a function is a predicate on
|
|
34
|
+
* the error payload. While nothing listens to `errors()`, failures are only
|
|
35
|
+
* logged with console.error (the pre-existing behavior).
|
|
36
|
+
*/
|
|
11
37
|
function driverFromAsync(
|
|
12
38
|
promiseReturningFunction: (...args: any[]) => Promise<any>,
|
|
13
39
|
opts: DriverFromAsyncOptions = {}
|
|
14
|
-
): (fromApp$: Stream<any>) => {select: (selector?: any) => Stream<any>} {
|
|
40
|
+
): (fromApp$: Stream<any>) => {select: (selector?: any) => Stream<any>; errors: (selector?: any) => Stream<any>} {
|
|
15
41
|
const {
|
|
16
42
|
selector: selectorProperty = 'category',
|
|
17
43
|
args: functionArgs = 'value',
|
|
@@ -48,6 +74,19 @@ function driverFromAsync(
|
|
|
48
74
|
stop: () => {},
|
|
49
75
|
});
|
|
50
76
|
|
|
77
|
+
// Active errors(selector) streams (1H-6): an error goes to the ones it matches, and is
|
|
78
|
+
// logged when none matches.
|
|
79
|
+
const errorSubs = new Set<{listener: any; selector: any}>();
|
|
80
|
+
const matches = (selector: any, val: any) =>
|
|
81
|
+
selector === undefined ||
|
|
82
|
+
(typeof selector === 'function' ? selector(val) : val?.[selectorProperty] === selector);
|
|
83
|
+
const filterBy = (stream: Stream<any>, selector?: any) =>
|
|
84
|
+
selector === undefined ? stream : stream.filter((val: any) => matches(selector, val));
|
|
85
|
+
|
|
86
|
+
// 3E/R11: set by the source's dispose(), which Cycle's engine calls on teardown just
|
|
87
|
+
// before it completes the sink proxies; that completion is expected, not worth a warning.
|
|
88
|
+
let disposing = false;
|
|
89
|
+
|
|
51
90
|
fromApp$.addListener({
|
|
52
91
|
next: (incoming: any) => {
|
|
53
92
|
const preProcessed = preFunction(incoming);
|
|
@@ -65,61 +104,59 @@ function driverFromAsync(
|
|
|
65
104
|
}
|
|
66
105
|
}
|
|
67
106
|
const errMsg = `Error in driver created using driverFromAsync(${functionName})`;
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (typeof outgoing === 'object' && outgoing !== null) {
|
|
75
|
-
outgoing[selectorProperty] = incoming[selectorProperty];
|
|
76
|
-
} else {
|
|
77
|
-
console.warn(
|
|
78
|
-
`The 'return' option for driverFromAsync(${functionName}) was not set, but the promise returned an non-object. The result will be returned as-is, but the '${selectorProperty}' property will not be set, so will not be filtered by the 'select' method of the driver.`
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
} else if (typeof returnProperty === 'string') {
|
|
82
|
-
outgoing = {
|
|
83
|
-
[returnProperty]: rawVal,
|
|
84
|
-
[selectorProperty]: incoming[selectorProperty],
|
|
85
|
-
};
|
|
86
|
-
} else {
|
|
87
|
-
throw new Error(
|
|
88
|
-
`The 'return' option for driverFromAsync(${functionName}) must be a string. Received ${typeof returnProperty}`
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
|
-
return outgoing;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
if (typeof innerVal.then === 'function') {
|
|
95
|
-
innerVal
|
|
96
|
-
.then((innerOutgoing: any) => {
|
|
97
|
-
const processedOutgoing = postFunction(innerOutgoing, incoming);
|
|
98
|
-
if (typeof processedOutgoing.then === 'function') {
|
|
99
|
-
processedOutgoing
|
|
100
|
-
.then((innerProcessedOutgoing: any) => {
|
|
101
|
-
sendFn!(constructReply(innerProcessedOutgoing));
|
|
102
|
-
})
|
|
103
|
-
.catch((err: any) => console.error(`${errMsg}: ${err}`));
|
|
104
|
-
} else {
|
|
105
|
-
sendFn!(constructReply(processedOutgoing));
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
|
-
.catch((err: any) => console.error(`${errMsg}: ${err}`));
|
|
107
|
+
const constructReply = (rawVal: any) => {
|
|
108
|
+
let outgoing: any;
|
|
109
|
+
if (returnProperty === undefined) {
|
|
110
|
+
outgoing = rawVal;
|
|
111
|
+
if (typeof outgoing === 'object' && outgoing !== null) {
|
|
112
|
+
outgoing[selectorProperty] = incoming[selectorProperty];
|
|
109
113
|
} else {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
.then((innerProcessedOutgoing: any) => {
|
|
114
|
-
sendFn!(constructReply(innerProcessedOutgoing));
|
|
115
|
-
})
|
|
116
|
-
.catch((err: any) => console.error(`${errMsg}: ${err}`));
|
|
117
|
-
} else {
|
|
118
|
-
sendFn!(constructReply(processedOutgoing));
|
|
119
|
-
}
|
|
114
|
+
console.warn(
|
|
115
|
+
`The 'return' option for driverFromAsync(${functionName}) was not set, but the promise returned an non-object. The result will be returned as-is, but the '${selectorProperty}' property will not be set, so will not be filtered by the 'select' method of the driver.`
|
|
116
|
+
);
|
|
120
117
|
}
|
|
121
|
-
})
|
|
122
|
-
|
|
118
|
+
} else if (typeof returnProperty === 'string') {
|
|
119
|
+
outgoing = {
|
|
120
|
+
[returnProperty]: rawVal,
|
|
121
|
+
[selectorProperty]: incoming[selectorProperty],
|
|
122
|
+
};
|
|
123
|
+
} else {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`The 'return' option for driverFromAsync(${functionName}) must be a string. Received ${typeof returnProperty}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return outgoing;
|
|
129
|
+
};
|
|
130
|
+
// Rejections (of the function, a thenable it resolves to, or post()) go to errors();
|
|
131
|
+
// an error no active errors() selector matches is logged, as before.
|
|
132
|
+
const reportError = (err: any) => {
|
|
133
|
+
const val = {error: err, [selectorProperty]: incoming?.[selectorProperty], request: incoming};
|
|
134
|
+
let handled = false;
|
|
135
|
+
errorSubs.forEach(sub => {
|
|
136
|
+
let hit = false;
|
|
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}`);
|
|
144
|
+
};
|
|
145
|
+
const isThenable = (val: any) => val != null && typeof val.then === 'function';
|
|
146
|
+
promiseReturningFunction(...argArr)
|
|
147
|
+
.then((innerVal: any) =>
|
|
148
|
+
isThenable(innerVal)
|
|
149
|
+
? innerVal.then((innerOutgoing: any) => postFunction(innerOutgoing, incoming))
|
|
150
|
+
: postFunction(innerVal, incoming)
|
|
151
|
+
)
|
|
152
|
+
.then(constructReply)
|
|
153
|
+
.then((reply: any) => {
|
|
154
|
+
try {
|
|
155
|
+
sendFn!(reply);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
console.error(`${errMsg}: ${err}`);
|
|
158
|
+
}
|
|
159
|
+
}, reportError);
|
|
123
160
|
},
|
|
124
161
|
error: (err: any) => {
|
|
125
162
|
console.error(
|
|
@@ -128,6 +165,7 @@ function driverFromAsync(
|
|
|
128
165
|
);
|
|
129
166
|
},
|
|
130
167
|
complete: () => {
|
|
168
|
+
if (disposing) return;
|
|
131
169
|
console.warn(
|
|
132
170
|
`Unexpected completion of sink stream to driver created using driverFromAsync(${functionName})`
|
|
133
171
|
);
|
|
@@ -135,10 +173,20 @@ function driverFromAsync(
|
|
|
135
173
|
});
|
|
136
174
|
|
|
137
175
|
return {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
176
|
+
dispose: () => {
|
|
177
|
+
disposing = true;
|
|
178
|
+
},
|
|
179
|
+
select: (selector?: any) => filterBy(toApp$, selector),
|
|
180
|
+
errors: (selector?: any) => {
|
|
181
|
+
let sub: any;
|
|
182
|
+
return xs.create<any>({
|
|
183
|
+
start: (listener) => {
|
|
184
|
+
errorSubs.add((sub = {listener, selector}));
|
|
185
|
+
},
|
|
186
|
+
stop: () => {
|
|
187
|
+
errorSubs.delete(sub);
|
|
188
|
+
},
|
|
189
|
+
});
|
|
142
190
|
},
|
|
143
191
|
};
|
|
144
192
|
};
|
package/src/extra/eventDriver.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import xs, {Stream} from 'xstream';
|
|
2
2
|
import {adapt} from '../cycle/run/adapt';
|
|
3
|
+
// [diagnostics hook] no-op when diagnostics are off
|
|
4
|
+
import {onBusEmit, onBusSelect} from './diagnostics/index';
|
|
3
5
|
|
|
4
6
|
export interface EventBusSource {
|
|
5
7
|
select(type?: string | string[]): any;
|
|
@@ -15,6 +17,7 @@ export default function eventBusDriver(out$: Stream<BusEvent>): EventBusSource {
|
|
|
15
17
|
|
|
16
18
|
out$.subscribe({
|
|
17
19
|
next: (event: BusEvent) => {
|
|
20
|
+
onBusEmit(event && event.type, (event as any)?.__emitterName);
|
|
18
21
|
events.dispatchEvent(new CustomEvent('data', {detail: event}));
|
|
19
22
|
if (typeof window !== 'undefined' && (window as any).__SYGNAL_DEVTOOLS__?.connected) {
|
|
20
23
|
(window as any).__SYGNAL_DEVTOOLS__.onBusEvent(event);
|
|
@@ -26,6 +29,7 @@ export default function eventBusDriver(out$: Stream<BusEvent>): EventBusSource {
|
|
|
26
29
|
|
|
27
30
|
return {
|
|
28
31
|
select: (type?: string | string[]) => {
|
|
32
|
+
onBusSelect(type);
|
|
29
33
|
const all = !type;
|
|
30
34
|
const _type = Array.isArray(type) ? type : [type];
|
|
31
35
|
let cb: ((e: Event) => void) | undefined;
|