@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,56 @@
1
+ /**
2
+ * Kernel Module
3
+ *
4
+ * Simplified bootstrap for qdadm applications.
5
+ */
6
+
7
+ export { Kernel } from './Kernel'
8
+ export type {
9
+ AuthAdapter,
10
+ AppConfig,
11
+ Features,
12
+ Pages,
13
+ LayoutComponents,
14
+ PrimeVueConfig,
15
+ SecurityConfig,
16
+ SSEConfig,
17
+ DebugBarConfig,
18
+ NotificationsConfig,
19
+ HomeRoute,
20
+ KernelOptions,
21
+ } from './Kernel'
22
+ export { SignalBus, createSignalBus, SIGNALS, SIGNAL_ACTIONS, buildSignal } from './SignalBus'
23
+ export type { SignalBusOptions, SignalAction, EntitySignalPayload } from './SignalBus'
24
+ export { EventRouter, createEventRouter } from './EventRouter'
25
+ export type { SignalTarget, RouteContext, RouteCallback, RouteTarget, RoutesConfig, EventRouterOptions } from './EventRouter'
26
+ export { SSEBridge, createSSEBridge, SSE_SIGNALS } from './SSEBridge'
27
+ export type { SSEBridgeOptions } from './SSEBridge'
28
+ export { Module } from './Module'
29
+ export { KernelContext, createKernelContext } from './KernelContext'
30
+ export type {
31
+ NavItem,
32
+ ZoneOptions,
33
+ BlockConfig,
34
+ RouteOptions,
35
+ CrudPages,
36
+ CrudOptions,
37
+ UserEntityOptions,
38
+ PermissionMeta,
39
+ PermissionOptions,
40
+ } from './KernelContext'
41
+ export {
42
+ ModuleLoader,
43
+ createModuleLoader,
44
+ ModuleNotFoundError,
45
+ CircularDependencyError,
46
+ ModuleLoadError,
47
+ } from './ModuleLoader'
48
+ export type {
49
+ ModuleContext,
50
+ ModuleLike,
51
+ ObjectModuleDefinition,
52
+ ModuleClassConstructor,
53
+ LegacyInitApi,
54
+ LegacyInitFunction,
55
+ ModuleDefinition,
56
+ } from './ModuleLoader'
@@ -0,0 +1,33 @@
1
+ /**
2
+ * qdadm - Module system exports
3
+ */
4
+
5
+ export {
6
+ registry,
7
+ initModules,
8
+ setSectionOrder,
9
+ getRoutes,
10
+ getNavSections,
11
+ alterMenuSections,
12
+ isMenuAltered,
13
+ getRouteFamilies,
14
+ getEntityConfigs,
15
+ getEntityConfig,
16
+ getSiblingRoutes,
17
+ getChildRoutes,
18
+ isRouteInFamily,
19
+ resetRegistry,
20
+ type ParentConfig,
21
+ type ModuleRouteMeta,
22
+ type ModuleRoute,
23
+ type NavItem,
24
+ type NavItemWithSection,
25
+ type NavSection,
26
+ type AddRoutesOptions,
27
+ type EntityConfig,
28
+ type ModuleInitContext,
29
+ type ModuleInitFunction,
30
+ type ModuleDefinition,
31
+ type InitModulesOptions,
32
+ type MenuAlterContext,
33
+ } from './moduleRegistry'
@@ -0,0 +1,492 @@
1
+ /**
2
+ * Module Registry - Auto-discovery and registration system
3
+ *
4
+ * Each module provides an init.js that registers:
5
+ * - Routes & Navigation (via registry)
6
+ * - Zone blocks (via zones)
7
+ * - Signal handlers (via signals)
8
+ * - Hooks (via hooks)
9
+ *
10
+ * Usage in module (modules/agents/init.js):
11
+ *
12
+ * export function init({ registry, zones, signals, hooks }) {
13
+ * // Routes & Navigation
14
+ * registry.addRoutes('agents', [...])
15
+ * registry.addNavItem({ section: 'Simulation', route: 'agents', ... })
16
+ * registry.addRouteFamily('agents', ['agent-'])
17
+ *
18
+ * // Zone blocks
19
+ * zones.registerBlock('agents-list-header', { id: 'agents-header', component: Header })
20
+ *
21
+ * // Signal handlers
22
+ * signals.on('agents:created', handleCreated)
23
+ *
24
+ * // Hooks
25
+ * hooks.register('agents:presave', validateAgent)
26
+ * }
27
+ */
28
+
29
+ import type { RouteRecordRaw, RouteComponent, RouteRecordRedirectOption } from 'vue-router'
30
+ import type { HookRegistry } from '../hooks/HookRegistry'
31
+ import type { ZoneRegistry } from '../zones/ZoneRegistry'
32
+ import type { SignalBus } from '../kernel/SignalBus'
33
+
34
+ /**
35
+ * Parent entity configuration for child routes
36
+ */
37
+ export interface ParentConfig {
38
+ entity: string
39
+ param: string
40
+ foreignKey?: string
41
+ itemRoute?: string
42
+ }
43
+
44
+ /**
45
+ * Route meta with qdadm extensions
46
+ */
47
+ export interface ModuleRouteMeta {
48
+ entity?: string
49
+ parent?: ParentConfig
50
+ navLabel?: string
51
+ layout?: 'list' | 'form' | 'dashboard' | 'base' | string
52
+ title?: string
53
+ [key: string]: unknown
54
+ }
55
+
56
+ /**
57
+ * Route definition with meta
58
+ *
59
+ * This is a simplified route type that captures what modules use.
60
+ * It's compatible with vue-router's RouteRecordRaw.
61
+ */
62
+ export interface ModuleRoute {
63
+ path: string
64
+ name?: string
65
+ component?: RouteComponent
66
+ components?: Record<string, RouteComponent>
67
+ redirect?: RouteRecordRedirectOption
68
+ props?: boolean | Record<string, unknown> | ((to: unknown) => Record<string, unknown>)
69
+ children?: ModuleRoute[]
70
+ meta?: ModuleRouteMeta
71
+ }
72
+
73
+ /**
74
+ * Navigation item
75
+ */
76
+ export interface NavItem {
77
+ route: string
78
+ label: string
79
+ icon?: string
80
+ entity?: string
81
+ exact?: boolean
82
+ }
83
+
84
+ /**
85
+ * Navigation item with section
86
+ */
87
+ export interface NavItemWithSection extends NavItem {
88
+ section: string
89
+ }
90
+
91
+ /**
92
+ * Navigation section
93
+ */
94
+ export interface NavSection {
95
+ title: string
96
+ items: NavItem[]
97
+ }
98
+
99
+ /**
100
+ * Route options for addRoutes
101
+ */
102
+ export interface AddRoutesOptions {
103
+ entity?: string
104
+ parent?: ParentConfig
105
+ label?: string
106
+ layout?: 'list' | 'form' | 'dashboard' | 'base' | string
107
+ }
108
+
109
+ /**
110
+ * Entity configuration or manager
111
+ */
112
+ export type EntityConfig = Record<string, unknown>
113
+
114
+ /**
115
+ * Module init context
116
+ */
117
+ export interface ModuleInitContext {
118
+ registry: typeof registry
119
+ zones?: ZoneRegistry
120
+ signals?: SignalBus
121
+ hooks?: HookRegistry
122
+ }
123
+
124
+ /**
125
+ * Module init function signature
126
+ */
127
+ export type ModuleInitFunction = (context: ModuleInitContext) => void
128
+
129
+ /**
130
+ * Module with init function
131
+ */
132
+ export interface ModuleDefinition {
133
+ init?: ModuleInitFunction
134
+ }
135
+
136
+ /**
137
+ * Init modules options
138
+ */
139
+ export interface InitModulesOptions {
140
+ coreNavItems?: NavItemWithSection[]
141
+ zones?: ZoneRegistry
142
+ signals?: SignalBus
143
+ hooks?: HookRegistry
144
+ }
145
+
146
+ /**
147
+ * Menu alter context
148
+ */
149
+ export interface MenuAlterContext {
150
+ sections: NavSection[]
151
+ }
152
+
153
+ // Storage
154
+ const routes: ModuleRoute[] = []
155
+ const navItems: NavItemWithSection[] = []
156
+ const routeFamilies: Record<string, string[]> = {}
157
+ const navSections = new Map<string, NavItem[]>()
158
+ const entityConfigs = new Map<string, EntityConfig>()
159
+
160
+ // Configurable section order (set via setSectionOrder or bootstrap)
161
+ let sectionOrder: string[] = []
162
+
163
+ // Altered nav sections after menu:alter hook
164
+ // null means not yet altered, use raw navSections
165
+ let alteredNavSections: NavSection[] | null = null
166
+
167
+ // Promise for in-flight alteration (prevents concurrent calls)
168
+ let alterationPromise: Promise<void> | null = null
169
+
170
+ /**
171
+ * Registry API passed to module init functions
172
+ */
173
+ const registry = {
174
+ /**
175
+ * Add routes for this module
176
+ *
177
+ * @param prefix - Path prefix for all routes (e.g., 'books' or 'books/:id/reviews')
178
+ * @param moduleRoutes - Route definitions with relative paths
179
+ * @param options - Route options
180
+ */
181
+ addRoutes(
182
+ prefix: string,
183
+ moduleRoutes: (ModuleRoute | RouteRecordRaw)[],
184
+ options: AddRoutesOptions = {}
185
+ ): void {
186
+ const { entity, parent, label, layout } = options
187
+ const prefixedRoutes = moduleRoutes.map((route) => {
188
+ const r = route as ModuleRoute
189
+ const routePath = r.path || ''
190
+ const routeLayout = r.meta?.layout
191
+ return {
192
+ ...r,
193
+ path: routePath ? `${prefix}/${routePath}` : prefix,
194
+ meta: {
195
+ ...r.meta,
196
+ ...(entity && { entity }),
197
+ ...(parent && { parent }),
198
+ ...(label && { navLabel: label }),
199
+ // Layout can be set per-route or inherited from options
200
+ ...((routeLayout || layout) && { layout: routeLayout || layout }),
201
+ },
202
+ } as ModuleRoute
203
+ })
204
+ routes.push(...prefixedRoutes)
205
+ },
206
+
207
+ /**
208
+ * Add a navigation item to a section
209
+ * @param item - { section, route, icon, label, exact? }
210
+ */
211
+ addNavItem(item: NavItemWithSection): void {
212
+ const { section, ...navItem } = item
213
+ if (!navSections.has(section)) {
214
+ navSections.set(section, [])
215
+ }
216
+ navSections.get(section)!.push(navItem)
217
+ navItems.push(item)
218
+ },
219
+
220
+ /**
221
+ * Add route family mapping (for active state detection)
222
+ * @param parent - Parent route name (e.g., 'agents')
223
+ * @param prefixes - Child route prefixes (e.g., ['agent-'])
224
+ */
225
+ addRouteFamily(parent: string, prefixes: string[]): void {
226
+ if (!routeFamilies[parent]) {
227
+ routeFamilies[parent] = []
228
+ }
229
+ routeFamilies[parent].push(...prefixes)
230
+ },
231
+
232
+ /**
233
+ * Declare an entity managed by this module
234
+ * @param name - Entity name (e.g., 'users')
235
+ * @param config - Entity config or manager instance
236
+ *
237
+ * Usage:
238
+ * registry.addEntity('users', { endpoint: '/users' })
239
+ * registry.addEntity('users', new CustomUsersManager())
240
+ */
241
+ addEntity(name: string, config: EntityConfig): void {
242
+ entityConfigs.set(name, config)
243
+ },
244
+ }
245
+
246
+ /**
247
+ * Set section order for navigation menu
248
+ * Called from bootstrap config
249
+ * @param order - Section titles in order
250
+ */
251
+ export function setSectionOrder(order: string[]): void {
252
+ sectionOrder = order || []
253
+ }
254
+
255
+ /**
256
+ * Initialize modules from a glob import result
257
+ * The consuming app calls this with its own import.meta.glob
258
+ *
259
+ * Usage in app:
260
+ * const moduleInits = import.meta.glob('./modules/* /init.js', { eager: true })
261
+ * initModules(moduleInits, { zones, signals, hooks })
262
+ *
263
+ * @param moduleInits - Result of import.meta.glob
264
+ * @param options - { coreNavItems, zones, signals, hooks }
265
+ */
266
+ export function initModules(
267
+ moduleInits: Record<string, ModuleDefinition>,
268
+ options: InitModulesOptions = {}
269
+ ): void {
270
+ const { coreNavItems, zones, signals, hooks } = options
271
+
272
+ // Add core nav items (pages that aren't in modules)
273
+ if (coreNavItems) {
274
+ for (const item of coreNavItems) {
275
+ registry.addNavItem(item)
276
+ }
277
+ }
278
+
279
+ // Context passed to module init functions
280
+ const context: ModuleInitContext = { registry, zones, signals, hooks }
281
+
282
+ // Initialize all discovered modules
283
+ for (const path in moduleInits) {
284
+ const module = moduleInits[path]
285
+ if (module && typeof module.init === 'function') {
286
+ module.init(context)
287
+ }
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Get all registered routes
293
+ */
294
+ export function getRoutes(): ModuleRoute[] {
295
+ return routes
296
+ }
297
+
298
+ /**
299
+ * Build raw navigation sections from registered nav items (before alteration)
300
+ * @returns Array of navigation sections
301
+ */
302
+ function buildRawNavSections(): NavSection[] {
303
+ const sections: NavSection[] = []
304
+
305
+ // First add sections in the configured order
306
+ for (const title of sectionOrder) {
307
+ if (navSections.has(title)) {
308
+ sections.push({
309
+ title,
310
+ items: [...navSections.get(title)!], // Clone items array
311
+ })
312
+ }
313
+ }
314
+
315
+ // Add any sections not in the predefined order
316
+ for (const [title, items] of navSections) {
317
+ if (!sectionOrder.includes(title)) {
318
+ sections.push({ title, items: [...items] }) // Clone items array
319
+ }
320
+ }
321
+
322
+ return sections
323
+ }
324
+
325
+ /**
326
+ * Get navigation sections in order
327
+ *
328
+ * Returns altered sections if menu:alter hook has been invoked,
329
+ * otherwise returns raw sections from module registrations.
330
+ *
331
+ * @returns Array of navigation sections
332
+ */
333
+ export function getNavSections(): NavSection[] {
334
+ // Return altered sections if available, otherwise build from raw
335
+ if (alteredNavSections !== null) {
336
+ return alteredNavSections
337
+ }
338
+ return buildRawNavSections()
339
+ }
340
+
341
+ /**
342
+ * Invoke menu:alter hook to allow modules to modify navigation structure
343
+ *
344
+ * Called lazily by useNavigation on first access. Modules can register
345
+ * handlers for 'menu:alter' hook to add, remove, reorder, or modify
346
+ * navigation sections and items.
347
+ *
348
+ * @param hooks - The hook registry instance
349
+ *
350
+ * @example
351
+ * // In module init or extension
352
+ * hooks.register('menu:alter', (context) => {
353
+ * // Add a new section
354
+ * context.sections.push({
355
+ * title: 'Tools',
356
+ * items: [{ route: 'tools', label: 'Tools', icon: 'pi pi-wrench' }]
357
+ * })
358
+ *
359
+ * // Add item to existing section
360
+ * const adminSection = context.sections.find(s => s.title === 'Admin')
361
+ * if (adminSection) {
362
+ * adminSection.items.push({ route: 'settings', label: 'Settings' })
363
+ * }
364
+ *
365
+ * // Remove a section
366
+ * const idx = context.sections.findIndex(s => s.title === 'Legacy')
367
+ * if (idx !== -1) context.sections.splice(idx, 1)
368
+ *
369
+ * return context
370
+ * })
371
+ */
372
+ export async function alterMenuSections(hooks: HookRegistry | null | undefined): Promise<void> {
373
+ // Already altered? Return immediately
374
+ if (alteredNavSections !== null) {
375
+ return
376
+ }
377
+
378
+ // In-flight alteration? Wait for it
379
+ if (alterationPromise !== null) {
380
+ await alterationPromise
381
+ return
382
+ }
383
+
384
+ // No hooks registry? Mark as altered with raw sections
385
+ if (!hooks) {
386
+ alteredNavSections = buildRawNavSections()
387
+ return
388
+ }
389
+
390
+ // Perform alteration
391
+ alterationPromise = (async () => {
392
+ const rawSections = buildRawNavSections()
393
+
394
+ // Invoke menu:alter hook with mutable context
395
+ const alteredContext = (await hooks.alter('menu:alter', {
396
+ sections: rawSections,
397
+ })) as MenuAlterContext
398
+
399
+ // Store altered sections for getNavSections()
400
+ alteredNavSections = alteredContext.sections
401
+ alterationPromise = null
402
+ })()
403
+
404
+ await alterationPromise
405
+ }
406
+
407
+ /**
408
+ * Check if menu:alter hook has been invoked
409
+ * @returns Whether menu has been altered
410
+ */
411
+ export function isMenuAltered(): boolean {
412
+ return alteredNavSections !== null
413
+ }
414
+
415
+ /**
416
+ * Get route families mapping
417
+ */
418
+ export function getRouteFamilies(): Record<string, string[]> {
419
+ return routeFamilies
420
+ }
421
+
422
+ /**
423
+ * Get entity configurations declared by modules
424
+ * @returns Map of entity configs
425
+ */
426
+ export function getEntityConfigs(): Map<string, EntityConfig> {
427
+ return entityConfigs
428
+ }
429
+
430
+ /**
431
+ * Get a specific entity configuration
432
+ * @param name - Entity name
433
+ * @returns Entity config or undefined
434
+ */
435
+ export function getEntityConfig(name: string): EntityConfig | undefined {
436
+ return entityConfigs.get(name)
437
+ }
438
+
439
+ /**
440
+ * Get sibling routes (routes sharing the same parent entity + param)
441
+ * @param parentEntity - Parent entity name
442
+ * @param parentParam - Parent route param
443
+ * @returns Routes with matching parent config
444
+ */
445
+ export function getSiblingRoutes(parentEntity: string, parentParam: string): ModuleRoute[] {
446
+ return routes.filter((route) => {
447
+ const parent = route.meta?.parent
448
+ return parent?.entity === parentEntity && parent?.param === parentParam
449
+ })
450
+ }
451
+
452
+ /**
453
+ * Get child routes (routes that have this entity as parent)
454
+ * @param entityName - Entity name to find children for
455
+ * @returns Routes with this entity as parent
456
+ */
457
+ export function getChildRoutes(entityName: string): ModuleRoute[] {
458
+ return routes.filter((route) => {
459
+ const parent = route.meta?.parent
460
+ return parent?.entity === entityName
461
+ })
462
+ }
463
+
464
+ /**
465
+ * Check if a route belongs to a family
466
+ */
467
+ export function isRouteInFamily(currentRoute: string, parentRoute: string): boolean {
468
+ if (currentRoute === parentRoute) return true
469
+
470
+ const prefixes = routeFamilies[parentRoute]
471
+ if (prefixes && currentRoute) {
472
+ return prefixes.some((prefix) => currentRoute.startsWith(prefix))
473
+ }
474
+ return false
475
+ }
476
+
477
+ /**
478
+ * Reset registry (for testing)
479
+ */
480
+ export function resetRegistry(): void {
481
+ routes.length = 0
482
+ navItems.length = 0
483
+ Object.keys(routeFamilies).forEach((key) => delete routeFamilies[key])
484
+ navSections.clear()
485
+ entityConfigs.clear()
486
+ sectionOrder = []
487
+ alteredNavSections = null
488
+ alterationPromise = null
489
+ }
490
+
491
+ // Export registry for direct access if needed
492
+ export { registry }