@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,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 }
|