@quazardous/qdadm 1.19.3
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/LICENSE +21 -0
- package/README.md +111 -0
- package/package.json +101 -0
- package/src/assets/logo.svg +6 -0
- package/src/auth/SessionAuthAdapter.ts +459 -0
- package/src/auth/index.ts +15 -0
- package/src/chain/ActiveStack.ts +154 -0
- package/src/chain/StackHydrator.ts +292 -0
- package/src/chain/index.ts +25 -0
- package/src/chain/useActiveStack.ts +120 -0
- package/src/chain/useStackHydrator.ts +155 -0
- package/src/components/BoolCell.vue +34 -0
- package/src/components/InfoBanner.vue +73 -0
- package/src/components/QdadmRoot.vue +52 -0
- package/src/components/SeverityTag.vue +86 -0
- package/src/components/dialogs/BulkStatusDialog.vue +60 -0
- package/src/components/dialogs/MultiStepDialog.vue +331 -0
- package/src/components/dialogs/SimpleDialog.vue +122 -0
- package/src/components/dialogs/UnsavedChangesDialog.vue +80 -0
- package/src/components/display/BannerZone.vue +65 -0
- package/src/components/display/CardsGrid.vue +72 -0
- package/src/components/display/CopyableId.vue +84 -0
- package/src/components/display/EmptyState.vue +62 -0
- package/src/components/display/IntensityBar.vue +190 -0
- package/src/components/display/RichCardsGrid.vue +101 -0
- package/src/components/edit/FormActions.vue +78 -0
- package/src/components/edit/FormField.vue +82 -0
- package/src/components/edit/FormInput.vue +158 -0
- package/src/components/edit/FormPage.vue +260 -0
- package/src/components/edit/FormTab.vue +217 -0
- package/src/components/edit/FormTabs.vue +120 -0
- package/src/components/edit/LookupField.vue +406 -0
- package/src/components/edit/LookupPickerDialog.vue +230 -0
- package/src/components/editors/JsonEditorFoldable.vue +470 -0
- package/src/components/editors/JsonStructuredField.vue +264 -0
- package/src/components/editors/JsonViewer.vue +51 -0
- package/src/components/editors/KeyValueEditor.vue +314 -0
- package/src/components/editors/LanguageEditor.vue +259 -0
- package/src/components/editors/PermissionEditor.vue +558 -0
- package/src/components/editors/ScopeEditor.vue +372 -0
- package/src/components/editors/VanillaJsonEditor.vue +183 -0
- package/src/components/index.ts +83 -0
- package/src/components/item/FieldGroups.vue +392 -0
- package/src/components/layout/AppLayout.vue +1032 -0
- package/src/components/layout/BaseLayout.vue +190 -0
- package/src/components/layout/Breadcrumb.vue +55 -0
- package/src/components/layout/DashboardLayout.vue +104 -0
- package/src/components/layout/FormLayout.vue +256 -0
- package/src/components/layout/ListLayout.vue +278 -0
- package/src/components/layout/PageHeader.vue +156 -0
- package/src/components/layout/PageLayout.vue +126 -0
- package/src/components/layout/PageNav.vue +192 -0
- package/src/components/layout/SidebarBox.vue +87 -0
- package/src/components/layout/Zone.vue +182 -0
- package/src/components/layout/defaults/DefaultBreadcrumb.vue +149 -0
- package/src/components/layout/defaults/DefaultFooter.vue +89 -0
- package/src/components/layout/defaults/DefaultFormActions.vue +67 -0
- package/src/components/layout/defaults/DefaultHeader.vue +78 -0
- package/src/components/layout/defaults/DefaultMenu.vue +188 -0
- package/src/components/layout/defaults/DefaultPagination.vue +75 -0
- package/src/components/layout/defaults/DefaultTable.vue +137 -0
- package/src/components/layout/defaults/DefaultUserInfo.vue +111 -0
- package/src/components/layout/defaults/index.ts +16 -0
- package/src/components/lists/ActionButtons.vue +49 -0
- package/src/components/lists/ActionColumn.vue +40 -0
- package/src/components/lists/FilterBar.vue +54 -0
- package/src/components/lists/ListPage.vue +467 -0
- package/src/components/pages/LoginPage.vue +334 -0
- package/src/components/pages/NotFoundPage.vue +98 -0
- package/src/components/show/ShowDisplay.vue +343 -0
- package/src/components/show/ShowField.vue +67 -0
- package/src/components/show/ShowPage.vue +431 -0
- package/src/composables/index.ts +155 -0
- package/src/composables/useApp.ts +67 -0
- package/src/composables/useAuth.ts +98 -0
- package/src/composables/useBareForm.ts +319 -0
- package/src/composables/useBreadcrumb.ts +252 -0
- package/src/composables/useChildPage.ts +113 -0
- package/src/composables/useCurrentEntity.ts +60 -0
- package/src/composables/useDeferred.ts +94 -0
- package/src/composables/useDirtyState.ts +138 -0
- package/src/composables/useEntityItemFormPage.ts +942 -0
- package/src/composables/useEntityItemFormPage.types.ts +451 -0
- package/src/composables/useEntityItemPage.ts +438 -0
- package/src/composables/useEntityItemShowPage.ts +722 -0
- package/src/composables/useFieldManager.ts +692 -0
- package/src/composables/useForm.ts +544 -0
- package/src/composables/useGuardStore.ts +53 -0
- package/src/composables/useHooks.ts +56 -0
- package/src/composables/useInfoBanner.ts +217 -0
- package/src/composables/useJsonSyntax.ts +122 -0
- package/src/composables/useLayoutResolver.ts +329 -0
- package/src/composables/useListPage.ts +1535 -0
- package/src/composables/useListPage.types.ts +437 -0
- package/src/composables/useListPage.utils.ts +101 -0
- package/src/composables/useNavContext.ts +439 -0
- package/src/composables/useNavigation.ts +216 -0
- package/src/composables/useOptionsLookup.ts +323 -0
- package/src/composables/usePageTitle.ts +84 -0
- package/src/composables/useSSEBridge.ts +142 -0
- package/src/composables/useSemanticBreadcrumb.ts +222 -0
- package/src/composables/useSidebarState.ts +24 -0
- package/src/composables/useSignals.ts +52 -0
- package/src/composables/useUnsavedChangesGuard.ts +155 -0
- package/src/composables/useUserImpersonator.ts +317 -0
- package/src/composables/useZoneRegistry.ts +252 -0
- package/src/core/bundles.ts +558 -0
- package/src/core/decorator.ts +420 -0
- package/src/core/extension.ts +470 -0
- package/src/core/index.ts +59 -0
- package/src/deferred/DeferredRegistry.ts +353 -0
- package/src/deferred/index.ts +15 -0
- package/src/editors/index.ts +12 -0
- package/src/entity/EntityManager.cache.ts +530 -0
- package/src/entity/EntityManager.crud.ts +615 -0
- package/src/entity/EntityManager.interface.ts +78 -0
- package/src/entity/EntityManager.query.ts +221 -0
- package/src/entity/EntityManager.relations.ts +151 -0
- package/src/entity/EntityManager.ts +944 -0
- package/src/entity/EntityManager.types.ts +288 -0
- package/src/entity/auth/CompositeAuthAdapter.ts +194 -0
- package/src/entity/auth/EntityAuthAdapter.ts +119 -0
- package/src/entity/auth/PermissiveAdapter.ts +43 -0
- package/src/entity/auth/RoleHierarchy.ts +128 -0
- package/src/entity/auth/SecurityChecker.ts +202 -0
- package/src/entity/auth/factory.ts +203 -0
- package/src/entity/auth/index.ts +47 -0
- package/src/entity/factory.test.js +189 -0
- package/src/entity/factory.ts +146 -0
- package/src/entity/index.ts +49 -0
- package/src/entity/storage/ApiStorage.ts +228 -0
- package/src/entity/storage/IStorage.ts +100 -0
- package/src/entity/storage/LocalStorage.ts +200 -0
- package/src/entity/storage/MemoryStorage.ts +184 -0
- package/src/entity/storage/MockApiStorage.ts +318 -0
- package/src/entity/storage/SdkStorage.ts +377 -0
- package/src/entity/storage/factory.test.js +159 -0
- package/src/entity/storage/factory.ts +209 -0
- package/src/entity/storage/index.ts +75 -0
- package/src/gen/FieldMapper.ts +122 -0
- package/src/gen/StorageProfileFactory.ts +93 -0
- package/src/gen/connectors/BaseConnector.ts +150 -0
- package/src/gen/connectors/ManualConnector.test.js +499 -0
- package/src/gen/connectors/ManualConnector.ts +414 -0
- package/src/gen/connectors/OpenAPIConnector.test.js +836 -0
- package/src/gen/connectors/OpenAPIConnector.ts +693 -0
- package/src/gen/connectors/__fixtures__/sample-openapi.json +311 -0
- package/src/gen/connectors/index.ts +27 -0
- package/src/gen/createManagers.ts +245 -0
- package/src/gen/decorators.ts +134 -0
- package/src/gen/generateManagers.test.js +540 -0
- package/src/gen/generateManagers.ts +398 -0
- package/src/gen/index.ts +66 -0
- package/src/gen/schema.ts +248 -0
- package/src/gen/vite-plugin.ts +114 -0
- package/src/generated/managers/testManager.js +45 -0
- package/src/generated/managers/testManager.ts +46 -0
- package/src/hooks/HookRegistry.ts +16 -0
- package/src/hooks/index.ts +15 -0
- 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 +453 -0
- package/src/kernel/EventRouter.ts +23 -0
- package/src/kernel/Kernel.api.ts +79 -0
- package/src/kernel/Kernel.auth.ts +120 -0
- package/src/kernel/Kernel.i18n.ts +29 -0
- package/src/kernel/Kernel.modules.ts +170 -0
- package/src/kernel/Kernel.registries.ts +237 -0
- package/src/kernel/Kernel.routing.ts +334 -0
- package/src/kernel/Kernel.ts +460 -0
- package/src/kernel/Kernel.types.ts +263 -0
- package/src/kernel/Kernel.vue.ts +304 -0
- 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 +252 -0
- package/src/kernel/KernelContext.types.ts +173 -0
- package/src/kernel/KernelContext.zones.ts +54 -0
- package/src/kernel/Module.ts +169 -0
- package/src/kernel/ModuleLoader.ts +645 -0
- package/src/kernel/SSEBridge.ts +11 -0
- package/src/kernel/SignalBus.ts +48 -0
- package/src/kernel/index.ts +56 -0
- package/src/module/index.ts +33 -0
- package/src/module/moduleRegistry.ts +492 -0
- package/src/modules/debug/AuthCollector.ts +501 -0
- package/src/modules/debug/Collector.ts +20 -0
- package/src/modules/debug/DebugBridge.ts +14 -0
- package/src/modules/debug/DebugModule.ts +220 -0
- package/src/modules/debug/EntitiesCollector.ts +839 -0
- package/src/modules/debug/ErrorCollector.ts +4 -0
- package/src/modules/debug/I18nCollector.ts +9 -0
- package/src/modules/debug/LocalStorageAdapter.ts +7 -0
- package/src/modules/debug/RouterCollector.ts +449 -0
- package/src/modules/debug/SignalCollector.ts +4 -0
- package/src/modules/debug/ToastCollector.ts +4 -0
- package/src/modules/debug/ZonesCollector.ts +447 -0
- package/src/modules/debug/components/DebugBar.vue +31 -0
- package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
- package/src/modules/debug/components/index.ts +9 -0
- package/src/modules/debug/components/panels/AuthPanel.vue +267 -0
- package/src/modules/debug/components/panels/EntitiesPanel.vue +572 -0
- package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
- package/src/modules/debug/components/panels/RouterPanel.vue +440 -0
- package/src/modules/debug/components/panels/ZonesPanel.vue +133 -0
- package/src/modules/debug/components/panels/index.ts +16 -0
- package/src/modules/debug/index.ts +58 -0
- package/src/modules/debug/styles.scss +2671 -0
- package/src/notifications/NotificationBadge.vue +35 -0
- package/src/notifications/NotificationListener.vue +71 -0
- package/src/notifications/NotificationModule.ts +76 -0
- package/src/notifications/NotificationPanel.vue +478 -0
- package/src/notifications/NotificationStore.ts +288 -0
- package/src/notifications/index.ts +42 -0
- package/src/notifications/styles.scss +32 -0
- package/src/orchestrator/Orchestrator.ts +427 -0
- package/src/orchestrator/index.ts +8 -0
- package/src/orchestrator/useOrchestrator.ts +80 -0
- package/src/plugin.ts +251 -0
- package/src/query/FilterQuery.ts +306 -0
- package/src/query/QueryExecutor.ts +341 -0
- package/src/query/index.ts +16 -0
- package/src/security/EntityRolesProvider.ts +385 -0
- package/src/security/PermissionMatcher.ts +152 -0
- package/src/security/PermissionRegistry.ts +273 -0
- package/src/security/PersistableRolesProvider.ts +584 -0
- package/src/security/RolesManager.ts +136 -0
- package/src/security/RolesProvider.ts +167 -0
- package/src/security/RolesProviderStorage.ts +211 -0
- package/src/security/SecurityModule.ts +94 -0
- package/src/security/StaticRolesProvider.ts +117 -0
- package/src/security/UsersManager.ts +163 -0
- package/src/security/index.ts +77 -0
- package/src/security/pages/RoleForm.vue +276 -0
- package/src/security/pages/RoleList.vue +123 -0
- package/src/styles/_alerts.scss +95 -0
- package/src/styles/_buttons.scss +391 -0
- package/src/styles/_cards.scss +241 -0
- package/src/styles/_code.scss +78 -0
- package/src/styles/_dialogs.scss +55 -0
- package/src/styles/_filter-bar.scss +70 -0
- package/src/styles/_forms.scss +434 -0
- package/src/styles/_helpers.scss +153 -0
- package/src/styles/_lists.scss +225 -0
- package/src/styles/_main.scss +356 -0
- package/src/styles/_markdown.scss +88 -0
- package/src/styles/_responsive.scss +255 -0
- package/src/styles/_show-pages.scss +186 -0
- package/src/styles/_sidebar.scss +380 -0
- package/src/styles/_states.scss +115 -0
- package/src/styles/_stats.scss +47 -0
- package/src/styles/_variables.scss +224 -0
- package/src/styles/index.scss +38 -0
- package/src/styles/theme/_components.scss +331 -0
- package/src/styles/theme/_index.scss +10 -0
- package/src/styles/theme/_tokens.scss +147 -0
- package/src/styles/theme/_utilities.scss +175 -0
- package/src/toast/ToastBridgeModule.ts +68 -0
- package/src/toast/ToastListener.vue +55 -0
- package/src/toast/index.ts +26 -0
- package/src/toast/useSignalToast.ts +154 -0
- package/src/types/index.ts +285 -0
- package/src/types/pluralize.d.ts +19 -0
- package/src/types/quarkernel.d.ts +50 -0
- package/src/utils/formatters.ts +174 -0
- package/src/utils/index.ts +35 -0
- package/src/utils/transformers.ts +131 -0
- package/src/vite/qdadmDebugPlugin.ts +401 -0
- package/src/vite-env.d.ts +50 -0
- package/src/zones/ZoneRegistry.ts +934 -0
- package/src/zones/index.ts +29 -0
- package/src/zones/zones.ts +191 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RouterCollector - Debug collector for Vue Router state
|
|
3
|
+
*
|
|
4
|
+
* Displays current routing information:
|
|
5
|
+
* - Current route (name, path, params, query, meta)
|
|
6
|
+
* - Navigation history (recent route changes)
|
|
7
|
+
* - All registered routes
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* const collector = new RouterCollector()
|
|
11
|
+
* collector.install(ctx)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
Collector,
|
|
16
|
+
type CollectorContext,
|
|
17
|
+
type CollectorEntry,
|
|
18
|
+
type CollectorManifest,
|
|
19
|
+
type CollectorOptions,
|
|
20
|
+
type CollectorSnapshot,
|
|
21
|
+
} from './Collector'
|
|
22
|
+
import { computeSemanticBreadcrumb } from '../../composables/useSemanticBreadcrumb'
|
|
23
|
+
import type { Router, RouteLocationNormalized, RouteRecordNormalized } from 'vue-router'
|
|
24
|
+
import type { ActiveStack, StackLevel } from '../../chain/ActiveStack'
|
|
25
|
+
import type { StackHydrator, HydratedLevel } from '../../chain/StackHydrator'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Serialized route info
|
|
29
|
+
*/
|
|
30
|
+
export interface SerializedRoute {
|
|
31
|
+
name: string
|
|
32
|
+
path: string
|
|
33
|
+
fullPath: string
|
|
34
|
+
params: Record<string, string | string[]>
|
|
35
|
+
query: Record<string, string | string[] | null>
|
|
36
|
+
hash: string
|
|
37
|
+
meta: Record<string, unknown>
|
|
38
|
+
matched: Array<{
|
|
39
|
+
name: string | null
|
|
40
|
+
path: string
|
|
41
|
+
meta: Record<string, unknown>
|
|
42
|
+
}>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Navigation history entry
|
|
47
|
+
*/
|
|
48
|
+
export interface NavigationEntry extends CollectorEntry {
|
|
49
|
+
id: number
|
|
50
|
+
to: SerializedRoute
|
|
51
|
+
from: SerializedRoute | null
|
|
52
|
+
seen: boolean
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Route list entry
|
|
57
|
+
*/
|
|
58
|
+
export interface RouteListEntry {
|
|
59
|
+
name: string
|
|
60
|
+
path: string
|
|
61
|
+
meta: Record<string, unknown>
|
|
62
|
+
hasComponent: boolean
|
|
63
|
+
children: number
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Active stack debug info
|
|
68
|
+
*/
|
|
69
|
+
export interface ActiveStackInfo {
|
|
70
|
+
levels: Array<{
|
|
71
|
+
entity: string
|
|
72
|
+
param: string
|
|
73
|
+
foreignKey: string | null
|
|
74
|
+
id: string | null
|
|
75
|
+
hydrated: boolean
|
|
76
|
+
loading: boolean
|
|
77
|
+
label: string | null
|
|
78
|
+
hasData: boolean
|
|
79
|
+
}>
|
|
80
|
+
depth: number
|
|
81
|
+
current: StackLevel | null
|
|
82
|
+
parent: StackLevel | null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* RouterCollector options
|
|
87
|
+
*/
|
|
88
|
+
export interface RouterCollectorOptions extends CollectorOptions {
|
|
89
|
+
maxHistory?: number
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Extended context for router collector
|
|
94
|
+
*/
|
|
95
|
+
interface RouterCollectorContext extends CollectorContext {
|
|
96
|
+
router?: Router
|
|
97
|
+
activeStack?: ActiveStack
|
|
98
|
+
stackHydrator?: StackHydrator
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Collector for Vue Router state visualization
|
|
103
|
+
*/
|
|
104
|
+
export class RouterCollector extends Collector<NavigationEntry> {
|
|
105
|
+
/**
|
|
106
|
+
* Collector name identifier
|
|
107
|
+
*/
|
|
108
|
+
static override collectorName = 'router'
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* This collector records navigation events
|
|
112
|
+
*/
|
|
113
|
+
static override records = true
|
|
114
|
+
|
|
115
|
+
private _history: NavigationEntry[] = []
|
|
116
|
+
private _maxHistory: number
|
|
117
|
+
private _removeGuard: (() => void) | null = null
|
|
118
|
+
private _guardInstalled: boolean = false
|
|
119
|
+
private _activeStack: ActiveStack | null = null
|
|
120
|
+
private _stackHydrator: StackHydrator | null = null
|
|
121
|
+
private _removeHydratedListener: (() => void) | null = null
|
|
122
|
+
private _removeStackChangeListener: (() => void) | null = null
|
|
123
|
+
|
|
124
|
+
constructor(options: RouterCollectorOptions = {}) {
|
|
125
|
+
super(options)
|
|
126
|
+
this._maxHistory = options.maxHistory ?? 20
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get router lazily (router might not exist at install time)
|
|
131
|
+
* @private
|
|
132
|
+
*/
|
|
133
|
+
private get _router(): Router | undefined {
|
|
134
|
+
return (this._ctx as RouterCollectorContext)?.router
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Internal install - store context (router is accessed lazily)
|
|
139
|
+
* @protected
|
|
140
|
+
*/
|
|
141
|
+
protected override _doInstall(ctx: CollectorContext): void {
|
|
142
|
+
this._ctx = ctx
|
|
143
|
+
const routerCtx = ctx as RouterCollectorContext
|
|
144
|
+
this._activeStack = routerCtx.activeStack ?? null
|
|
145
|
+
this._stackHydrator = routerCtx.stackHydrator ?? null
|
|
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
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Ensure router guard is installed (called lazily)
|
|
165
|
+
* @private
|
|
166
|
+
*/
|
|
167
|
+
private _ensureGuard(): void {
|
|
168
|
+
if (this._guardInstalled || !this._router) return
|
|
169
|
+
|
|
170
|
+
// Track navigation with afterEach
|
|
171
|
+
this._removeGuard = this._router.afterEach((to, from) => {
|
|
172
|
+
this._addNavigation(to, from)
|
|
173
|
+
})
|
|
174
|
+
this._guardInstalled = true
|
|
175
|
+
|
|
176
|
+
// Add initial route if not on home
|
|
177
|
+
const current = this._router.currentRoute?.value
|
|
178
|
+
if (current && current.path !== '/') {
|
|
179
|
+
this._history.unshift({
|
|
180
|
+
timestamp: Date.now(),
|
|
181
|
+
id: Date.now(),
|
|
182
|
+
to: this._serializeRoute(current),
|
|
183
|
+
from: null,
|
|
184
|
+
seen: false
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Add navigation event to history
|
|
191
|
+
* @private
|
|
192
|
+
*/
|
|
193
|
+
private _addNavigation(to: RouteLocationNormalized, from: RouteLocationNormalized): void {
|
|
194
|
+
const entry: NavigationEntry = {
|
|
195
|
+
timestamp: Date.now(),
|
|
196
|
+
id: Date.now(),
|
|
197
|
+
to: this._serializeRoute(to),
|
|
198
|
+
from: from?.name ? this._serializeRoute(from) : null,
|
|
199
|
+
seen: false
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Create new array reference for Vue reactivity
|
|
203
|
+
this._history = [entry, ...this._history.slice(0, this._maxHistory - 1)]
|
|
204
|
+
|
|
205
|
+
this.notifyChange()
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Serialize route to plain object
|
|
210
|
+
* @private
|
|
211
|
+
*/
|
|
212
|
+
private _serializeRoute(route: RouteLocationNormalized): SerializedRoute {
|
|
213
|
+
return {
|
|
214
|
+
name: (route.name as string) || '(unnamed)',
|
|
215
|
+
path: route.path,
|
|
216
|
+
fullPath: route.fullPath,
|
|
217
|
+
params: { ...route.params } as Record<string, string | string[]>,
|
|
218
|
+
query: { ...route.query } as Record<string, string | string[] | null>,
|
|
219
|
+
hash: route.hash,
|
|
220
|
+
meta: { ...route.meta },
|
|
221
|
+
// Matched routes with their individual metas
|
|
222
|
+
matched: route.matched?.map(m => ({
|
|
223
|
+
name: (m.name as string) || null,
|
|
224
|
+
path: m.path,
|
|
225
|
+
meta: { ...m.meta }
|
|
226
|
+
})) ?? []
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Internal uninstall - cleanup
|
|
232
|
+
* @protected
|
|
233
|
+
*/
|
|
234
|
+
protected override _doUninstall(): void {
|
|
235
|
+
if (this._removeGuard) {
|
|
236
|
+
this._removeGuard()
|
|
237
|
+
this._removeGuard = null
|
|
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
|
+
}
|
|
247
|
+
this._guardInstalled = false
|
|
248
|
+
this._ctx = null
|
|
249
|
+
this._history = []
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get badge - count of unseen navigations
|
|
254
|
+
*/
|
|
255
|
+
override getBadge(): number {
|
|
256
|
+
return this._history.filter(h => !h.seen).length
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Mark all history as seen
|
|
261
|
+
*/
|
|
262
|
+
markSeen(): void {
|
|
263
|
+
for (const item of this._history) {
|
|
264
|
+
item.seen = true
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get current route info
|
|
270
|
+
*/
|
|
271
|
+
getCurrentRoute(): SerializedRoute | null {
|
|
272
|
+
this._ensureGuard()
|
|
273
|
+
const router = this._router
|
|
274
|
+
if (!router) return null
|
|
275
|
+
const current = router.currentRoute?.value
|
|
276
|
+
if (!current) return null
|
|
277
|
+
return this._serializeRoute(current)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Get navigation history
|
|
282
|
+
*/
|
|
283
|
+
getHistory(): NavigationEntry[] {
|
|
284
|
+
return this._history
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get all registered routes
|
|
289
|
+
*/
|
|
290
|
+
getRoutes(): RouteListEntry[] {
|
|
291
|
+
this._ensureGuard()
|
|
292
|
+
if (!this._router) return []
|
|
293
|
+
|
|
294
|
+
const routes = this._router.getRoutes()
|
|
295
|
+
return routes.map((route: RouteRecordNormalized) => ({
|
|
296
|
+
name: (route.name as string) || '(unnamed)',
|
|
297
|
+
path: route.path,
|
|
298
|
+
meta: { ...route.meta },
|
|
299
|
+
hasComponent: !!route.components?.default,
|
|
300
|
+
children: route.children?.length || 0
|
|
301
|
+
})).sort((a, b) => a.path.localeCompare(b.path))
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Get computed breadcrumb for current route
|
|
306
|
+
* Returns semantic objects per level - adapters resolve labels/paths
|
|
307
|
+
*
|
|
308
|
+
* Uses shared computeSemanticBreadcrumb function from composables.
|
|
309
|
+
*
|
|
310
|
+
* Kinds:
|
|
311
|
+
* - route: Generic route (e.g., home)
|
|
312
|
+
* - entity-list: Entity collection (e.g., /books)
|
|
313
|
+
* - entity-show: Entity instance view
|
|
314
|
+
* - entity-edit: Entity instance edit
|
|
315
|
+
* - entity-create: Entity creation
|
|
316
|
+
*/
|
|
317
|
+
getBreadcrumb(): unknown[] {
|
|
318
|
+
this._ensureGuard()
|
|
319
|
+
const current = this._router?.currentRoute?.value
|
|
320
|
+
if (!current) return []
|
|
321
|
+
|
|
322
|
+
return computeSemanticBreadcrumb(current.path, this._router!.getRoutes(), current)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Get entries for display (implements Collector interface)
|
|
327
|
+
*/
|
|
328
|
+
override getEntries(): NavigationEntry[] {
|
|
329
|
+
return this._history
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Clear navigation history
|
|
334
|
+
*/
|
|
335
|
+
override clear(): void {
|
|
336
|
+
this._history = []
|
|
337
|
+
this.notifyChange()
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Get activeStack state for debugging
|
|
342
|
+
* Combines sync stack (config) with hydrator (async data)
|
|
343
|
+
*/
|
|
344
|
+
getActiveStack(): ActiveStackInfo | null {
|
|
345
|
+
if (!this._activeStack) return null
|
|
346
|
+
|
|
347
|
+
const syncLevels = this._activeStack.getLevels()
|
|
348
|
+
const hydratorLevels = this._stackHydrator?.getLevels() ?? []
|
|
349
|
+
|
|
350
|
+
return {
|
|
351
|
+
levels: syncLevels.map((level, idx) => {
|
|
352
|
+
const hydrated = hydratorLevels[idx] as HydratedLevel | undefined
|
|
353
|
+
return {
|
|
354
|
+
// Sync (from ActiveStack)
|
|
355
|
+
entity: level.entity,
|
|
356
|
+
param: level.param,
|
|
357
|
+
foreignKey: level.foreignKey,
|
|
358
|
+
id: level.id,
|
|
359
|
+
// Async (from StackHydrator)
|
|
360
|
+
hydrated: hydrated?.hydrated ?? false,
|
|
361
|
+
loading: hydrated?.loading ?? false,
|
|
362
|
+
label: hydrated?.label ?? null,
|
|
363
|
+
hasData: !!hydrated?.data,
|
|
364
|
+
}
|
|
365
|
+
}),
|
|
366
|
+
depth: syncLevels.length,
|
|
367
|
+
current: this._activeStack.getCurrent(),
|
|
368
|
+
parent: this._activeStack.getParent(),
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Navigate to a path
|
|
374
|
+
* @param path - Path to navigate to
|
|
375
|
+
*/
|
|
376
|
+
async navigate(path: string): Promise<void> {
|
|
377
|
+
if (!this._router) return
|
|
378
|
+
await this._router.push(path)
|
|
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
|
+
}
|
|
449
|
+
}
|