@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.
Files changed (291) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/package.json +101 -0
  4. package/src/assets/logo.svg +6 -0
  5. package/src/auth/SessionAuthAdapter.ts +459 -0
  6. package/src/auth/index.ts +15 -0
  7. package/src/chain/ActiveStack.ts +154 -0
  8. package/src/chain/StackHydrator.ts +292 -0
  9. package/src/chain/index.ts +25 -0
  10. package/src/chain/useActiveStack.ts +120 -0
  11. package/src/chain/useStackHydrator.ts +155 -0
  12. package/src/components/BoolCell.vue +34 -0
  13. package/src/components/InfoBanner.vue +73 -0
  14. package/src/components/QdadmRoot.vue +52 -0
  15. package/src/components/SeverityTag.vue +86 -0
  16. package/src/components/dialogs/BulkStatusDialog.vue +60 -0
  17. package/src/components/dialogs/MultiStepDialog.vue +331 -0
  18. package/src/components/dialogs/SimpleDialog.vue +122 -0
  19. package/src/components/dialogs/UnsavedChangesDialog.vue +80 -0
  20. package/src/components/display/BannerZone.vue +65 -0
  21. package/src/components/display/CardsGrid.vue +72 -0
  22. package/src/components/display/CopyableId.vue +84 -0
  23. package/src/components/display/EmptyState.vue +62 -0
  24. package/src/components/display/IntensityBar.vue +190 -0
  25. package/src/components/display/RichCardsGrid.vue +101 -0
  26. package/src/components/edit/FormActions.vue +78 -0
  27. package/src/components/edit/FormField.vue +82 -0
  28. package/src/components/edit/FormInput.vue +158 -0
  29. package/src/components/edit/FormPage.vue +260 -0
  30. package/src/components/edit/FormTab.vue +217 -0
  31. package/src/components/edit/FormTabs.vue +120 -0
  32. package/src/components/edit/LookupField.vue +406 -0
  33. package/src/components/edit/LookupPickerDialog.vue +230 -0
  34. package/src/components/editors/JsonEditorFoldable.vue +470 -0
  35. package/src/components/editors/JsonStructuredField.vue +264 -0
  36. package/src/components/editors/JsonViewer.vue +51 -0
  37. package/src/components/editors/KeyValueEditor.vue +314 -0
  38. package/src/components/editors/LanguageEditor.vue +259 -0
  39. package/src/components/editors/PermissionEditor.vue +558 -0
  40. package/src/components/editors/ScopeEditor.vue +372 -0
  41. package/src/components/editors/VanillaJsonEditor.vue +183 -0
  42. package/src/components/index.ts +83 -0
  43. package/src/components/item/FieldGroups.vue +392 -0
  44. package/src/components/layout/AppLayout.vue +1032 -0
  45. package/src/components/layout/BaseLayout.vue +190 -0
  46. package/src/components/layout/Breadcrumb.vue +55 -0
  47. package/src/components/layout/DashboardLayout.vue +104 -0
  48. package/src/components/layout/FormLayout.vue +256 -0
  49. package/src/components/layout/ListLayout.vue +278 -0
  50. package/src/components/layout/PageHeader.vue +156 -0
  51. package/src/components/layout/PageLayout.vue +126 -0
  52. package/src/components/layout/PageNav.vue +192 -0
  53. package/src/components/layout/SidebarBox.vue +87 -0
  54. package/src/components/layout/Zone.vue +182 -0
  55. package/src/components/layout/defaults/DefaultBreadcrumb.vue +149 -0
  56. package/src/components/layout/defaults/DefaultFooter.vue +89 -0
  57. package/src/components/layout/defaults/DefaultFormActions.vue +67 -0
  58. package/src/components/layout/defaults/DefaultHeader.vue +78 -0
  59. package/src/components/layout/defaults/DefaultMenu.vue +188 -0
  60. package/src/components/layout/defaults/DefaultPagination.vue +75 -0
  61. package/src/components/layout/defaults/DefaultTable.vue +137 -0
  62. package/src/components/layout/defaults/DefaultUserInfo.vue +111 -0
  63. package/src/components/layout/defaults/index.ts +16 -0
  64. package/src/components/lists/ActionButtons.vue +49 -0
  65. package/src/components/lists/ActionColumn.vue +40 -0
  66. package/src/components/lists/FilterBar.vue +54 -0
  67. package/src/components/lists/ListPage.vue +467 -0
  68. package/src/components/pages/LoginPage.vue +334 -0
  69. package/src/components/pages/NotFoundPage.vue +98 -0
  70. package/src/components/show/ShowDisplay.vue +343 -0
  71. package/src/components/show/ShowField.vue +67 -0
  72. package/src/components/show/ShowPage.vue +431 -0
  73. package/src/composables/index.ts +155 -0
  74. package/src/composables/useApp.ts +67 -0
  75. package/src/composables/useAuth.ts +98 -0
  76. package/src/composables/useBareForm.ts +319 -0
  77. package/src/composables/useBreadcrumb.ts +252 -0
  78. package/src/composables/useChildPage.ts +113 -0
  79. package/src/composables/useCurrentEntity.ts +60 -0
  80. package/src/composables/useDeferred.ts +94 -0
  81. package/src/composables/useDirtyState.ts +138 -0
  82. package/src/composables/useEntityItemFormPage.ts +942 -0
  83. package/src/composables/useEntityItemFormPage.types.ts +451 -0
  84. package/src/composables/useEntityItemPage.ts +438 -0
  85. package/src/composables/useEntityItemShowPage.ts +722 -0
  86. package/src/composables/useFieldManager.ts +692 -0
  87. package/src/composables/useForm.ts +544 -0
  88. package/src/composables/useGuardStore.ts +53 -0
  89. package/src/composables/useHooks.ts +56 -0
  90. package/src/composables/useInfoBanner.ts +217 -0
  91. package/src/composables/useJsonSyntax.ts +122 -0
  92. package/src/composables/useLayoutResolver.ts +329 -0
  93. package/src/composables/useListPage.ts +1535 -0
  94. package/src/composables/useListPage.types.ts +437 -0
  95. package/src/composables/useListPage.utils.ts +101 -0
  96. package/src/composables/useNavContext.ts +439 -0
  97. package/src/composables/useNavigation.ts +216 -0
  98. package/src/composables/useOptionsLookup.ts +323 -0
  99. package/src/composables/usePageTitle.ts +84 -0
  100. package/src/composables/useSSEBridge.ts +142 -0
  101. package/src/composables/useSemanticBreadcrumb.ts +222 -0
  102. package/src/composables/useSidebarState.ts +24 -0
  103. package/src/composables/useSignals.ts +52 -0
  104. package/src/composables/useUnsavedChangesGuard.ts +155 -0
  105. package/src/composables/useUserImpersonator.ts +317 -0
  106. package/src/composables/useZoneRegistry.ts +252 -0
  107. package/src/core/bundles.ts +558 -0
  108. package/src/core/decorator.ts +420 -0
  109. package/src/core/extension.ts +470 -0
  110. package/src/core/index.ts +59 -0
  111. package/src/deferred/DeferredRegistry.ts +353 -0
  112. package/src/deferred/index.ts +15 -0
  113. package/src/editors/index.ts +12 -0
  114. package/src/entity/EntityManager.cache.ts +530 -0
  115. package/src/entity/EntityManager.crud.ts +615 -0
  116. package/src/entity/EntityManager.interface.ts +78 -0
  117. package/src/entity/EntityManager.query.ts +221 -0
  118. package/src/entity/EntityManager.relations.ts +151 -0
  119. package/src/entity/EntityManager.ts +944 -0
  120. package/src/entity/EntityManager.types.ts +288 -0
  121. package/src/entity/auth/CompositeAuthAdapter.ts +194 -0
  122. package/src/entity/auth/EntityAuthAdapter.ts +119 -0
  123. package/src/entity/auth/PermissiveAdapter.ts +43 -0
  124. package/src/entity/auth/RoleHierarchy.ts +128 -0
  125. package/src/entity/auth/SecurityChecker.ts +202 -0
  126. package/src/entity/auth/factory.ts +203 -0
  127. package/src/entity/auth/index.ts +47 -0
  128. package/src/entity/factory.test.js +189 -0
  129. package/src/entity/factory.ts +146 -0
  130. package/src/entity/index.ts +49 -0
  131. package/src/entity/storage/ApiStorage.ts +228 -0
  132. package/src/entity/storage/IStorage.ts +100 -0
  133. package/src/entity/storage/LocalStorage.ts +200 -0
  134. package/src/entity/storage/MemoryStorage.ts +184 -0
  135. package/src/entity/storage/MockApiStorage.ts +318 -0
  136. package/src/entity/storage/SdkStorage.ts +377 -0
  137. package/src/entity/storage/factory.test.js +159 -0
  138. package/src/entity/storage/factory.ts +209 -0
  139. package/src/entity/storage/index.ts +75 -0
  140. package/src/gen/FieldMapper.ts +122 -0
  141. package/src/gen/StorageProfileFactory.ts +93 -0
  142. package/src/gen/connectors/BaseConnector.ts +150 -0
  143. package/src/gen/connectors/ManualConnector.test.js +499 -0
  144. package/src/gen/connectors/ManualConnector.ts +414 -0
  145. package/src/gen/connectors/OpenAPIConnector.test.js +836 -0
  146. package/src/gen/connectors/OpenAPIConnector.ts +693 -0
  147. package/src/gen/connectors/__fixtures__/sample-openapi.json +311 -0
  148. package/src/gen/connectors/index.ts +27 -0
  149. package/src/gen/createManagers.ts +245 -0
  150. package/src/gen/decorators.ts +134 -0
  151. package/src/gen/generateManagers.test.js +540 -0
  152. package/src/gen/generateManagers.ts +398 -0
  153. package/src/gen/index.ts +66 -0
  154. package/src/gen/schema.ts +248 -0
  155. package/src/gen/vite-plugin.ts +114 -0
  156. package/src/generated/managers/testManager.js +45 -0
  157. package/src/generated/managers/testManager.ts +46 -0
  158. package/src/hooks/HookRegistry.ts +16 -0
  159. package/src/hooks/index.ts +15 -0
  160. package/src/i18n/I18n.ts +344 -0
  161. package/src/i18n/IncrementalDomainProvider.ts +153 -0
  162. package/src/i18n/InlineTranslationProvider.ts +4 -0
  163. package/src/i18n/LazyTranslationProvider.ts +102 -0
  164. package/src/i18n/MessagesRegistry.ts +4 -0
  165. package/src/i18n/Resolver.ts +4 -0
  166. package/src/i18n/__tests__/I18n.test.ts +169 -0
  167. package/src/i18n/__tests__/IncrementalDomainProvider.test.ts +146 -0
  168. package/src/i18n/__tests__/LazyTranslationProvider.test.ts +100 -0
  169. package/src/i18n/__tests__/Resolver.test.ts +271 -0
  170. package/src/i18n/defaults/DefaultCoreProvider.ts +28 -0
  171. package/src/i18n/defaults/core.en.yml +55 -0
  172. package/src/i18n/defaults/core.fr.yml +55 -0
  173. package/src/i18n/index.ts +55 -0
  174. package/src/i18n/loaders/raw-modules.d.ts +15 -0
  175. package/src/i18n/loaders/yaml.ts +35 -0
  176. package/src/i18n/strategies.ts +4 -0
  177. package/src/i18n/types.ts +34 -0
  178. package/src/i18n/useI18n.ts +34 -0
  179. package/src/index.ts +453 -0
  180. package/src/kernel/EventRouter.ts +23 -0
  181. package/src/kernel/Kernel.api.ts +79 -0
  182. package/src/kernel/Kernel.auth.ts +120 -0
  183. package/src/kernel/Kernel.i18n.ts +29 -0
  184. package/src/kernel/Kernel.modules.ts +170 -0
  185. package/src/kernel/Kernel.registries.ts +237 -0
  186. package/src/kernel/Kernel.routing.ts +334 -0
  187. package/src/kernel/Kernel.ts +460 -0
  188. package/src/kernel/Kernel.types.ts +263 -0
  189. package/src/kernel/Kernel.vue.ts +304 -0
  190. package/src/kernel/KernelContext.entities.ts +80 -0
  191. package/src/kernel/KernelContext.events.ts +57 -0
  192. package/src/kernel/KernelContext.i18n.ts +37 -0
  193. package/src/kernel/KernelContext.permissions.ts +38 -0
  194. package/src/kernel/KernelContext.routing.ts +280 -0
  195. package/src/kernel/KernelContext.ts +252 -0
  196. package/src/kernel/KernelContext.types.ts +173 -0
  197. package/src/kernel/KernelContext.zones.ts +54 -0
  198. package/src/kernel/Module.ts +169 -0
  199. package/src/kernel/ModuleLoader.ts +645 -0
  200. package/src/kernel/SSEBridge.ts +11 -0
  201. package/src/kernel/SignalBus.ts +48 -0
  202. package/src/kernel/index.ts +56 -0
  203. package/src/module/index.ts +33 -0
  204. package/src/module/moduleRegistry.ts +492 -0
  205. package/src/modules/debug/AuthCollector.ts +501 -0
  206. package/src/modules/debug/Collector.ts +20 -0
  207. package/src/modules/debug/DebugBridge.ts +14 -0
  208. package/src/modules/debug/DebugModule.ts +220 -0
  209. package/src/modules/debug/EntitiesCollector.ts +839 -0
  210. package/src/modules/debug/ErrorCollector.ts +4 -0
  211. package/src/modules/debug/I18nCollector.ts +9 -0
  212. package/src/modules/debug/LocalStorageAdapter.ts +7 -0
  213. package/src/modules/debug/RouterCollector.ts +449 -0
  214. package/src/modules/debug/SignalCollector.ts +4 -0
  215. package/src/modules/debug/ToastCollector.ts +4 -0
  216. package/src/modules/debug/ZonesCollector.ts +447 -0
  217. package/src/modules/debug/components/DebugBar.vue +31 -0
  218. package/src/modules/debug/components/adminPanelsConfig.ts +42 -0
  219. package/src/modules/debug/components/index.ts +9 -0
  220. package/src/modules/debug/components/panels/AuthPanel.vue +267 -0
  221. package/src/modules/debug/components/panels/EntitiesPanel.vue +572 -0
  222. package/src/modules/debug/components/panels/I18nPanel.vue +738 -0
  223. package/src/modules/debug/components/panels/RouterPanel.vue +440 -0
  224. package/src/modules/debug/components/panels/ZonesPanel.vue +133 -0
  225. package/src/modules/debug/components/panels/index.ts +16 -0
  226. package/src/modules/debug/index.ts +58 -0
  227. package/src/modules/debug/styles.scss +2671 -0
  228. package/src/notifications/NotificationBadge.vue +35 -0
  229. package/src/notifications/NotificationListener.vue +71 -0
  230. package/src/notifications/NotificationModule.ts +76 -0
  231. package/src/notifications/NotificationPanel.vue +478 -0
  232. package/src/notifications/NotificationStore.ts +288 -0
  233. package/src/notifications/index.ts +42 -0
  234. package/src/notifications/styles.scss +32 -0
  235. package/src/orchestrator/Orchestrator.ts +427 -0
  236. package/src/orchestrator/index.ts +8 -0
  237. package/src/orchestrator/useOrchestrator.ts +80 -0
  238. package/src/plugin.ts +251 -0
  239. package/src/query/FilterQuery.ts +306 -0
  240. package/src/query/QueryExecutor.ts +341 -0
  241. package/src/query/index.ts +16 -0
  242. package/src/security/EntityRolesProvider.ts +385 -0
  243. package/src/security/PermissionMatcher.ts +152 -0
  244. package/src/security/PermissionRegistry.ts +273 -0
  245. package/src/security/PersistableRolesProvider.ts +584 -0
  246. package/src/security/RolesManager.ts +136 -0
  247. package/src/security/RolesProvider.ts +167 -0
  248. package/src/security/RolesProviderStorage.ts +211 -0
  249. package/src/security/SecurityModule.ts +94 -0
  250. package/src/security/StaticRolesProvider.ts +117 -0
  251. package/src/security/UsersManager.ts +163 -0
  252. package/src/security/index.ts +77 -0
  253. package/src/security/pages/RoleForm.vue +276 -0
  254. package/src/security/pages/RoleList.vue +123 -0
  255. package/src/styles/_alerts.scss +95 -0
  256. package/src/styles/_buttons.scss +391 -0
  257. package/src/styles/_cards.scss +241 -0
  258. package/src/styles/_code.scss +78 -0
  259. package/src/styles/_dialogs.scss +55 -0
  260. package/src/styles/_filter-bar.scss +70 -0
  261. package/src/styles/_forms.scss +434 -0
  262. package/src/styles/_helpers.scss +153 -0
  263. package/src/styles/_lists.scss +225 -0
  264. package/src/styles/_main.scss +356 -0
  265. package/src/styles/_markdown.scss +88 -0
  266. package/src/styles/_responsive.scss +255 -0
  267. package/src/styles/_show-pages.scss +186 -0
  268. package/src/styles/_sidebar.scss +380 -0
  269. package/src/styles/_states.scss +115 -0
  270. package/src/styles/_stats.scss +47 -0
  271. package/src/styles/_variables.scss +224 -0
  272. package/src/styles/index.scss +38 -0
  273. package/src/styles/theme/_components.scss +331 -0
  274. package/src/styles/theme/_index.scss +10 -0
  275. package/src/styles/theme/_tokens.scss +147 -0
  276. package/src/styles/theme/_utilities.scss +175 -0
  277. package/src/toast/ToastBridgeModule.ts +68 -0
  278. package/src/toast/ToastListener.vue +55 -0
  279. package/src/toast/index.ts +26 -0
  280. package/src/toast/useSignalToast.ts +154 -0
  281. package/src/types/index.ts +285 -0
  282. package/src/types/pluralize.d.ts +19 -0
  283. package/src/types/quarkernel.d.ts +50 -0
  284. package/src/utils/formatters.ts +174 -0
  285. package/src/utils/index.ts +35 -0
  286. package/src/utils/transformers.ts +131 -0
  287. package/src/vite/qdadmDebugPlugin.ts +401 -0
  288. package/src/vite-env.d.ts +50 -0
  289. package/src/zones/ZoneRegistry.ts +934 -0
  290. package/src/zones/index.ts +29 -0
  291. package/src/zones/zones.ts +191 -0
