qdadm 1.13.0 → 1.19.2
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/package.json +8 -4
- package/src/chain/ActiveStack.ts +79 -98
- package/src/chain/StackHydrator.ts +3 -2
- package/src/chain/index.ts +7 -1
- package/src/components/QdadmRoot.vue +52 -0
- package/src/components/edit/FormActions.vue +9 -6
- package/src/components/edit/LookupPickerDialog.vue +6 -3
- package/src/components/index.ts +6 -0
- package/src/composables/useEntityItemFormPage.ts +1 -0
- package/src/composables/useEntityItemShowPage.ts +1 -0
- package/src/composables/useFieldManager.ts +100 -3
- package/src/composables/useListPage.ts +50 -59
- package/src/composables/useListPage.utils.ts +101 -0
- package/src/composables/useNavigation.ts +26 -3
- package/src/composables/useOptionsLookup.ts +5 -1
- package/src/gen/generateManagers.test.js +27 -0
- package/src/gen/generateManagers.ts +12 -0
- package/src/hooks/HookRegistry.ts +14 -435
- package/src/i18n/I18n.ts +344 -0
- package/src/i18n/IncrementalDomainProvider.ts +153 -0
- package/src/i18n/InlineTranslationProvider.ts +4 -0
- package/src/i18n/LazyTranslationProvider.ts +102 -0
- package/src/i18n/MessagesRegistry.ts +4 -0
- package/src/i18n/Resolver.ts +4 -0
- package/src/i18n/__tests__/I18n.test.ts +169 -0
- package/src/i18n/__tests__/IncrementalDomainProvider.test.ts +146 -0
- package/src/i18n/__tests__/LazyTranslationProvider.test.ts +100 -0
- package/src/i18n/__tests__/Resolver.test.ts +271 -0
- package/src/i18n/defaults/DefaultCoreProvider.ts +28 -0
- package/src/i18n/defaults/core.en.yml +55 -0
- package/src/i18n/defaults/core.fr.yml +55 -0
- package/src/i18n/index.ts +55 -0
- package/src/i18n/loaders/raw-modules.d.ts +15 -0
- package/src/i18n/loaders/yaml.ts +35 -0
- package/src/i18n/strategies.ts +4 -0
- package/src/i18n/types.ts +34 -0
- package/src/i18n/useI18n.ts +34 -0
- package/src/index.ts +37 -0
- package/src/kernel/EventRouter.ts +17 -300
- package/src/kernel/Kernel.i18n.ts +29 -0
- package/src/kernel/Kernel.modules.ts +6 -0
- package/src/kernel/Kernel.registries.ts +10 -2
- package/src/kernel/Kernel.routing.ts +43 -1
- package/src/kernel/Kernel.ts +43 -0
- package/src/kernel/Kernel.types.ts +52 -1
- package/src/kernel/Kernel.vue.ts +121 -15
- package/src/kernel/KernelContext.entities.ts +80 -0
- package/src/kernel/KernelContext.events.ts +57 -0
- package/src/kernel/KernelContext.i18n.ts +37 -0
- package/src/kernel/KernelContext.permissions.ts +38 -0
- package/src/kernel/KernelContext.routing.ts +280 -0
- package/src/kernel/KernelContext.ts +125 -834
- package/src/kernel/KernelContext.types.ts +173 -0
- package/src/kernel/KernelContext.zones.ts +54 -0
- package/src/kernel/SSEBridge.ts +7 -362
- package/src/kernel/SignalBus.ts +24 -148
- package/src/modules/debug/AuthCollector.ts +48 -1
- package/src/modules/debug/Collector.ts +16 -302
- package/src/modules/debug/DebugBridge.ts +10 -171
- package/src/modules/debug/DebugModule.ts +35 -5
- package/src/modules/debug/EntitiesCollector.ts +97 -1
- package/src/modules/debug/ErrorCollector.ts +2 -77
- package/src/modules/debug/I18nCollector.ts +9 -0
- package/src/modules/debug/LocalStorageAdapter.ts +3 -147
- package/src/modules/debug/RouterCollector.ts +101 -1
- package/src/modules/debug/SignalCollector.ts +2 -150
- package/src/modules/debug/ToastCollector.ts +2 -91
- package/src/modules/debug/ZonesCollector.ts +93 -1
- package/src/modules/debug/components/DebugBar.vue +19 -775
- package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
- package/src/modules/debug/components/index.ts +4 -3
- package/src/modules/debug/components/panels/AuthPanel.vue +1 -1
- package/src/modules/debug/components/panels/EntitiesPanel.vue +5 -5
- package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
- package/src/modules/debug/components/panels/RouterPanel.vue +1 -1
- package/src/modules/debug/components/panels/index.ts +10 -4
- package/src/modules/debug/index.ts +15 -0
- package/src/modules/debug/styles.scss +22 -18
- package/src/utils/index.ts +0 -3
- package/src/vite/qdadmDebugPlugin.ts +401 -0
- package/src/vite-env.d.ts +16 -0
- package/src/modules/debug/components/ObjectTree.vue +0 -123
- package/src/modules/debug/components/panels/EntriesPanel.vue +0 -100
- package/src/modules/debug/components/panels/SignalsPanel.vue +0 -188
- package/src/modules/debug/components/panels/ToastsPanel.vue +0 -45
- package/src/utils/debugInjector.ts +0 -306
|
@@ -11,7 +11,14 @@
|
|
|
11
11
|
* collector.install(ctx)
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
Collector,
|
|
16
|
+
type CollectorContext,
|
|
17
|
+
type CollectorEntry,
|
|
18
|
+
type CollectorManifest,
|
|
19
|
+
type CollectorOptions,
|
|
20
|
+
type CollectorSnapshot,
|
|
21
|
+
} from './Collector'
|
|
15
22
|
import { computeSemanticBreadcrumb } from '../../composables/useSemanticBreadcrumb'
|
|
16
23
|
import type { Router, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
|
|
17
24
|
import type { ActiveStack, StackLevel } from '../../chain/ActiveStack'
|
|
@@ -111,6 +118,8 @@ export class RouterCollector extends Collector<NavigationEntry> {
|
|
|
111
118
|
private _guardInstalled: boolean = false
|
|
112
119
|
private _activeStack: ActiveStack | null = null
|
|
113
120
|
private _stackHydrator: StackHydrator | null = null
|
|
121
|
+
private _removeHydratedListener: (() => void) | null = null
|
|
122
|
+
private _removeStackChangeListener: (() => void) | null = null
|
|
114
123
|
|
|
115
124
|
constructor(options: RouterCollectorOptions = {}) {
|
|
116
125
|
super(options)
|
|
@@ -135,6 +144,20 @@ export class RouterCollector extends Collector<NavigationEntry> {
|
|
|
135
144
|
this._activeStack = routerCtx.activeStack ?? null
|
|
136
145
|
this._stackHydrator = routerCtx.stackHydrator ?? null
|
|
137
146
|
// Router guard will be installed lazily when router becomes available
|
|
147
|
+
|
|
148
|
+
// Re-render the panel when the active stack changes or finishes hydrating —
|
|
149
|
+
// otherwise level.label stays on its loading placeholder until the next
|
|
150
|
+
// navigation triggers reactivity. afterEach (the navigation guard) only
|
|
151
|
+
// fires when the route changes, not when the async hydrator resolves.
|
|
152
|
+
const signals = ctx.signals
|
|
153
|
+
if (signals) {
|
|
154
|
+
this._removeStackChangeListener = signals.on('stack:change', () => {
|
|
155
|
+
this.notifyChange()
|
|
156
|
+
})
|
|
157
|
+
this._removeHydratedListener = signals.on('stack:hydrated', () => {
|
|
158
|
+
this.notifyChange()
|
|
159
|
+
})
|
|
160
|
+
}
|
|
138
161
|
}
|
|
139
162
|
|
|
140
163
|
/**
|
|
@@ -213,6 +236,14 @@ export class RouterCollector extends Collector<NavigationEntry> {
|
|
|
213
236
|
this._removeGuard()
|
|
214
237
|
this._removeGuard = null
|
|
215
238
|
}
|
|
239
|
+
if (this._removeStackChangeListener) {
|
|
240
|
+
this._removeStackChangeListener()
|
|
241
|
+
this._removeStackChangeListener = null
|
|
242
|
+
}
|
|
243
|
+
if (this._removeHydratedListener) {
|
|
244
|
+
this._removeHydratedListener()
|
|
245
|
+
this._removeHydratedListener = null
|
|
246
|
+
}
|
|
216
247
|
this._guardInstalled = false
|
|
217
248
|
this._ctx = null
|
|
218
249
|
this._history = []
|
|
@@ -346,4 +377,73 @@ export class RouterCollector extends Collector<NavigationEntry> {
|
|
|
346
377
|
if (!this._router) return
|
|
347
378
|
await this._router.push(path)
|
|
348
379
|
}
|
|
380
|
+
|
|
381
|
+
override describe(): CollectorManifest {
|
|
382
|
+
return {
|
|
383
|
+
name: this.name,
|
|
384
|
+
records: true,
|
|
385
|
+
summary:
|
|
386
|
+
'Captures navigations (afterEach) and exposes the current route, registered routes and the live ActiveStack.',
|
|
387
|
+
entryShape: {
|
|
388
|
+
id: 'number',
|
|
389
|
+
to: 'SerializedRoute',
|
|
390
|
+
from: 'SerializedRoute?',
|
|
391
|
+
seen: 'boolean',
|
|
392
|
+
timestamp: 'number',
|
|
393
|
+
},
|
|
394
|
+
stateShape: {
|
|
395
|
+
currentRoute: 'SerializedRoute?',
|
|
396
|
+
routes: 'RouteListEntry[]',
|
|
397
|
+
breadcrumb: 'unknown[]',
|
|
398
|
+
activeStack: 'ActiveStackInfo?',
|
|
399
|
+
},
|
|
400
|
+
actions: [
|
|
401
|
+
...this._builtinActionManifests(),
|
|
402
|
+
{
|
|
403
|
+
name: 'navigate',
|
|
404
|
+
summary: 'Push a path onto the router (router.push).',
|
|
405
|
+
args: { path: 'string' },
|
|
406
|
+
mutates: true,
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
name: 'getCurrentRoute',
|
|
410
|
+
summary: 'Return the serialized current route.',
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
name: 'getRoutes',
|
|
414
|
+
summary: 'List every registered route.',
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: 'getBreadcrumb',
|
|
418
|
+
summary: 'Return the semantic breadcrumb for the current route.',
|
|
419
|
+
},
|
|
420
|
+
],
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
override snapshot(): CollectorSnapshot {
|
|
425
|
+
return {
|
|
426
|
+
name: this.name,
|
|
427
|
+
entries: this._history.map((e) => ({ ...e })),
|
|
428
|
+
count: this._history.length,
|
|
429
|
+
unseen: this.getBadge(),
|
|
430
|
+
state: {
|
|
431
|
+
currentRoute: this.getCurrentRoute(),
|
|
432
|
+
routes: this.getRoutes(),
|
|
433
|
+
breadcrumb: this.getBreadcrumb(),
|
|
434
|
+
activeStack: this.getActiveStack(),
|
|
435
|
+
},
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
override async call(actionName: string, args: Record<string, unknown> = {}): Promise<unknown> {
|
|
440
|
+
if (actionName === 'navigate') {
|
|
441
|
+
await this.navigate(String(args.path ?? ''))
|
|
442
|
+
return { ok: true }
|
|
443
|
+
}
|
|
444
|
+
if (actionName === 'getCurrentRoute') return this.getCurrentRoute()
|
|
445
|
+
if (actionName === 'getRoutes') return this.getRoutes()
|
|
446
|
+
if (actionName === 'getBreadcrumb') return this.getBreadcrumb()
|
|
447
|
+
return super.call(actionName, args)
|
|
448
|
+
}
|
|
349
449
|
}
|
|
@@ -1,152 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* SignalCollector
|
|
3
|
-
*
|
|
4
|
-
* Extends the base Collector to capture all signals emitted through the SignalBus.
|
|
5
|
-
* Uses wildcard subscription to capture all domain:action events.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* const collector = new SignalCollector({ maxEntries: 50 })
|
|
9
|
-
* collector.install(ctx) // ctx.signals is the SignalBus
|
|
10
|
-
*
|
|
11
|
-
* // Later, retrieve captured signals
|
|
12
|
-
* collector.getEntries() // [{ name, data, source, timestamp }, ...]
|
|
2
|
+
* SignalCollector — qdadm re-export from @quazardous/qddebug.
|
|
13
3
|
*/
|
|
14
|
-
|
|
15
|
-
import { Collector, type CollectorContext, type CollectorEntry } from './Collector'
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Signal entry type
|
|
19
|
-
*/
|
|
20
|
-
export interface SignalEntry extends CollectorEntry {
|
|
21
|
-
name: string
|
|
22
|
-
data: unknown
|
|
23
|
-
source: string | null
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Collector for SignalBus events
|
|
28
|
-
*
|
|
29
|
-
* Records all signals with their name, data, and source for debugging.
|
|
30
|
-
* Uses the `**` wildcard pattern to capture all signals including
|
|
31
|
-
* multi-segment names (e.g., entity:data-invalidate, auth:impersonate:start).
|
|
32
|
-
*/
|
|
33
|
-
export class SignalCollector extends Collector<SignalEntry> {
|
|
34
|
-
/**
|
|
35
|
-
* Collector name for identification
|
|
36
|
-
*/
|
|
37
|
-
static override collectorName = 'signals'
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Signals to skip recording (internal kernel signals with non-serializable data)
|
|
41
|
-
* @private
|
|
42
|
-
*/
|
|
43
|
-
private static _skipSignals = new Set(['kernel:ready', 'kernel:shutdown'])
|
|
44
|
-
|
|
45
|
-
private _unsubscribe: (() => void) | null = null
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Internal install - subscribe to all signals
|
|
49
|
-
* @protected
|
|
50
|
-
*/
|
|
51
|
-
protected override _doInstall(ctx: CollectorContext): void {
|
|
52
|
-
if (!ctx?.signals) {
|
|
53
|
-
console.warn('[SignalCollector] No signals bus found in context')
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Subscribe to all signals using wildcard pattern
|
|
58
|
-
// QuarKernel supports wildcards with the configured delimiter (:)
|
|
59
|
-
// '**' matches all signals including multi-segment (entity:data-invalidate)
|
|
60
|
-
this._unsubscribe = ctx.signals.on('**', (event) => {
|
|
61
|
-
// Skip internal signals with non-serializable data (kernel, orchestrator)
|
|
62
|
-
if (SignalCollector._skipSignals.has(event.name)) {
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Sanitize data to avoid cyclic references
|
|
67
|
-
const data = this._sanitizeData(event.data)
|
|
68
|
-
|
|
69
|
-
this.record({
|
|
70
|
-
name: event.name,
|
|
71
|
-
data,
|
|
72
|
-
source: (data as Record<string, unknown>)?.source as string ?? null
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Sanitize event data to remove non-serializable objects
|
|
79
|
-
*
|
|
80
|
-
* Handles cases where data contains references to Kernel, Orchestrator,
|
|
81
|
-
* or other complex objects that would cause cyclic reference errors.
|
|
82
|
-
*
|
|
83
|
-
* @param data - Raw event data
|
|
84
|
-
* @returns Sanitized data safe for recording
|
|
85
|
-
* @private
|
|
86
|
-
*/
|
|
87
|
-
private _sanitizeData(data: unknown): unknown {
|
|
88
|
-
if (data === null || data === undefined) return data
|
|
89
|
-
if (typeof data !== 'object') return data
|
|
90
|
-
|
|
91
|
-
// Try to create a simple clone, fallback to description if cyclic
|
|
92
|
-
try {
|
|
93
|
-
const obj = data as Record<string, unknown>
|
|
94
|
-
// Quick check for obviously problematic properties
|
|
95
|
-
if (obj.kernel || obj.orchestrator || obj._kernel) {
|
|
96
|
-
// Extract only safe properties
|
|
97
|
-
const safe: Record<string, unknown> = {}
|
|
98
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
99
|
-
if (key !== 'kernel' && key !== 'orchestrator' && key !== '_kernel') {
|
|
100
|
-
if (typeof value !== 'object' || value === null) {
|
|
101
|
-
safe[key] = value
|
|
102
|
-
} else if (Array.isArray(value)) {
|
|
103
|
-
safe[key] = `[Array(${value.length})]`
|
|
104
|
-
} else {
|
|
105
|
-
safe[key] = '[Object]'
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return safe
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// For other objects, try JSON roundtrip to detect cycles
|
|
113
|
-
JSON.stringify(data)
|
|
114
|
-
return data
|
|
115
|
-
} catch {
|
|
116
|
-
// If serialization fails, return a safe representation
|
|
117
|
-
return { _type: 'unserializable', keys: Object.keys(data as object) }
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Internal uninstall - cleanup subscription
|
|
123
|
-
* @protected
|
|
124
|
-
*/
|
|
125
|
-
protected override _doUninstall(): void {
|
|
126
|
-
if (this._unsubscribe) {
|
|
127
|
-
this._unsubscribe()
|
|
128
|
-
this._unsubscribe = null
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Get entries filtered by signal name pattern
|
|
134
|
-
*
|
|
135
|
-
* @param pattern - Pattern to match signal names
|
|
136
|
-
* @returns Filtered entries
|
|
137
|
-
*/
|
|
138
|
-
getByPattern(pattern: string | RegExp): SignalEntry[] {
|
|
139
|
-
const regex = pattern instanceof RegExp ? pattern : new RegExp(pattern)
|
|
140
|
-
return this.entries.filter((entry) => regex.test(entry.name))
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Get entries for a specific signal domain
|
|
145
|
-
*
|
|
146
|
-
* @param domain - Domain prefix (e.g., 'entity', 'auth', 'books')
|
|
147
|
-
* @returns Entries matching the domain
|
|
148
|
-
*/
|
|
149
|
-
getByDomain(domain: string): SignalEntry[] {
|
|
150
|
-
return this.entries.filter((entry) => entry.name.startsWith(`${domain}:`))
|
|
151
|
-
}
|
|
152
|
-
}
|
|
4
|
+
export { SignalCollector, type SignalEntry } from '@quazardous/qddebug'
|
|
@@ -1,93 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ToastCollector
|
|
3
|
-
*
|
|
4
|
-
* This collector listens to toast signals emitted on the signal bus
|
|
5
|
-
* and records them for display in the debug panel.
|
|
6
|
-
*
|
|
7
|
-
* Toast signals should follow the pattern:
|
|
8
|
-
* - signals.emit('toast:success', { summary: '...', detail: '...' })
|
|
9
|
-
* - signals.emit('toast:error', { summary: '...', detail: '...' })
|
|
10
|
-
* - signals.emit('toast:info', { summary: '...', detail: '...' })
|
|
11
|
-
* - signals.emit('toast:warn', { summary: '...', detail: '...' })
|
|
12
|
-
*
|
|
13
|
-
* Use with ToastBridge module which handles displaying toasts via PrimeVue.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* const collector = new ToastCollector()
|
|
17
|
-
* collector.install(ctx)
|
|
18
|
-
* // Toasts emitted via signals are now automatically recorded
|
|
2
|
+
* ToastCollector — qdadm re-export from @quazardous/qddebug.
|
|
19
3
|
*/
|
|
20
|
-
|
|
21
|
-
import { Collector, type CollectorContext, type CollectorEntry } from './Collector'
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Toast entry type
|
|
25
|
-
*/
|
|
26
|
-
export interface ToastEntry extends CollectorEntry {
|
|
27
|
-
severity: string
|
|
28
|
-
summary?: string
|
|
29
|
-
detail?: string
|
|
30
|
-
life?: number
|
|
31
|
-
emitter: string
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Collector for toast notifications
|
|
36
|
-
*/
|
|
37
|
-
export class ToastCollector extends Collector<ToastEntry> {
|
|
38
|
-
/**
|
|
39
|
-
* Collector name identifier
|
|
40
|
-
*/
|
|
41
|
-
static override collectorName = 'toasts'
|
|
42
|
-
|
|
43
|
-
private _unsubscribe: (() => void) | null = null
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Internal install - subscribe to toast signals
|
|
47
|
-
* @protected
|
|
48
|
-
*/
|
|
49
|
-
protected override _doInstall(ctx: CollectorContext): void {
|
|
50
|
-
// Listen for toast signals on the signal bus
|
|
51
|
-
if (ctx?.signals) {
|
|
52
|
-
this._unsubscribe = ctx.signals.on('toast:*', (event) => {
|
|
53
|
-
const data = event.data as { summary?: string; detail?: string; life?: number; emitter?: string } | undefined
|
|
54
|
-
this.record({
|
|
55
|
-
severity: event.name.split(':')[1] ?? 'info', // toast:success -> success
|
|
56
|
-
summary: data?.summary,
|
|
57
|
-
detail: data?.detail,
|
|
58
|
-
life: data?.life,
|
|
59
|
-
emitter: data?.emitter ?? 'unknown'
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
} else {
|
|
63
|
-
console.warn('[ToastCollector] No signals bus in context - toast recording disabled')
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Internal uninstall - cleanup
|
|
69
|
-
* @protected
|
|
70
|
-
*/
|
|
71
|
-
protected override _doUninstall(): void {
|
|
72
|
-
if (this._unsubscribe) {
|
|
73
|
-
this._unsubscribe()
|
|
74
|
-
this._unsubscribe = null
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Get entries by severity
|
|
80
|
-
* @param severity - Severity level (success, info, warn, error)
|
|
81
|
-
* @returns Filtered entries
|
|
82
|
-
*/
|
|
83
|
-
getBySeverity(severity: string): ToastEntry[] {
|
|
84
|
-
return this.entries.filter((entry) => entry.severity === severity)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Get error toasts count for badge
|
|
89
|
-
*/
|
|
90
|
-
getErrorCount(): number {
|
|
91
|
-
return this.entries.filter((e) => e.severity === 'error').length
|
|
92
|
-
}
|
|
93
|
-
}
|
|
4
|
+
export { ToastCollector, type ToastEntry } from '@quazardous/qddebug'
|
|
@@ -15,7 +15,14 @@
|
|
|
15
15
|
* collector.getZoneInfo() // { zones: [...], totalBlocks: n }
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
Collector,
|
|
20
|
+
type CollectorContext,
|
|
21
|
+
type CollectorEntry,
|
|
22
|
+
type CollectorManifest,
|
|
23
|
+
type CollectorOptions,
|
|
24
|
+
type CollectorSnapshot,
|
|
25
|
+
} from './Collector'
|
|
19
26
|
import type { ZoneRegistry } from '../../zones/ZoneRegistry'
|
|
20
27
|
import type { Router } from 'vue-router'
|
|
21
28
|
|
|
@@ -352,4 +359,89 @@ export class ZonesCollector extends Collector<ZoneEntry> {
|
|
|
352
359
|
return true
|
|
353
360
|
}
|
|
354
361
|
}
|
|
362
|
+
|
|
363
|
+
override describe(): CollectorManifest {
|
|
364
|
+
return {
|
|
365
|
+
name: this.name,
|
|
366
|
+
records: false,
|
|
367
|
+
summary:
|
|
368
|
+
'Lists every zone defined in the running app and the blocks/wrappers registered in each. Reflects current page (filterable).',
|
|
369
|
+
entryShape: {
|
|
370
|
+
name: 'string',
|
|
371
|
+
isOnPage: 'boolean',
|
|
372
|
+
hasDefault: 'boolean',
|
|
373
|
+
defaultName: 'string?',
|
|
374
|
+
blocksCount: 'number',
|
|
375
|
+
blocks: 'ZoneBlock[]',
|
|
376
|
+
},
|
|
377
|
+
stateShape: {
|
|
378
|
+
showCurrentPageOnly: 'boolean',
|
|
379
|
+
showInternalZones: 'boolean',
|
|
380
|
+
highlightedZone: 'string?',
|
|
381
|
+
},
|
|
382
|
+
actions: [
|
|
383
|
+
...this._builtinActionManifests(),
|
|
384
|
+
{
|
|
385
|
+
name: 'highlight',
|
|
386
|
+
summary: 'Draw an overlay on the page around a zone (browser-only).',
|
|
387
|
+
args: { name: 'string' },
|
|
388
|
+
mutates: true,
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
name: 'clearHighlights',
|
|
392
|
+
summary: 'Remove every zone overlay.',
|
|
393
|
+
mutates: true,
|
|
394
|
+
},
|
|
395
|
+
{
|
|
396
|
+
name: 'setShowCurrentPageOnly',
|
|
397
|
+
summary: 'Filter zones to those rendered on the current page.',
|
|
398
|
+
args: { value: 'boolean' },
|
|
399
|
+
mutates: true,
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: 'setShowInternalZones',
|
|
403
|
+
summary: 'Show internal zones (prefixed with _).',
|
|
404
|
+
args: { value: 'boolean' },
|
|
405
|
+
mutates: true,
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
override snapshot(): CollectorSnapshot {
|
|
412
|
+
const entries = this.getEntries(true)
|
|
413
|
+
return {
|
|
414
|
+
name: this.name,
|
|
415
|
+
entries,
|
|
416
|
+
count: entries.length,
|
|
417
|
+
unseen: 0,
|
|
418
|
+
state: {
|
|
419
|
+
showCurrentPageOnly: this._showCurrentPageOnly,
|
|
420
|
+
showInternalZones: this._showInternalZones,
|
|
421
|
+
highlightedZone: this._highlightedZone,
|
|
422
|
+
},
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
override async call(actionName: string, args: Record<string, unknown> = {}): Promise<unknown> {
|
|
427
|
+
if (actionName === 'highlight') {
|
|
428
|
+
this.highlightZone(String(args.name ?? ''))
|
|
429
|
+
return { ok: true }
|
|
430
|
+
}
|
|
431
|
+
if (actionName === 'clearHighlights') {
|
|
432
|
+
this.clearHighlights()
|
|
433
|
+
return { ok: true }
|
|
434
|
+
}
|
|
435
|
+
if (actionName === 'setShowCurrentPageOnly') {
|
|
436
|
+
this._showCurrentPageOnly = Boolean(args.value)
|
|
437
|
+
this.notifyChange()
|
|
438
|
+
return { ok: true, value: this._showCurrentPageOnly }
|
|
439
|
+
}
|
|
440
|
+
if (actionName === 'setShowInternalZones') {
|
|
441
|
+
this._showInternalZones = Boolean(args.value)
|
|
442
|
+
this.notifyChange()
|
|
443
|
+
return { ok: true, value: this._showInternalZones }
|
|
444
|
+
}
|
|
445
|
+
return super.call(actionName, args)
|
|
446
|
+
}
|
|
355
447
|
}
|