@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,280 @@
1
+ /**
2
+ * Patch KernelContext prototype with routing-related methods:
3
+ * - routes(basePath, routes, opts)
4
+ * - navItem(item)
5
+ * - routeFamily(base, prefixes)
6
+ * - crud(entity, pages, options) — convention CRUD generator
7
+ * - childPage(parent, page, options) — non-entity child route
8
+ *
9
+ * The string helpers (`singularize`, `toKebab`, `capitalize`) are kept
10
+ * module-local — they're pure, stateless, and were only used by `crud()` and
11
+ * `childPage()`. Keeping them off the prototype keeps the public shape clean.
12
+ */
13
+
14
+ import type { RouteRecordRaw } from 'vue-router'
15
+ import { registry, getRoutes } from '../module/moduleRegistry'
16
+ import type {
17
+ ChildPageOptions,
18
+ CrudOptions,
19
+ CrudPages,
20
+ NavItem,
21
+ ParentConfig,
22
+ RouteOptions,
23
+ } from './KernelContext.types'
24
+
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ type Self = any
27
+
28
+ /** Singularize a plural word (simple English rules) */
29
+ function singularize(plural: string): string {
30
+ if (plural.endsWith('ies')) return plural.slice(0, -3) + 'y'
31
+ if (plural.endsWith('ses') || plural.endsWith('xes') || plural.endsWith('zes')) {
32
+ return plural.slice(0, -2)
33
+ }
34
+ if (plural.endsWith('s') && !plural.endsWith('ss')) return plural.slice(0, -1)
35
+ return plural
36
+ }
37
+
38
+ /** Convert camelCase to kebab-case (e.g. 'botTasks' → 'bot-tasks') */
39
+ function toKebab(str: string): string {
40
+ return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase()
41
+ }
42
+
43
+ /** Capitalize first letter */
44
+ function capitalize(str: string): string {
45
+ return str.charAt(0).toUpperCase() + str.slice(1)
46
+ }
47
+
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
+ export function applyRoutingMethods(KernelContextClass: { prototype: any }): void {
50
+ const proto = KernelContextClass.prototype as Self
51
+
52
+ proto.routes = function (
53
+ this: Self,
54
+ basePath: string,
55
+ routes: RouteRecordRaw[],
56
+ opts: RouteOptions = {}
57
+ ): Self {
58
+ registry.addRoutes(basePath, routes, opts)
59
+ return this
60
+ }
61
+
62
+ proto.navItem = function (this: Self, item: NavItem): Self {
63
+ registry.addNavItem(item)
64
+ return this
65
+ }
66
+
67
+ proto.routeFamily = function (this: Self, base: string, prefixes: string[]): Self {
68
+ registry.addRouteFamily(base, prefixes)
69
+ return this
70
+ }
71
+
72
+ /**
73
+ * Register standard CRUD routes with naming conventions
74
+ * (entity 'books' → route prefix 'book', /books → 'book', etc.)
75
+ */
76
+ proto.crud = function (
77
+ this: Self,
78
+ entity: string,
79
+ pages: CrudPages,
80
+ options: CrudOptions = {}
81
+ ): Self {
82
+ // Entity name is always used for permission binding.
83
+ // Manager may not be registered yet (child entity before parent module loads).
84
+ const entityBinding = entity
85
+ const manager = this._kernel.orchestrator?.isRegistered(entity)
86
+ ? this._kernel.orchestrator.get(entity)
87
+ : null
88
+ const idParam = manager?.idField || 'id'
89
+
90
+ // Handle parent route configuration
91
+ const urlSegment = options.pathSegment || toKebab(entity)
92
+ let basePath = urlSegment
93
+ let parentConfig: ParentConfig | null = null
94
+ let parentRoutePrefix: string | null = null
95
+
96
+ if (options.parentRoute) {
97
+ const parentRouteName = options.parentRoute
98
+ const allRoutes = getRoutes()
99
+ const parentRoute = allRoutes.find((r) => r.name === parentRouteName)
100
+
101
+ if (parentRoute) {
102
+ const parentEntityName = parentRoute.meta?.entity
103
+ const parentManager = parentEntityName
104
+ ? this._kernel.orchestrator?.get(parentEntityName)
105
+ : null
106
+ const parentIdParam = parentManager?.idField || 'id'
107
+
108
+ // Build base path: parentPath/:parentId/entity (e.g., books/:bookId/loans)
109
+ const parentBasePath =
110
+ parentRoute.path.replace(/\/(create|:.*)?$/, '') || parentEntityName
111
+ basePath = `${parentBasePath}/:${parentIdParam}/${urlSegment}`
112
+
113
+ if (parentEntityName) {
114
+ parentConfig = {
115
+ entity: parentEntityName,
116
+ param: parentIdParam,
117
+ foreignKey: options.foreignKey || `${singularize(parentEntityName)}_id`,
118
+ }
119
+ }
120
+
121
+ parentRoutePrefix = parentRouteName
122
+ }
123
+ }
124
+
125
+ // Derive route prefix:
126
+ // With parent: 'book' + '-loan' → 'book-loan'
127
+ // Without parent: 'books' → 'book'
128
+ const routePrefix =
129
+ options.routePrefix ||
130
+ (parentRoutePrefix
131
+ ? `${parentRoutePrefix}-${singularize(entity)}`
132
+ : singularize(entity))
133
+
134
+ const routes: RouteRecordRaw[] = []
135
+
136
+ if (pages.list) {
137
+ routes.push({
138
+ path: '',
139
+ name: routePrefix,
140
+ component: pages.list,
141
+ meta: { layout: 'list' },
142
+ })
143
+ }
144
+
145
+ if (pages.show) {
146
+ routes.push({
147
+ path: `:${idParam}`,
148
+ name: `${routePrefix}-show`,
149
+ component: pages.show,
150
+ meta: { layout: 'show' },
151
+ })
152
+ }
153
+
154
+ if (pages.form) {
155
+ // Single form pattern (recommended)
156
+ routes.push({
157
+ path: 'create',
158
+ name: `${routePrefix}-create`,
159
+ component: pages.form,
160
+ })
161
+ routes.push({
162
+ path: `:${idParam}/edit`,
163
+ name: `${routePrefix}-edit`,
164
+ component: pages.form,
165
+ })
166
+ } else {
167
+ if (pages.create) {
168
+ routes.push({
169
+ path: 'create',
170
+ name: `${routePrefix}-create`,
171
+ component: pages.create,
172
+ })
173
+ }
174
+ if (pages.edit) {
175
+ routes.push({
176
+ path: `:${idParam}/edit`,
177
+ name: `${routePrefix}-edit`,
178
+ component: pages.edit,
179
+ })
180
+ }
181
+ }
182
+
183
+ const routeOpts: RouteOptions = {}
184
+ // Set entity if registered, OR if this is a child route — child routes
185
+ // need entity binding for permission checks even before the manager
186
+ // exists.
187
+ if (manager || parentConfig) {
188
+ routeOpts.entity = entityBinding
189
+ }
190
+ if (parentConfig) {
191
+ routeOpts.parent = parentConfig
192
+ }
193
+ if (options.label) {
194
+ routeOpts.label = options.label
195
+ }
196
+
197
+ this.routes(basePath, routes, routeOpts)
198
+ this.routeFamily(routePrefix, [`${routePrefix}-`])
199
+
200
+ if (options.nav) {
201
+ const label = options.nav.label || capitalize(entity)
202
+ const navItem: NavItem = {
203
+ section: options.nav.section,
204
+ route: routePrefix,
205
+ icon: options.nav.icon,
206
+ label,
207
+ }
208
+ // Only set entity on nav item if registered (avoids permission check
209
+ // failure). Routes always get entity binding, but nav items need it
210
+ // resolvable.
211
+ if (manager) {
212
+ navItem.entity = entityBinding
213
+ }
214
+ this.navItem(navItem)
215
+ }
216
+
217
+ return this
218
+ }
219
+
220
+ /**
221
+ * Register a custom child page on an entity item (e.g. /books/:id/statistics).
222
+ */
223
+ proto.childPage = function (
224
+ this: Self,
225
+ parentRouteName: string,
226
+ pageName: string,
227
+ options: ChildPageOptions
228
+ ): Self {
229
+ const allRoutes = getRoutes()
230
+ const parentRoute = allRoutes.find((r) => r.name === parentRouteName)
231
+
232
+ if (!parentRoute) {
233
+ console.warn(`[qdadm] childPage: parent route '${parentRouteName}' not found`)
234
+ return this
235
+ }
236
+
237
+ const parentEntityName = parentRoute.meta?.entity as string | undefined
238
+ const parentManager = parentEntityName
239
+ ? this._kernel.orchestrator?.get(parentEntityName)
240
+ : null
241
+ const parentIdParam = parentManager?.idField || 'id'
242
+
243
+ const parentBasePath =
244
+ parentRoute.path.replace(/\/(create|:.*)?$/, '') || parentEntityName
245
+ const basePath = `${parentBasePath}/:${parentIdParam}/${pageName}`
246
+
247
+ const routeName = `${parentRouteName}-${pageName}`
248
+
249
+ const parentConfig: ParentConfig | undefined = parentEntityName
250
+ ? { entity: parentEntityName, param: parentIdParam }
251
+ : undefined
252
+
253
+ const routeOpts: RouteOptions = {}
254
+ if (parentConfig) {
255
+ routeOpts.parent = parentConfig
256
+ }
257
+ if (options.label) {
258
+ routeOpts.label = options.label
259
+ }
260
+
261
+ this.routes(
262
+ basePath,
263
+ [
264
+ {
265
+ path: '',
266
+ name: routeName,
267
+ component: options.component,
268
+ meta: {
269
+ layout: 'page',
270
+ ...(options.icon && { icon: options.icon }),
271
+ ...options.meta,
272
+ },
273
+ },
274
+ ],
275
+ routeOpts
276
+ )
277
+
278
+ return this
279
+ }
280
+ }
@@ -0,0 +1,252 @@
1
+ /**
2
+ * KernelContext - Fluent API wrapper for module registration.
3
+ *
4
+ * Provides a chainable interface for modules to register entities, routes,
5
+ * navigation items, zones, blocks, signal handlers, hooks, permissions and
6
+ * translations.
7
+ *
8
+ * The class shell here keeps the constructor + service getters; the fluent
9
+ * registration methods live in dedicated patch modules following the same
10
+ * pattern as `Kernel.ts`:
11
+ *
12
+ * - KernelContext.entities.ts — entity, userEntity
13
+ * - KernelContext.routing.ts — routes, navItem, routeFamily, crud, childPage
14
+ * - KernelContext.zones.ts — zone, block, provide, component
15
+ * - KernelContext.events.ts — on, hook, defer
16
+ * - KernelContext.permissions.ts — permissions, entityPermissions
17
+ * - KernelContext.i18n.ts — messages, aliases, messagesProvider
18
+ *
19
+ * Usage in a module:
20
+ * ```ts
21
+ * class UsersModule extends Module {
22
+ * async connect(ctx: KernelContext) {
23
+ * ctx
24
+ * .entity('users', { storage: 'api:/api/users' })
25
+ * .routes('users', [
26
+ * { path: '', name: 'users', component: UserList },
27
+ * { path: ':id', name: 'users-edit', component: UserEdit }
28
+ * ])
29
+ * .navItem({ section: 'Admin', route: 'users', icon: 'pi pi-users', label: 'Users' })
30
+ * .zone('users-list-header')
31
+ * .block('users-list-header', { component: UserStats, weight: 10 })
32
+ * .on('users:created', (event) => console.log('User created:', event))
33
+ * }
34
+ * }
35
+ * ```
36
+ */
37
+
38
+ import type { App, Component } from 'vue'
39
+ import type { Router, RouteRecordRaw } from 'vue-router'
40
+ import type { ListenerOptions } from '@quazardous/quarkernel'
41
+ import type { SignalBus } from './SignalBus'
42
+ import type { Orchestrator } from '../orchestrator/Orchestrator'
43
+ import type { HookRegistry } from '../hooks/HookRegistry'
44
+ import type { ZoneRegistry } from '../zones/ZoneRegistry'
45
+ import type { DeferredRegistry } from '../deferred/DeferredRegistry.js'
46
+ import type { SecurityChecker } from '../entity/auth/SecurityChecker'
47
+ import type { PermissionRegistry } from '../security/PermissionRegistry'
48
+ import type { EntityManager } from '../entity/EntityManager'
49
+ import type { I18n } from '../i18n/I18n'
50
+ import type { AliasPattern, MessagesBundle, TranslationProvider } from '../i18n/types'
51
+
52
+ // Internal types (shared with patch modules, not exported here).
53
+ import type {
54
+ AuthAdapter,
55
+ DeferredFactory,
56
+ HookHandler,
57
+ KernelInterface,
58
+ ModuleWithCleanup,
59
+ SignalHandler,
60
+ } from './KernelContext.types'
61
+
62
+ // Public types — re-exported to keep the existing barrel API stable.
63
+ export type {
64
+ BlockConfig,
65
+ ChildPageOptions,
66
+ CrudOptions,
67
+ CrudPages,
68
+ NavItem,
69
+ ParentConfig,
70
+ PermissionMeta,
71
+ PermissionOptions,
72
+ RouteOptions,
73
+ UserEntityOptions,
74
+ ZoneOptions,
75
+ } from './KernelContext.types'
76
+ import type {
77
+ BlockConfig,
78
+ ChildPageOptions,
79
+ CrudOptions,
80
+ CrudPages,
81
+ NavItem,
82
+ PermissionMeta,
83
+ PermissionOptions,
84
+ RouteOptions,
85
+ UserEntityOptions,
86
+ ZoneOptions,
87
+ } from './KernelContext.types'
88
+
89
+ // Prototype-patched method groups
90
+ import { applyEntityMethods } from './KernelContext.entities'
91
+ import { applyRoutingMethods } from './KernelContext.routing'
92
+ import { applyZoneMethods } from './KernelContext.zones'
93
+ import { applyEventMethods } from './KernelContext.events'
94
+ import { applyPermissionMethods } from './KernelContext.permissions'
95
+ import { applyI18nMethods } from './KernelContext.i18n'
96
+
97
+ export class KernelContext {
98
+ // Marked public so the patch modules (which read this.* via Self=any) can
99
+ // see them. They were `private` in the previous monolithic file but were
100
+ // already accessed across patch boundaries the moment we split.
101
+ _kernel: KernelInterface
102
+ _module: ModuleWithCleanup | null
103
+
104
+ constructor(kernel: KernelInterface, module: ModuleWithCleanup | null) {
105
+ this._kernel = kernel
106
+ this._module = module
107
+ }
108
+
109
+ // ─────────────────────────────────────────────────────────────────────────────
110
+ // Getters for kernel services
111
+ // ─────────────────────────────────────────────────────────────────────────────
112
+
113
+ /** Vue app instance */
114
+ get app(): App | null {
115
+ return this._kernel.vueApp
116
+ }
117
+
118
+ /** Vue router instance */
119
+ get router(): Router | null {
120
+ return this._kernel.router
121
+ }
122
+
123
+ /** SignalBus instance */
124
+ get signals(): SignalBus | null {
125
+ return this._kernel.signals
126
+ }
127
+
128
+ /** Orchestrator instance (entity managers) */
129
+ get orchestrator(): Orchestrator | null {
130
+ return this._kernel.orchestrator
131
+ }
132
+
133
+ /** ZoneRegistry instance */
134
+ get zones(): ZoneRegistry | null {
135
+ return this._kernel.zoneRegistry
136
+ }
137
+
138
+ /** HookRegistry instance */
139
+ get hooks(): HookRegistry | null {
140
+ return this._kernel.hookRegistry
141
+ }
142
+
143
+ /** DeferredRegistry instance */
144
+ get deferred(): DeferredRegistry | null {
145
+ return this._kernel.deferred
146
+ }
147
+
148
+ /** Whether running in development mode */
149
+ get isDev(): boolean {
150
+ return (import.meta as unknown as { env?: { DEV?: boolean } }).env?.DEV ?? false
151
+ }
152
+
153
+ /** Debug mode from kernel options */
154
+ get debug(): boolean {
155
+ return this._kernel.options?.debug ?? false
156
+ }
157
+
158
+ /** Auth adapter */
159
+ get authAdapter(): AuthAdapter | null {
160
+ return this._kernel.options?.authAdapter ?? null
161
+ }
162
+
163
+ /** Security checker (role hierarchy, permissions) */
164
+ get security(): SecurityChecker | null {
165
+ return this._kernel.securityChecker
166
+ }
167
+
168
+ /** Permission registry */
169
+ get permissionRegistry(): PermissionRegistry | null {
170
+ return this._kernel.permissionRegistry
171
+ }
172
+
173
+ /** Auth adapter shortcut */
174
+ get auth(): AuthAdapter | null {
175
+ return this._kernel.options?.authAdapter ?? null
176
+ }
177
+
178
+ /** ActiveStack instance (sync navigation context) */
179
+ get activeStack(): unknown {
180
+ return this._kernel.activeStack ?? null
181
+ }
182
+
183
+ /** StackHydrator instance (async data loading) */
184
+ get stackHydrator(): unknown {
185
+ return this._kernel.stackHydrator ?? null
186
+ }
187
+
188
+ /** I18n subsystem */
189
+ get i18n(): I18n | null {
190
+ return this._kernel.i18nInstance ?? null
191
+ }
192
+ }
193
+
194
+ // Declare prototype-patched methods (declaration merging)
195
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
196
+ export interface KernelContext {
197
+ // Entities (KernelContext.entities.ts)
198
+ entity(name: string, config: string | Record<string, unknown> | EntityManager): this
199
+ userEntity(options: UserEntityOptions): this
200
+
201
+ // Routing (KernelContext.routing.ts)
202
+ routes(basePath: string, routes: RouteRecordRaw[], opts?: RouteOptions): this
203
+ navItem(item: NavItem): this
204
+ routeFamily(base: string, prefixes: string[]): this
205
+ crud(entity: string, pages: CrudPages, options?: CrudOptions): this
206
+ childPage(parentRouteName: string, pageName: string, options: ChildPageOptions): this
207
+
208
+ // Zones / Vue (KernelContext.zones.ts)
209
+ zone(name: string, options?: ZoneOptions): this
210
+ block(zoneName: string, config: BlockConfig): this
211
+ provide(key: string | symbol, value: unknown): this
212
+ component(name: string, component: Component): this
213
+
214
+ // Events (KernelContext.events.ts)
215
+ on(signal: string, handler: SignalHandler, options?: ListenerOptions): this
216
+ hook(hookName: string, handler: HookHandler, options?: Record<string, unknown>): this
217
+ defer(name: string, factory: DeferredFactory): this
218
+
219
+ // Permissions (KernelContext.permissions.ts)
220
+ permissions(
221
+ namespace: string,
222
+ permissions: Record<string, string | PermissionMeta>,
223
+ options?: PermissionOptions
224
+ ): this
225
+ entityPermissions(
226
+ entity: string,
227
+ permissions: Record<string, string | PermissionMeta>
228
+ ): this
229
+
230
+ // i18n (KernelContext.i18n.ts)
231
+ messages(locale: string, bundle: MessagesBundle): this
232
+ aliases(patterns: AliasPattern[]): this
233
+ messagesProvider(provider: TranslationProvider): this
234
+ }
235
+
236
+ // Apply prototype patches
237
+ applyEntityMethods(KernelContext)
238
+ applyRoutingMethods(KernelContext)
239
+ applyZoneMethods(KernelContext)
240
+ applyEventMethods(KernelContext)
241
+ applyPermissionMethods(KernelContext)
242
+ applyI18nMethods(KernelContext)
243
+
244
+ /**
245
+ * Factory function to create a KernelContext instance.
246
+ */
247
+ export function createKernelContext(
248
+ kernel: KernelInterface,
249
+ module: ModuleWithCleanup | null
250
+ ): KernelContext {
251
+ return new KernelContext(kernel, module)
252
+ }
@@ -0,0 +1,173 @@
1
+ /**
2
+ * KernelContext types - shared between the class shell and the prototype-patch
3
+ * modules (KernelContext.{entities,routing,zones,events,permissions,i18n}.ts).
4
+ *
5
+ * Internal types (ModuleWithCleanup, AuthAdapter, KernelOptions,
6
+ * KernelInterface, function types) stay un-exported by KernelContext.ts so
7
+ * they remain private to qdadm internals; the public types (NavItem,
8
+ * ZoneOptions, …) are re-exported from KernelContext.ts to preserve the
9
+ * existing barrel API.
10
+ */
11
+
12
+ import type { App, Component } from 'vue'
13
+ import type { Router } from 'vue-router'
14
+ import type { UsersManagerOptions } from '../security/UsersManager'
15
+ import type { SignalBus } from './SignalBus'
16
+ import type { Orchestrator } from '../orchestrator/Orchestrator'
17
+ import type { HookRegistry } from '../hooks/HookRegistry'
18
+ import type { ZoneRegistry } from '../zones/ZoneRegistry'
19
+ import type { DeferredRegistry } from '../deferred/DeferredRegistry.js'
20
+ import type { SecurityChecker } from '../entity/auth/SecurityChecker'
21
+ import type { PermissionRegistry } from '../security/PermissionRegistry'
22
+ import type { I18n } from '../i18n/I18n'
23
+
24
+ // ─────────────────────────────────────────────────────────────────────────────
25
+ // Internal types — shared across patch modules, not exposed by index.ts
26
+ // ─────────────────────────────────────────────────────────────────────────────
27
+
28
+ /** Extended Module interface with internal cleanup method */
29
+ export interface ModuleWithCleanup {
30
+ constructor?: { name?: string }
31
+ _addSignalCleanup?: (cleanup: () => void) => void
32
+ }
33
+
34
+ /** Auth adapter interface */
35
+ export interface AuthAdapter {
36
+ getUser(): unknown
37
+ [key: string]: unknown
38
+ }
39
+
40
+ /** Kernel options interface */
41
+ export interface KernelOptions {
42
+ debug?: boolean
43
+ authAdapter?: AuthAdapter | null
44
+ storageResolver?: unknown
45
+ managerResolver?: unknown
46
+ managerRegistry?: Record<string, unknown>
47
+ [key: string]: unknown
48
+ }
49
+
50
+ /** Kernel interface for type safety inside KernelContext */
51
+ export interface KernelInterface {
52
+ vueApp: App | null
53
+ router: Router | null
54
+ signals: SignalBus | null
55
+ orchestrator: Orchestrator | null
56
+ zoneRegistry: ZoneRegistry | null
57
+ hookRegistry: HookRegistry | null
58
+ deferred: DeferredRegistry | null
59
+ securityChecker: SecurityChecker | null
60
+ permissionRegistry: PermissionRegistry | null
61
+ activeStack: unknown
62
+ stackHydrator: unknown
63
+ options: KernelOptions
64
+ i18nInstance: I18n | null
65
+ _pendingProvides: Map<string | symbol, unknown>
66
+ _pendingComponents: Map<string, Component>
67
+ }
68
+
69
+ /** Signal handler function type */
70
+ export type SignalHandler = (event: { data: unknown; [key: string]: unknown }) => void
71
+
72
+ /** Hook handler function type */
73
+ export type HookHandler = (context: unknown) => unknown | Promise<unknown>
74
+
75
+ /** Deferred factory function type */
76
+ export type DeferredFactory = () => Promise<void>
77
+
78
+ // ─────────────────────────────────────────────────────────────────────────────
79
+ // Public types — re-exported by KernelContext.ts (and ultimately by index.ts)
80
+ // ─────────────────────────────────────────────────────────────────────────────
81
+
82
+ /** Navigation item configuration */
83
+ export interface NavItem {
84
+ section: string
85
+ route: string
86
+ label: string
87
+ icon?: string
88
+ exact?: boolean
89
+ entity?: string
90
+ }
91
+
92
+ /** Zone configuration */
93
+ export interface ZoneOptions {
94
+ default?: Component
95
+ }
96
+
97
+ /** Block configuration */
98
+ export interface BlockConfig {
99
+ component: Component
100
+ weight?: number
101
+ props?: Record<string, unknown>
102
+ id?: string
103
+ operation?: 'add' | 'replace' | 'extend' | 'wrap'
104
+ /** Block ID to replace (required if operation='replace') */
105
+ replaces?: string
106
+ /** Block ID to insert before (for operation='extend') */
107
+ before?: string
108
+ /** Block ID to insert after (for operation='extend') */
109
+ after?: string
110
+ /** Block ID to wrap (required if operation='wrap') */
111
+ wraps?: string
112
+ }
113
+
114
+ /** Route options */
115
+ export interface RouteOptions {
116
+ entity?: string
117
+ parent?: ParentConfig
118
+ label?: string
119
+ layout?: string
120
+ }
121
+
122
+ /** Parent route configuration */
123
+ export interface ParentConfig {
124
+ entity: string
125
+ param: string
126
+ foreignKey?: string
127
+ }
128
+
129
+ /** CRUD page components */
130
+ export interface CrudPages {
131
+ list?: () => Promise<{ default: Component }>
132
+ show?: () => Promise<{ default: Component }>
133
+ form?: () => Promise<{ default: Component }>
134
+ create?: () => Promise<{ default: Component }>
135
+ edit?: () => Promise<{ default: Component }>
136
+ }
137
+
138
+ /** CRUD options */
139
+ export interface CrudOptions {
140
+ nav?: {
141
+ section: string
142
+ icon?: string
143
+ label?: string
144
+ }
145
+ routePrefix?: string
146
+ /** Override the URL path segment (defaults to entity name). Useful for camelCase entities like 'jobTasks' → 'tasks'. */
147
+ pathSegment?: string
148
+ parentRoute?: string
149
+ foreignKey?: string
150
+ label?: string
151
+ }
152
+
153
+ /** Child page options for non-entity child routes */
154
+ export interface ChildPageOptions {
155
+ component: () => Promise<{ default: Component }>
156
+ label?: string
157
+ icon?: string
158
+ meta?: Record<string, unknown>
159
+ }
160
+
161
+ /** User entity options */
162
+ export type UserEntityOptions = UsersManagerOptions
163
+
164
+ /** Permission metadata */
165
+ export interface PermissionMeta {
166
+ label: string
167
+ description?: string
168
+ }
169
+
170
+ /** Permission options */
171
+ export interface PermissionOptions {
172
+ isEntity?: boolean
173
+ }