@@ -0,0 +1,334 @@
1
+ import {
2
+ createRouter,
3
+ createWebHistory,
4
+ createWebHashHistory,
5
+ type RouteRecordRaw,
6
+ type RouteLocationNormalized,
7
+ type NavigationGuardNext,
8
+ type RouteRecordNormalized,
9
+ } from 'vue-router'
10
+ import { getRoutes } from '../module/moduleRegistry'
11
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
+ type Self = any
13
+
14
+ /**
15
+ * Patch Kernel prototype with routing-related methods.
16
+ */
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ export function applyRoutingMethods(KernelClass: { prototype: any }): void {
19
+ const proto = KernelClass.prototype as Self
20
+
21
+ /**
22
+ * Create Vue Router.
23
+ *
24
+ * When `options.existingRouter` is provided, the Kernel reuses it
25
+ * and registers its computed routes via `addRoute()` instead of
26
+ * calling `createRouter()`. Combined with `options.routePrefix`,
27
+ * this lets a host mount the whole admin tree under a sub-path
28
+ * (e.g. `routePrefix: '/admin'` shifts the layout to `/admin` and
29
+ * the catch-all to `/admin/:pathMatch(.*)*`).
30
+ */
31
+ proto._createRouter = function (this: Self): void {
32
+ const { pages, homeRoute, coreRoutes, basePath } = this.options
33
+
34
+ if (!pages?.layout) {
35
+ throw new Error('[Kernel] pages.layout is required')
36
+ }
37
+
38
+ let homeRouteConfig: RouteRecordRaw
39
+ if (typeof homeRoute === 'object' && homeRoute?.component) {
40
+ homeRouteConfig = {
41
+ path: '',
42
+ name: homeRoute.name || '_home',
43
+ component: homeRoute.component,
44
+ }
45
+ } else {
46
+ homeRouteConfig = {
47
+ path: '',
48
+ name: '_home',
49
+ redirect: { name: (homeRoute as string) || 'home' },
50
+ }
51
+ }
52
+
53
+ const moduleRoutes = getRoutes() as RouteRecordRaw[]
54
+
55
+ const publicRoutes = moduleRoutes.filter(
56
+ (r) => r.meta?.public || r.meta?.requiresAuth === false
57
+ )
58
+ const protectedRoutes = moduleRoutes.filter(
59
+ (r) => !r.meta?.public && r.meta?.requiresAuth !== false
60
+ )
61
+
62
+ if (pages.login) {
63
+ publicRoutes.unshift({
64
+ path: '/login',
65
+ name: 'login',
66
+ component: pages.login,
67
+ meta: { public: true },
68
+ })
69
+ }
70
+
71
+ const layoutChildren: RouteRecordRaw[] = [
72
+ homeRouteConfig,
73
+ ...(coreRoutes || []),
74
+ ...protectedRoutes,
75
+ ]
76
+
77
+ let routes: RouteRecordRaw[]
78
+
79
+ if (pages.shell) {
80
+ routes = [
81
+ {
82
+ path: '/',
83
+ name: '_shell',
84
+ component: pages.shell,
85
+ children: [
86
+ ...publicRoutes,
87
+ {
88
+ path: '',
89
+ name: '_layout',
90
+ component: pages.layout,
91
+ meta: { requiresAuth: true },
92
+ children: layoutChildren,
93
+ },
94
+ ],
95
+ },
96
+ ]
97
+ } else {
98
+ routes = [
99
+ ...publicRoutes,
100
+ {
101
+ path: '/',
102
+ name: '_layout',
103
+ component: pages.layout,
104
+ meta: { requiresAuth: true },
105
+ children: layoutChildren,
106
+ },
107
+ ]
108
+ }
109
+
110
+ const notFoundComponent =
111
+ pages.notFound || (() => import('../components/pages/NotFoundPage.vue'))
112
+ const notFoundRoute: RouteRecordRaw = {
113
+ path: '/:pathMatch(.*)*',
114
+ name: 'not-found',
115
+ component: notFoundComponent,
116
+ meta: { public: true },
117
+ }
118
+
119
+ routes.push(notFoundRoute)
120
+
121
+ // ──────────────────────────────────────────────────────────────
122
+ // routePrefix: prepend to every Kernel-built route so the whole
123
+ // tree mounts under a sub-path (host scenarios like a CMS that
124
+ // lives at `/` and hosts qdadm at `/admin`). Empty by default.
125
+ const prefix = (this.options.routePrefix ?? '').replace(/\/+$/, '')
126
+ if (prefix) {
127
+ routes = routes.map((r) => prefixRoute(r, prefix))
128
+ }
129
+
130
+ if (this.options.existingRouter) {
131
+ // Host shell owns the router. Skip createRouter, register on
132
+ // the supplied instance. We do NOT touch history mode here —
133
+ // the host is responsible for that.
134
+ this.router = this.options.existingRouter
135
+ for (const r of routes) {
136
+ this.router.addRoute(r)
137
+ }
138
+ return
139
+ }
140
+
141
+ const { hashMode } = this.options
142
+ const history = hashMode
143
+ ? createWebHashHistory(basePath)
144
+ : createWebHistory(basePath)
145
+
146
+ this.router = createRouter({
147
+ history,
148
+ routes,
149
+ })
150
+ }
151
+
152
+ /**
153
+ * Setup auth guard on router
154
+ */
155
+ proto._setupAuthGuard = function (this: Self): void {
156
+ const { authAdapter } = this.options
157
+ if (!authAdapter) return
158
+
159
+ let wasEverAuthenticated = authAdapter.isAuthenticated()
160
+
161
+ const debug = this.options.debug ?? false
162
+ this.signals!.on('auth:session-lost', () => {
163
+ if (debug) {
164
+ console.warn('[Kernel] auth:session-lost received, emitting toast:warn')
165
+ }
166
+ this.orchestrator!.toast.warn(
167
+ 'Session lost',
168
+ 'Your session has expired. Please log in again.',
169
+ 'Kernel'
170
+ )
171
+ })
172
+
173
+ this.router!.beforeEach((to: RouteLocationNormalized, _from: RouteLocationNormalized, next: NavigationGuardNext) => {
174
+ if (authAdapter.isAuthenticated()) {
175
+ wasEverAuthenticated = true
176
+ }
177
+
178
+ const isPublic = to.matched.some(
179
+ (record: RouteRecordNormalized) =>
180
+ record.meta.public === true || record.meta.requiresAuth === false
181
+ )
182
+
183
+ if (isPublic) {
184
+ next()
185
+ return
186
+ }
187
+
188
+ const requiresAuth = to.matched.some(
189
+ (record: RouteRecordNormalized) => record.meta.requiresAuth === true
190
+ )
191
+
192
+ if (requiresAuth && !authAdapter.isAuthenticated()) {
193
+ if (wasEverAuthenticated) {
194
+ const debug = this.options.debug ?? false
195
+ if (debug) {
196
+ console.warn(
197
+ '[Kernel] Session lost detected, emitting auth:session-lost'
198
+ )
199
+ }
200
+ this.signals!.emit('auth:session-lost', {
201
+ reason: 'token_missing',
202
+ redirectTo: 'login',
203
+ })
204
+ wasEverAuthenticated = false
205
+ }
206
+
207
+ const loginRoute = this.router!.hasRoute('login')
208
+ ? { name: 'login', query: { session_lost: '1' } }
209
+ : '/'
210
+ next(loginRoute)
211
+ return
212
+ }
213
+
214
+ const entity = to.meta?.entity as string | undefined
215
+ if (entity && this.orchestrator) {
216
+ try {
217
+ const manager = this.orchestrator.get(entity)
218
+ if (manager && !manager.canRead()) {
219
+ console.warn(
220
+ `[qdadm] Access denied to ${to.path} (entity: ${entity})`
221
+ )
222
+ this.orchestrator.toast.error(
223
+ 'Access Denied',
224
+ `You don't have permission to access ${manager.labelPlural || entity}`,
225
+ 'Kernel'
226
+ )
227
+ this.signals!.emit('auth:access-denied', {
228
+ path: to.path,
229
+ entity,
230
+ manager,
231
+ })
232
+ next({ path: '/' })
233
+ return
234
+ }
235
+ } catch {
236
+ // Entity not registered - allow navigation
237
+ }
238
+ }
239
+
240
+ next()
241
+ })
242
+ }
243
+
244
+ /**
245
+ * Setup activeStack synchronization with router
246
+ */
247
+ proto._setupStackSync = function (this: Self): void {
248
+ this.router!.afterEach((to: RouteLocationNormalized) => {
249
+ this._rebuildActiveStack(to)
250
+ })
251
+ }
252
+
253
+ /**
254
+ * Rebuild activeStack from route
255
+ */
256
+ proto._rebuildActiveStack = function (this: Self, route: RouteLocationNormalized): void {
257
+ const entityConfig = route.meta?.entity as string | undefined
258
+
259
+ interface ParentMeta {
260
+ entity: string
261
+ param: string
262
+ foreignKey?: string
263
+ parent?: ParentMeta
264
+ }
265
+
266
+ const parentMeta = route.meta?.parent as ParentMeta | undefined
267
+
268
+ // No entity and no parent → nothing to stack
269
+ if (!entityConfig && !parentMeta) {
270
+ this.activeStack!.clear()
271
+ return
272
+ }
273
+
274
+ interface StackLevel {
275
+ entity: string
276
+ param: string
277
+ foreignKey: string | null
278
+ id: string | null
279
+ }
280
+
281
+ const levels: StackLevel[] = []
282
+
283
+ // Build parent levels
284
+ let parentConfig = parentMeta
285
+ while (parentConfig) {
286
+ const id = (route.params[parentConfig.param] as string) ?? null
287
+ levels.unshift({
288
+ entity: parentConfig.entity,
289
+ param: parentConfig.param,
290
+ foreignKey: null,
291
+ id,
292
+ })
293
+ parentConfig = parentConfig.parent ?? undefined
294
+ }
295
+
296
+ // Add current entity level (only if route has its own entity)
297
+ if (entityConfig) {
298
+ const manager = this.orchestrator?.get(entityConfig)
299
+ const idField = manager?.idField ?? 'id'
300
+ const currentId = (route.params[idField] as string) ?? null
301
+ const currentForeignKey = parentMeta?.foreignKey ?? null
302
+
303
+ // Skip if the ID param is already consumed by a parent level
304
+ // (e.g., /jobs/:id/bot-tasks — :id belongs to the parent, not the child)
305
+ const idConsumedByParent = levels.some(l => l.param === idField)
306
+
307
+ if (currentId && !idConsumedByParent) {
308
+ levels.push({
309
+ entity: entityConfig,
310
+ param: idField,
311
+ foreignKey: currentForeignKey,
312
+ id: currentId,
313
+ })
314
+ }
315
+ }
316
+
317
+ this.activeStack!.set(levels)
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Prepend a path prefix to a top-level route record. Children inherit
323
+ * relative paths so we only touch the outer record. Used by the
324
+ * `routePrefix` option to mount the whole admin tree under a sub-path
325
+ * of the host router.
326
+ */
327
+ function prefixRoute(route: RouteRecordRaw, prefix: string): RouteRecordRaw {
328
+ if (!route.path) return route
329
+ // Normalise: ensure single leading slash.
330
+ const tail = route.path.startsWith('/') ? route.path : `/${route.path}`
331
+ // `/` becomes the prefix itself, otherwise prefix + tail.
332
+ const newPath = tail === '/' ? prefix : `${prefix}${tail}`
333
+ return { ...route, path: newPath }
334
+ }