@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,645 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModuleLoader - Duck typing module loader with dependency resolution
|
|
3
|
+
*
|
|
4
|
+
* Detects and normalizes multiple module formats:
|
|
5
|
+
* 1. Module instance - use directly
|
|
6
|
+
* 2. Module class - instantiate with new
|
|
7
|
+
* 3. Plain object with connect() - wrap in adapter
|
|
8
|
+
* 4. Plain function - wrap as legacy init({ registry, zones })
|
|
9
|
+
*
|
|
10
|
+
* Provides topological sorting based on requires + priority.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { Module } from './Module'
|
|
14
|
+
|
|
15
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
16
|
+
// Types
|
|
17
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Context passed to module connect() method
|
|
21
|
+
*/
|
|
22
|
+
export interface ModuleContext {
|
|
23
|
+
routes: (basePath: string, routes: unknown[], opts?: unknown) => void
|
|
24
|
+
navItem: (item: unknown) => void
|
|
25
|
+
routeFamily: (base: string, prefixes: string[]) => void
|
|
26
|
+
zones: unknown
|
|
27
|
+
[key: string]: unknown
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Module-like interface that all adapters implement
|
|
32
|
+
*/
|
|
33
|
+
export interface ModuleLike {
|
|
34
|
+
name: string
|
|
35
|
+
requires: string[]
|
|
36
|
+
priority: number
|
|
37
|
+
enabled(ctx: ModuleContext): boolean
|
|
38
|
+
connect(ctx: ModuleContext): Promise<void>
|
|
39
|
+
disconnect?(): Promise<void>
|
|
40
|
+
loadStyles?(): Promise<void>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Plain object module definition
|
|
45
|
+
*/
|
|
46
|
+
export interface ObjectModuleDefinition {
|
|
47
|
+
name?: string
|
|
48
|
+
requires?: string[]
|
|
49
|
+
priority?: number
|
|
50
|
+
enabled?: boolean | ((ctx: ModuleContext) => boolean)
|
|
51
|
+
connect?: (ctx: ModuleContext) => void | Promise<void>
|
|
52
|
+
disconnect?: () => void | Promise<void>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Module class constructor with static properties
|
|
57
|
+
*/
|
|
58
|
+
export interface ModuleClassConstructor {
|
|
59
|
+
new (): ModuleLike
|
|
60
|
+
name: string // Function.name (class name)
|
|
61
|
+
moduleName?: string // Static moduleName property
|
|
62
|
+
requires?: string[]
|
|
63
|
+
priority?: number
|
|
64
|
+
enabled?: (ctx: ModuleContext) => boolean
|
|
65
|
+
prototype: {
|
|
66
|
+
connect?: (ctx: ModuleContext) => void | Promise<void>
|
|
67
|
+
enabled?: (ctx: ModuleContext) => boolean
|
|
68
|
+
disconnect?: () => void | Promise<void>
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Legacy init function signature
|
|
74
|
+
*/
|
|
75
|
+
export interface LegacyInitApi {
|
|
76
|
+
registry: {
|
|
77
|
+
addRoutes: (basePath: string, routes: unknown[], opts?: unknown) => void
|
|
78
|
+
addNavItem: (item: unknown) => void
|
|
79
|
+
addRouteFamily: (base: string, prefixes: string[]) => void
|
|
80
|
+
}
|
|
81
|
+
zones: unknown
|
|
82
|
+
ctx: ModuleContext
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export type LegacyInitFunction = (api: LegacyInitApi) => void | Promise<void>
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Any valid module definition
|
|
89
|
+
*/
|
|
90
|
+
export type ModuleDefinition =
|
|
91
|
+
| Module
|
|
92
|
+
| ModuleLike
|
|
93
|
+
| ModuleClassConstructor
|
|
94
|
+
| ObjectModuleDefinition
|
|
95
|
+
| LegacyInitFunction
|
|
96
|
+
|
|
97
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
98
|
+
// Custom Errors
|
|
99
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Error thrown when a required module is not registered
|
|
103
|
+
*/
|
|
104
|
+
export class ModuleNotFoundError extends Error {
|
|
105
|
+
moduleName: string
|
|
106
|
+
requiredBy: string
|
|
107
|
+
|
|
108
|
+
constructor(moduleName: string, requiredBy: string) {
|
|
109
|
+
super(`Module '${moduleName}' not found (required by '${requiredBy}')`)
|
|
110
|
+
this.name = 'ModuleNotFoundError'
|
|
111
|
+
this.moduleName = moduleName
|
|
112
|
+
this.requiredBy = requiredBy
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Error thrown when circular dependencies are detected
|
|
118
|
+
*/
|
|
119
|
+
export class CircularDependencyError extends Error {
|
|
120
|
+
cycle: string[]
|
|
121
|
+
|
|
122
|
+
constructor(cycle: string[]) {
|
|
123
|
+
const cyclePath = cycle.join(' → ')
|
|
124
|
+
super(`Circular dependency detected: ${cyclePath}`)
|
|
125
|
+
this.name = 'CircularDependencyError'
|
|
126
|
+
this.cycle = cycle
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Error thrown when module connect() fails
|
|
132
|
+
*/
|
|
133
|
+
export class ModuleLoadError extends Error {
|
|
134
|
+
moduleName: string
|
|
135
|
+
override cause: Error
|
|
136
|
+
|
|
137
|
+
constructor(moduleName: string, cause: Error) {
|
|
138
|
+
super(`Failed to load module '${moduleName}': ${cause.message}`)
|
|
139
|
+
this.name = 'ModuleLoadError'
|
|
140
|
+
this.moduleName = moduleName
|
|
141
|
+
this.cause = cause
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
146
|
+
// Module Adapter for plain objects
|
|
147
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Wraps a plain object with connect() into a Module-like interface
|
|
151
|
+
*/
|
|
152
|
+
class ObjectModuleAdapter implements ModuleLike {
|
|
153
|
+
private _def: ObjectModuleDefinition
|
|
154
|
+
private _ctx: ModuleContext | null = null
|
|
155
|
+
|
|
156
|
+
constructor(def: ObjectModuleDefinition) {
|
|
157
|
+
this._def = def
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
get name(): string {
|
|
161
|
+
return this._def.name || 'anonymous'
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
get requires(): string[] {
|
|
165
|
+
return this._def.requires || []
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
get priority(): number {
|
|
169
|
+
return this._def.priority ?? 0
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
enabled(ctx: ModuleContext): boolean {
|
|
173
|
+
if (typeof this._def.enabled === 'function') {
|
|
174
|
+
return this._def.enabled(ctx)
|
|
175
|
+
}
|
|
176
|
+
return this._def.enabled !== false
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async connect(ctx: ModuleContext): Promise<void> {
|
|
180
|
+
this._ctx = ctx
|
|
181
|
+
if (typeof this._def.connect === 'function') {
|
|
182
|
+
await this._def.connect(ctx)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async disconnect(): Promise<void> {
|
|
187
|
+
if (typeof this._def.disconnect === 'function') {
|
|
188
|
+
await this._def.disconnect()
|
|
189
|
+
}
|
|
190
|
+
this._ctx = null
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
195
|
+
// Class Adapter for non-Module classes
|
|
196
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Wraps a class with static name and connect method into a Module-like interface
|
|
200
|
+
*/
|
|
201
|
+
class ClassModuleAdapter implements ModuleLike {
|
|
202
|
+
private _ClassDef: ModuleClassConstructor
|
|
203
|
+
private _instance: ModuleLike
|
|
204
|
+
private _ctx: ModuleContext | null = null
|
|
205
|
+
|
|
206
|
+
constructor(ClassDef: ModuleClassConstructor) {
|
|
207
|
+
this._ClassDef = ClassDef
|
|
208
|
+
this._instance = new ClassDef()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
get name(): string {
|
|
212
|
+
// Check for OWN moduleName property first (not inherited from Module base class)
|
|
213
|
+
// This prevents inheriting 'base' from Module when subclass uses 'static name' instead
|
|
214
|
+
if (Object.hasOwn(this._ClassDef, 'moduleName') && this._ClassDef.moduleName) {
|
|
215
|
+
return this._ClassDef.moduleName
|
|
216
|
+
}
|
|
217
|
+
// Fall back to name property (could be static name = 'xxx' or Function.name)
|
|
218
|
+
return this._ClassDef.name || 'anonymous'
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
get requires(): string[] {
|
|
222
|
+
return this._ClassDef.requires || []
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
get priority(): number {
|
|
226
|
+
return this._ClassDef.priority ?? 0
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
enabled(ctx: ModuleContext): boolean {
|
|
230
|
+
if (typeof this._instance.enabled === 'function') {
|
|
231
|
+
return this._instance.enabled(ctx)
|
|
232
|
+
}
|
|
233
|
+
if (typeof this._ClassDef.enabled === 'function') {
|
|
234
|
+
return this._ClassDef.enabled(ctx)
|
|
235
|
+
}
|
|
236
|
+
return true
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async connect(ctx: ModuleContext): Promise<void> {
|
|
240
|
+
this._ctx = ctx
|
|
241
|
+
if (typeof this._instance.connect === 'function') {
|
|
242
|
+
await this._instance.connect(ctx)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async disconnect(): Promise<void> {
|
|
247
|
+
if (typeof this._instance.disconnect === 'function') {
|
|
248
|
+
await this._instance.disconnect()
|
|
249
|
+
}
|
|
250
|
+
this._ctx = null
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
255
|
+
// Legacy Function Adapter
|
|
256
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Wraps a legacy init function into a Module-like interface
|
|
260
|
+
*/
|
|
261
|
+
class LegacyFunctionAdapter implements ModuleLike {
|
|
262
|
+
private _initFn: LegacyInitFunction
|
|
263
|
+
private _name: string
|
|
264
|
+
|
|
265
|
+
constructor(initFn: LegacyInitFunction) {
|
|
266
|
+
this._initFn = initFn
|
|
267
|
+
this._name = initFn.name || 'legacyModule'
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
get name(): string {
|
|
271
|
+
return this._name
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
get requires(): string[] {
|
|
275
|
+
return []
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
get priority(): number {
|
|
279
|
+
return 0
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
enabled(): boolean {
|
|
283
|
+
return true
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async connect(ctx: ModuleContext): Promise<void> {
|
|
287
|
+
// Legacy pattern: init({ registry, zones })
|
|
288
|
+
// Adapt KernelContext to legacy interface
|
|
289
|
+
const legacyApi: LegacyInitApi = {
|
|
290
|
+
registry: {
|
|
291
|
+
addRoutes: (basePath: string, routes: unknown[], opts?: unknown) =>
|
|
292
|
+
ctx.routes(basePath, routes, opts),
|
|
293
|
+
addNavItem: (item: unknown) => ctx.navItem(item),
|
|
294
|
+
addRouteFamily: (base: string, prefixes: string[]) => ctx.routeFamily(base, prefixes),
|
|
295
|
+
},
|
|
296
|
+
zones: ctx.zones,
|
|
297
|
+
ctx,
|
|
298
|
+
}
|
|
299
|
+
await this._initFn(legacyApi)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async disconnect(): Promise<void> {
|
|
303
|
+
// Legacy functions don't support disconnect
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
308
|
+
// ModuleLoader
|
|
309
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* ModuleLoader - Loads and manages modules with dependency resolution
|
|
313
|
+
*/
|
|
314
|
+
export class ModuleLoader {
|
|
315
|
+
/** Registered module definitions (before normalization) */
|
|
316
|
+
private _registered: Map<string, ModuleLike> = new Map()
|
|
317
|
+
|
|
318
|
+
/** Loaded module instances */
|
|
319
|
+
private _loaded: Map<string, ModuleLike> = new Map()
|
|
320
|
+
|
|
321
|
+
/** Load order for proper unloading */
|
|
322
|
+
private _loadOrder: string[] = []
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Register a module (any format)
|
|
326
|
+
*
|
|
327
|
+
* Accepts:
|
|
328
|
+
* - Module instance (instanceof Module)
|
|
329
|
+
* - Module class (has static name + prototype.connect or extends Module)
|
|
330
|
+
* - Plain object with connect function
|
|
331
|
+
* - Plain function (legacy init pattern)
|
|
332
|
+
*
|
|
333
|
+
* @param moduleDef - Module definition in any format
|
|
334
|
+
* @returns this for chaining
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* loader.add(new UsersModule())
|
|
338
|
+
* loader.add(UsersModule)
|
|
339
|
+
* loader.add({ name: 'simple', connect(ctx) { ... } })
|
|
340
|
+
* loader.add(function initLegacy({ registry }) { ... })
|
|
341
|
+
*/
|
|
342
|
+
add(moduleDef: ModuleDefinition): this {
|
|
343
|
+
const normalized = this._normalize(moduleDef)
|
|
344
|
+
const name = normalized.name
|
|
345
|
+
|
|
346
|
+
if (!name || name === 'anonymous') {
|
|
347
|
+
throw new Error('Module must have a name (static name property, options.name, or function name)')
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (this._registered.has(name)) {
|
|
351
|
+
throw new Error(`Module '${name}' is already registered`)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
this._registered.set(name, normalized)
|
|
355
|
+
return this
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Load all registered modules in dependency order
|
|
360
|
+
*
|
|
361
|
+
* @param ctx - Context to pass to connect() (typically KernelContext-like)
|
|
362
|
+
* @throws ModuleNotFoundError When a required module is not registered
|
|
363
|
+
* @throws CircularDependencyError When circular dependencies exist
|
|
364
|
+
* @throws ModuleLoadError When a module's connect() fails
|
|
365
|
+
*/
|
|
366
|
+
async loadAll(ctx: ModuleContext): Promise<void> {
|
|
367
|
+
// Get sorted modules
|
|
368
|
+
const sorted = this._topologicalSort()
|
|
369
|
+
|
|
370
|
+
// Load in order
|
|
371
|
+
for (const name of sorted) {
|
|
372
|
+
const module = this._registered.get(name)
|
|
373
|
+
|
|
374
|
+
if (!module) continue
|
|
375
|
+
|
|
376
|
+
// Check if enabled
|
|
377
|
+
if (!module.enabled(ctx)) {
|
|
378
|
+
continue
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Load styles and connect module
|
|
382
|
+
try {
|
|
383
|
+
// Load module styles if defined (Module subclasses only)
|
|
384
|
+
if (typeof module.loadStyles === 'function') {
|
|
385
|
+
await module.loadStyles()
|
|
386
|
+
}
|
|
387
|
+
await module.connect(ctx)
|
|
388
|
+
this._loaded.set(name, module)
|
|
389
|
+
this._loadOrder.push(name)
|
|
390
|
+
} catch (err) {
|
|
391
|
+
throw new ModuleLoadError(name, err as Error)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Unload all modules in reverse order
|
|
398
|
+
*/
|
|
399
|
+
async unloadAll(): Promise<void> {
|
|
400
|
+
// Unload in reverse order
|
|
401
|
+
const reversed = [...this._loadOrder].reverse()
|
|
402
|
+
|
|
403
|
+
for (const name of reversed) {
|
|
404
|
+
const module = this._loaded.get(name)
|
|
405
|
+
if (module && typeof module.disconnect === 'function') {
|
|
406
|
+
await module.disconnect()
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
this._loaded.clear()
|
|
411
|
+
this._loadOrder = []
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Get loaded modules (for debug/introspection)
|
|
416
|
+
*
|
|
417
|
+
* @returns Map of module name to module instance
|
|
418
|
+
*/
|
|
419
|
+
getModules(): Map<string, ModuleLike> {
|
|
420
|
+
return new Map(this._loaded)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Normalize any module format to Module-like interface
|
|
425
|
+
*/
|
|
426
|
+
private _normalize(moduleDef: ModuleDefinition): ModuleLike {
|
|
427
|
+
// 1. Already a Module instance
|
|
428
|
+
if (moduleDef instanceof Module) {
|
|
429
|
+
return moduleDef as unknown as ModuleLike
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// 2. Module class (constructor that extends Module or has static name + connect method)
|
|
433
|
+
if (typeof moduleDef === 'function') {
|
|
434
|
+
// Check if it's a class extending Module
|
|
435
|
+
const funcDef = moduleDef as ModuleClassConstructor
|
|
436
|
+
if (funcDef.prototype instanceof Module) {
|
|
437
|
+
return new funcDef()
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Check if it looks like a Module class:
|
|
441
|
+
// - Has own static 'name' property (not just inherited function name)
|
|
442
|
+
// - Prototype has connect method
|
|
443
|
+
const hasOwnStaticName = Object.prototype.hasOwnProperty.call(funcDef, 'name')
|
|
444
|
+
if (
|
|
445
|
+
hasOwnStaticName &&
|
|
446
|
+
typeof funcDef.name === 'string' &&
|
|
447
|
+
funcDef.name !== '' &&
|
|
448
|
+
typeof funcDef.prototype?.connect === 'function'
|
|
449
|
+
) {
|
|
450
|
+
// Use adapter to properly expose static properties
|
|
451
|
+
return new ClassModuleAdapter(funcDef)
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Otherwise it's a legacy init function
|
|
455
|
+
return new LegacyFunctionAdapter(moduleDef as LegacyInitFunction)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// 3. Plain object with connect function
|
|
459
|
+
if (
|
|
460
|
+
moduleDef &&
|
|
461
|
+
typeof moduleDef === 'object' &&
|
|
462
|
+
'connect' in moduleDef &&
|
|
463
|
+
typeof moduleDef.connect === 'function'
|
|
464
|
+
) {
|
|
465
|
+
return new ObjectModuleAdapter(moduleDef as ObjectModuleDefinition)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
throw new Error(
|
|
469
|
+
'Invalid module format. Expected: Module instance, Module class, object with connect(), or function'
|
|
470
|
+
)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Get the requires array from a module (handles static vs instance properties)
|
|
475
|
+
*/
|
|
476
|
+
private _getRequires(module: ModuleLike): string[] {
|
|
477
|
+
// Check instance property first
|
|
478
|
+
if (Array.isArray(module.requires)) {
|
|
479
|
+
return module.requires
|
|
480
|
+
}
|
|
481
|
+
// Check constructor (static) property for Module subclasses
|
|
482
|
+
const constructor = (module as { constructor?: { requires?: string[] } }).constructor
|
|
483
|
+
if (constructor && Array.isArray(constructor.requires)) {
|
|
484
|
+
return constructor.requires
|
|
485
|
+
}
|
|
486
|
+
return []
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Get the priority from a module (handles static vs instance properties)
|
|
491
|
+
*/
|
|
492
|
+
private _getPriority(module: ModuleLike): number {
|
|
493
|
+
// Check instance property first
|
|
494
|
+
if (typeof module.priority === 'number') {
|
|
495
|
+
return module.priority
|
|
496
|
+
}
|
|
497
|
+
// Check constructor (static) property for Module subclasses
|
|
498
|
+
const constructor = (module as { constructor?: { priority?: number } }).constructor
|
|
499
|
+
if (constructor && typeof constructor.priority === 'number') {
|
|
500
|
+
return constructor.priority
|
|
501
|
+
}
|
|
502
|
+
return 0
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Sort modules topologically based on requires + priority
|
|
507
|
+
*
|
|
508
|
+
* Uses Kahn's algorithm for topological sort with priority tie-breaking.
|
|
509
|
+
*
|
|
510
|
+
* @returns Sorted module names
|
|
511
|
+
* @throws ModuleNotFoundError When a required module is not registered
|
|
512
|
+
* @throws CircularDependencyError When circular dependencies exist
|
|
513
|
+
*/
|
|
514
|
+
private _topologicalSort(): string[] {
|
|
515
|
+
const modules = this._registered
|
|
516
|
+
const names = Array.from(modules.keys())
|
|
517
|
+
|
|
518
|
+
// Build dependency graph
|
|
519
|
+
// inDegree: number of dependencies for each module
|
|
520
|
+
// dependents: modules that depend on this module
|
|
521
|
+
const inDegree = new Map<string, number>()
|
|
522
|
+
const dependents = new Map<string, string[]>()
|
|
523
|
+
|
|
524
|
+
for (const name of names) {
|
|
525
|
+
inDegree.set(name, 0)
|
|
526
|
+
dependents.set(name, [])
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Process requires for each module
|
|
530
|
+
for (const [name, module] of modules) {
|
|
531
|
+
const requires = this._getRequires(module)
|
|
532
|
+
|
|
533
|
+
for (const req of requires) {
|
|
534
|
+
if (!modules.has(req)) {
|
|
535
|
+
throw new ModuleNotFoundError(req, name)
|
|
536
|
+
}
|
|
537
|
+
inDegree.set(name, (inDegree.get(name) || 0) + 1)
|
|
538
|
+
dependents.get(req)?.push(name)
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Initialize queue with modules that have no dependencies
|
|
543
|
+
// Sort by priority (lower first) for consistent ordering
|
|
544
|
+
const queue = names
|
|
545
|
+
.filter((name) => inDegree.get(name) === 0)
|
|
546
|
+
.sort((a, b) => {
|
|
547
|
+
const modA = modules.get(a)
|
|
548
|
+
const modB = modules.get(b)
|
|
549
|
+
return this._getPriority(modA!) - this._getPriority(modB!)
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
const result: string[] = []
|
|
553
|
+
|
|
554
|
+
while (queue.length > 0) {
|
|
555
|
+
// Take first (lowest priority)
|
|
556
|
+
const current = queue.shift()!
|
|
557
|
+
result.push(current)
|
|
558
|
+
|
|
559
|
+
// Reduce in-degree for dependents
|
|
560
|
+
for (const dep of dependents.get(current) || []) {
|
|
561
|
+
const newDegree = (inDegree.get(dep) || 0) - 1
|
|
562
|
+
inDegree.set(dep, newDegree)
|
|
563
|
+
|
|
564
|
+
if (newDegree === 0) {
|
|
565
|
+
queue.push(dep)
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Re-sort queue by priority
|
|
570
|
+
queue.sort((a, b) => {
|
|
571
|
+
const modA = modules.get(a)
|
|
572
|
+
const modB = modules.get(b)
|
|
573
|
+
return this._getPriority(modA!) - this._getPriority(modB!)
|
|
574
|
+
})
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Check for cycles
|
|
578
|
+
if (result.length !== names.length) {
|
|
579
|
+
// Find the cycle for error message
|
|
580
|
+
const cycle = this._findCycle(modules)
|
|
581
|
+
throw new CircularDependencyError(cycle)
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return result
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Find a cycle in the dependency graph for error reporting
|
|
589
|
+
*/
|
|
590
|
+
private _findCycle(modules: Map<string, ModuleLike>): string[] {
|
|
591
|
+
const visited = new Set<string>()
|
|
592
|
+
const stack = new Set<string>()
|
|
593
|
+
const path: string[] = []
|
|
594
|
+
|
|
595
|
+
const dfs = (name: string): string[] | null => {
|
|
596
|
+
if (stack.has(name)) {
|
|
597
|
+
// Found cycle - extract it from path
|
|
598
|
+
const cycleStart = path.indexOf(name)
|
|
599
|
+
return [...path.slice(cycleStart), name]
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
if (visited.has(name)) {
|
|
603
|
+
return null
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
visited.add(name)
|
|
607
|
+
stack.add(name)
|
|
608
|
+
path.push(name)
|
|
609
|
+
|
|
610
|
+
const module = modules.get(name)
|
|
611
|
+
const requires = module ? this._getRequires(module) : []
|
|
612
|
+
|
|
613
|
+
for (const req of requires) {
|
|
614
|
+
if (modules.has(req)) {
|
|
615
|
+
const cycle = dfs(req)
|
|
616
|
+
if (cycle) {
|
|
617
|
+
return cycle
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
stack.delete(name)
|
|
623
|
+
path.pop()
|
|
624
|
+
return null
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
for (const name of modules.keys()) {
|
|
628
|
+
const cycle = dfs(name)
|
|
629
|
+
if (cycle) {
|
|
630
|
+
return cycle
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
return ['unknown cycle']
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Factory function to create a ModuleLoader instance
|
|
640
|
+
*/
|
|
641
|
+
export function createModuleLoader(): ModuleLoader {
|
|
642
|
+
return new ModuleLoader()
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export default ModuleLoader
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSEBridge — qdadm re-export.
|
|
3
|
+
*
|
|
4
|
+
* The actual implementation now lives in `@quazardous/qdcore` so it can be
|
|
5
|
+
* shared with qdcms. The auth-aware connect/disconnect behaviour is now
|
|
6
|
+
* configurable via `connectOnSignal` / `disconnectOnSignal` options; defaults
|
|
7
|
+
* remain `'auth:login'` / `'auth:logout'` for backwards compatibility.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export { SSEBridge, createSSEBridge, SSE_SIGNALS } from '@quazardous/qdcore'
|
|
11
|
+
export type { SSEBridgeOptions } from '@quazardous/qdcore'
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SignalBus - qdadm wiring.
|
|
3
|
+
*
|
|
4
|
+
* The dispatcher itself now lives in `@quazardous/qdcore` so it can be shared
|
|
5
|
+
* with qdcms. This module re-exports the generic API and adds the qdadm
|
|
6
|
+
* application-level signal-name registry (`SIGNALS`).
|
|
7
|
+
*
|
|
8
|
+
* Signal naming conventions (qdadm flavor):
|
|
9
|
+
* - Generic CRUD: entity:created, entity:updated, entity:deleted
|
|
10
|
+
* - Entity-specific: {entityName}:created, etc. — built via `buildSignal()`
|
|
11
|
+
* - Auth: auth:login, auth:logout, auth:expired
|
|
12
|
+
* - API: api:error
|
|
13
|
+
*
|
|
14
|
+
* Wildcard subscriptions are inherited from QuarKernel:
|
|
15
|
+
* - 'entity:*' matches entity:created/updated/deleted
|
|
16
|
+
* - '*:created' matches any creation
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
SignalBus,
|
|
21
|
+
createSignalBus,
|
|
22
|
+
buildSignal,
|
|
23
|
+
SIGNAL_ACTIONS,
|
|
24
|
+
} from '@quazardous/qdcore'
|
|
25
|
+
export type {
|
|
26
|
+
SignalBusOptions,
|
|
27
|
+
SignalAction,
|
|
28
|
+
EntitySignalPayload,
|
|
29
|
+
} from '@quazardous/qdcore'
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* qdadm signal-name registry.
|
|
33
|
+
* Application-level convention; kept in qdadm because qdcore is naming-agnostic.
|
|
34
|
+
*/
|
|
35
|
+
export const SIGNALS = {
|
|
36
|
+
// Generic entity lifecycle signals
|
|
37
|
+
ENTITY_CREATED: 'entity:created',
|
|
38
|
+
ENTITY_UPDATED: 'entity:updated',
|
|
39
|
+
ENTITY_DELETED: 'entity:deleted',
|
|
40
|
+
|
|
41
|
+
// Auth lifecycle signals
|
|
42
|
+
AUTH_LOGIN: 'auth:login',
|
|
43
|
+
AUTH_LOGOUT: 'auth:logout',
|
|
44
|
+
AUTH_EXPIRED: 'auth:expired', // Emitted on 401/403 API responses
|
|
45
|
+
|
|
46
|
+
// API error signals
|
|
47
|
+
API_ERROR: 'api:error', // Emitted on any API error { status, message, url }
|
|
48
|
+
} as const
|