@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 quazardous
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # qdadm
2
+
3
+ **Vue 3 admin framework. PrimeVue. Zero boilerplate.**
4
+
5
+ Quick start: [../../README.md](../../README.md) (5 min tutorial)
6
+
7
+ Concepts & patterns: [QDADM_CREDO.md](QDADM_CREDO.md)
8
+
9
+ Changelog: [../../CHANGELOG.md](../../CHANGELOG.md)
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @quazardous/qdadm primevue @primeuix/themes
15
+ ```
16
+
17
+ ## Exports
18
+
19
+ ```js
20
+ // Core
21
+ import { Kernel, EntityManager } from '@quazardous/qdadm'
22
+
23
+ // Storage backends
24
+ import { MockApiStorage, ApiStorage, SdkStorage } from '@quazardous/qdadm'
25
+
26
+ // Auth
27
+ import { SessionAuthAdapter, LocalStorageSessionAuthAdapter } from '@quazardous/qdadm'
28
+
29
+ // Security (permissions, roles)
30
+ import { SecurityChecker, PermissionMatcher, PermissionRegistry } from '@quazardous/qdadm/security'
31
+ import { PersistableRoleProvider, createLocalStorageRolesProvider } from '@quazardous/qdadm/security'
32
+
33
+ // Composables
34
+ import { useForm, useBareForm, useListPageBuilder } from '@quazardous/qdadm/composables'
35
+
36
+ // Components
37
+ import { ListPage, PageLayout, AppLayout, FormField, FormActions } from '@quazardous/qdadm/components'
38
+
39
+ // Module system
40
+ import { KernelContext } from '@quazardous/qdadm/module'
41
+
42
+ // Styles
43
+ import '@quazardous/qdadm/styles'
44
+ ```
45
+
46
+ ## ctx.crud() - Route Helper
47
+
48
+ Register CRUD routes + navigation in one call:
49
+
50
+ ```js
51
+ class BooksModule extends Module {
52
+ async connect(ctx) {
53
+ ctx.entity('books', { ... })
54
+
55
+ // Full CRUD with single form
56
+ ctx.crud('books', {
57
+ list: () => import('./pages/BookList.vue'),
58
+ form: () => import('./pages/BookForm.vue') // create + edit
59
+ }, {
60
+ nav: { section: 'Library', icon: 'pi pi-book' }
61
+ })
62
+
63
+ // List only (read-only entity)
64
+ ctx.crud('settings', {
65
+ list: () => import('./pages/SettingsPage.vue')
66
+ }, { nav: { section: 'Config', icon: 'pi pi-cog' } })
67
+ }
68
+ }
69
+ ```
70
+
71
+ Auto-generates: routes, route family, nav item. Use `ctx.routes()` for custom pages.
72
+
73
+ ## SdkStorage
74
+
75
+ Adapter for generated SDK clients (hey-api, openapi-generator, etc.):
76
+
77
+ ```js
78
+ import { SdkStorage } from '@quazardous/qdadm'
79
+
80
+ const storage = new SdkStorage({
81
+ sdk,
82
+ methods: {
83
+ list: 'getApiBooks',
84
+ get: 'getApiBooksById',
85
+ create: 'postApiBooks',
86
+ update: 'patchApiBooksById',
87
+ delete: 'deleteApiBooksById'
88
+ }
89
+ })
90
+ ```
91
+
92
+ Options: `transformRequest`, `transformResponse`, `responseFormat`, `clientSidePagination`.
93
+
94
+ ## Documentation
95
+
96
+ | Doc | Purpose |
97
+ |-----|---------|
98
+ | [QDADM_CREDO](QDADM_CREDO.md) | Philosophy, patterns, concepts |
99
+ | [Architecture](../../docs/architecture.md) | PAC pattern, layers |
100
+ | [Extension](../../docs/extension.md) | Hooks, signals, zones |
101
+
102
+ ## Peer Dependencies
103
+
104
+ - vue ^3.3.0
105
+ - vue-router ^4.0.0
106
+ - primevue ^4.0.0
107
+ - pinia ^2.0.0
108
+
109
+ ## License
110
+
111
+ MIT
package/package.json ADDED
@@ -0,0 +1,101 @@
1
+ {
2
+ "name": "@quazardous/qdadm",
3
+ "version": "1.19.3",
4
+ "description": "Vue 3 framework for admin dashboards with PrimeVue",
5
+ "author": "quazardous",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/quazardous/qdadm.git"
10
+ },
11
+ "homepage": "https://github.com/quazardous/qdadm#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/quazardous/qdadm/issues"
14
+ },
15
+ "type": "module",
16
+ "main": "src/index.ts",
17
+ "scripts": {
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "type-check": "vue-tsc --noEmit",
21
+ "lint": "eslint src/",
22
+ "lint:fix": "eslint src/ --fix",
23
+ "format": "prettier --write src/",
24
+ "build:types": "vue-tsc --declaration --emitDeclarationOnly --outDir dist/types"
25
+ },
26
+ "exports": {
27
+ ".": "./src/index.ts",
28
+ "./auth": "./src/auth/index.ts",
29
+ "./security": "./src/security/index.ts",
30
+ "./composables": "./src/composables/index.ts",
31
+ "./components": "./src/components/index.ts",
32
+ "./editors": "./src/editors/index.ts",
33
+ "./module": "./src/module/index.ts",
34
+ "./utils": "./src/utils/index.ts",
35
+ "./modules/debug": "./src/modules/debug/index.ts",
36
+ "./styles": "./src/styles/index.scss",
37
+ "./styles/variables": "./src/styles/_variables.scss",
38
+ "./gen": "./src/gen/index.ts",
39
+ "./gen/vite-plugin": "./src/gen/vite-plugin.ts",
40
+ "./vite-plugin-debug": "./src/vite/qdadmDebugPlugin.ts"
41
+ },
42
+ "files": [
43
+ "src",
44
+ "README.md",
45
+ "LICENSE"
46
+ ],
47
+ "dependencies": {
48
+ "@quazardous/qdcore": "^0.2.1",
49
+ "@quazardous/qddebug": "^0.2.1",
50
+ "@quazardous/quarkernel": "^2.1.0",
51
+ "pluralize": "^8.0.0",
52
+ "yaml": "^2.8.4"
53
+ },
54
+ "peerDependencies": {
55
+ "pinia": "^2.0.0 || ^3.0.0",
56
+ "primevue": "^4.0.0",
57
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
58
+ "vue": "^3.3.0",
59
+ "vue-router": "^4.0.0"
60
+ },
61
+ "peerDependenciesMeta": {
62
+ "vanilla-jsoneditor": {
63
+ "optional": true
64
+ },
65
+ "vite": {
66
+ "optional": true
67
+ }
68
+ },
69
+ "optionalDependencies": {
70
+ "vanilla-jsoneditor": "^0.23.0"
71
+ },
72
+ "keywords": [
73
+ "vue",
74
+ "vue3",
75
+ "admin",
76
+ "dashboard",
77
+ "framework",
78
+ "primevue",
79
+ "crud",
80
+ "entity-manager"
81
+ ],
82
+ "devDependencies": {
83
+ "@eslint/js": "^9.39.2",
84
+ "@types/node": "^25.0.6",
85
+ "@vitejs/plugin-vue": "^6.0.0",
86
+ "@vue/test-utils": "^2.4.6",
87
+ "@vue/tsconfig": "^0.8.1",
88
+ "eslint": "^9.39.2",
89
+ "eslint-config-prettier": "^10.1.8",
90
+ "eslint-plugin-prettier": "^5.5.4",
91
+ "eslint-plugin-vue": "^10.6.2",
92
+ "globals": "^17.0.0",
93
+ "jsdom": "^25.0.1",
94
+ "prettier": "^3.7.4",
95
+ "sass": "^1.97.1",
96
+ "typescript": "^5.9.3",
97
+ "typescript-eslint": "^8.52.0",
98
+ "vitest": "^2.1.8",
99
+ "vue-tsc": "^3.2.2"
100
+ }
101
+ }
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
2
+ <polygon points="50,5 93,27.5 93,72.5 50,95 7,72.5 7,27.5" fill="#1E3A8A"/>
3
+ <text x="48" y="50" text-anchor="middle" dominant-baseline="central" font-family="system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif" font-size="58" font-weight="800" letter-spacing="-4">
4
+ <tspan fill="#60A5FA">Q</tspan><tspan fill="#93C5FD">D</tspan>
5
+ </text>
6
+ </svg>
@@ -0,0 +1,459 @@
1
+ /**
2
+ * SessionAuthAdapter - Base class for user authentication
3
+ *
4
+ * Applications extend this class to implement their authentication logic.
5
+ * The adapter handles user sessions: login, logout, token management.
6
+ *
7
+ * This is different from EntityAuthAdapter which handles entity-level permissions.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * class MyAuthAdapter extends SessionAuthAdapter {
12
+ * async login({ username, password }: LoginCredentials) {
13
+ * const response = await api.post('/auth/login', { username, password })
14
+ * this.setSession(response.token, response.user)
15
+ * return { token: response.token, user: response.user }
16
+ * }
17
+ *
18
+ * logout() {
19
+ * this.clearSession()
20
+ * }
21
+ * }
22
+ * ```
23
+ *
24
+ * @abstract
25
+ */
26
+
27
+ import type { SignalBus } from '../kernel/SignalBus'
28
+
29
+ /**
30
+ * Login credentials interface
31
+ */
32
+ export interface LoginCredentials {
33
+ username: string
34
+ password: string
35
+ [key: string]: unknown
36
+ }
37
+
38
+ /**
39
+ * Session data returned from login
40
+ */
41
+ export interface SessionData<TUser = AuthUser> {
42
+ token: string
43
+ user: TUser
44
+ }
45
+
46
+ /**
47
+ * Generic user object interface
48
+ */
49
+ export interface AuthUser {
50
+ id?: string | number
51
+ username?: string
52
+ email?: string
53
+ name?: string
54
+ roles?: string[]
55
+ [key: string]: unknown
56
+ }
57
+
58
+ /**
59
+ * Interface for session authentication adapters
60
+ */
61
+ export interface ISessionAuthAdapter<TUser = AuthUser> {
62
+ login(credentials: LoginCredentials): Promise<SessionData<TUser>>
63
+ logout(): void
64
+ isAuthenticated(): boolean
65
+ getToken(): string | null
66
+ getUser(): TUser | null
67
+ readonly user: TUser | null
68
+ }
69
+
70
+ /**
71
+ * Base class for session authentication
72
+ */
73
+ export class SessionAuthAdapter<TUser extends AuthUser = AuthUser> implements ISessionAuthAdapter<TUser> {
74
+ /**
75
+ * Internal session state
76
+ * @protected
77
+ */
78
+ protected _token: string | null = null
79
+ protected _user: TUser | null = null
80
+
81
+ /**
82
+ * Authenticate user with credentials
83
+ *
84
+ * @param credentials - Login credentials
85
+ * @returns Session data
86
+ * @throws Error If authentication fails
87
+ *
88
+ * @example
89
+ * const { token, user } = await adapter.login({ username: 'admin', password: 'secret' })
90
+ */
91
+ async login(_credentials: LoginCredentials): Promise<SessionData<TUser>> {
92
+ throw new Error('[SessionAuthAdapter] login() must be implemented by subclass')
93
+ }
94
+
95
+ /**
96
+ * End the current session
97
+ *
98
+ * Should clear all session data (tokens, user info).
99
+ * Called by AppLayout logout button and useAuth().logout()
100
+ */
101
+ logout(): void {
102
+ throw new Error('[SessionAuthAdapter] logout() must be implemented by subclass')
103
+ }
104
+
105
+ /**
106
+ * Check if user is currently authenticated
107
+ *
108
+ * @returns True if user has valid session
109
+ *
110
+ * @example
111
+ * if (adapter.isAuthenticated()) {
112
+ * // Show dashboard
113
+ * } else {
114
+ * // Redirect to login
115
+ * }
116
+ */
117
+ isAuthenticated(): boolean {
118
+ throw new Error('[SessionAuthAdapter] isAuthenticated() must be implemented by subclass')
119
+ }
120
+
121
+ /**
122
+ * Get the current authentication token
123
+ *
124
+ * Used by API clients to include in request headers.
125
+ *
126
+ * @returns JWT token or null if not authenticated
127
+ *
128
+ * @example
129
+ * const token = adapter.getToken()
130
+ * fetch('/api/data', {
131
+ * headers: { Authorization: `Bearer ${token}` }
132
+ * })
133
+ */
134
+ getToken(): string | null {
135
+ throw new Error('[SessionAuthAdapter] getToken() must be implemented by subclass')
136
+ }
137
+
138
+ /**
139
+ * Get the current user object
140
+ *
141
+ * Returns user data from the session. The shape depends on your backend.
142
+ *
143
+ * @returns User object or null if not authenticated
144
+ *
145
+ * @example
146
+ * const user = adapter.getUser()
147
+ * console.log(user.username, user.email, user.roles)
148
+ */
149
+ getUser(): TUser | null {
150
+ throw new Error('[SessionAuthAdapter] getUser() must be implemented by subclass')
151
+ }
152
+
153
+ /**
154
+ * Synchronous user getter (optional)
155
+ *
156
+ * Some implementations prefer a property instead of method.
157
+ * useAuth() supports both patterns.
158
+ */
159
+ get user(): TUser | null {
160
+ return this.getUser()
161
+ }
162
+
163
+ // ─────────────────────────────────────────────────────────────────
164
+ // Helper methods for subclasses
165
+ // ─────────────────────────────────────────────────────────────────
166
+
167
+ /**
168
+ * Set session data (helper for subclasses)
169
+ *
170
+ * @param token - Authentication token
171
+ * @param user - User object
172
+ * @protected
173
+ */
174
+ protected setSession(token: string, user: TUser): void {
175
+ this._token = token
176
+ this._user = user
177
+ }
178
+
179
+ /**
180
+ * Clear session data (helper for subclasses)
181
+ *
182
+ * @protected
183
+ */
184
+ protected clearSession(): void {
185
+ this._token = null
186
+ this._user = null
187
+ }
188
+
189
+ /**
190
+ * Destroy session (catastrophic/silent logout)
191
+ *
192
+ * Clears token WITHOUT triggering normal logout signals.
193
+ * Used by DebugBar to test how the app handles unexpected session loss.
194
+ */
195
+ destroySession(): void {
196
+ this._token = null
197
+ }
198
+
199
+ /**
200
+ * Validate that the adapter is properly configured
201
+ *
202
+ * Called during bootstrap to catch configuration errors early.
203
+ *
204
+ * @throws Error If required methods are not implemented
205
+ */
206
+ static validate(adapter: ISessionAuthAdapter): void {
207
+ const required: (keyof ISessionAuthAdapter)[] = ['login', 'logout', 'isAuthenticated', 'getToken', 'getUser']
208
+ const missing = required.filter(method => typeof adapter[method] !== 'function')
209
+
210
+ if (missing.length > 0) {
211
+ throw new Error(
212
+ `[SessionAuthAdapter] Missing required methods: ${missing.join(', ')}\n` +
213
+ 'Ensure your authAdapter implements all required methods or extends SessionAuthAdapter.'
214
+ )
215
+ }
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Stored session data structure
221
+ */
222
+ interface StoredSessionData<TUser = AuthUser> {
223
+ token: string
224
+ user: TUser
225
+ originalUser?: TUser
226
+ }
227
+
228
+ /**
229
+ * Configuration options for LocalStorageSessionAuthAdapter
230
+ */
231
+ export interface LocalStorageAuthConfig {
232
+ /** localStorage key for session data (default: 'qdadm_auth') */
233
+ storageKey?: string
234
+ }
235
+
236
+ /**
237
+ * LocalStorage-based SessionAuthAdapter implementation
238
+ *
239
+ * Ready-to-use adapter that stores session in localStorage.
240
+ * Extend and override login() to add your API call.
241
+ *
242
+ * @example
243
+ * ```ts
244
+ * class MyAuthAdapter extends LocalStorageSessionAuthAdapter {
245
+ * constructor() {
246
+ * super('my_app_auth') // localStorage key
247
+ * }
248
+ *
249
+ * async login({ username, password }: LoginCredentials) {
250
+ * const res = await fetch('/api/login', {
251
+ * method: 'POST',
252
+ * body: JSON.stringify({ username, password })
253
+ * })
254
+ * const data = await res.json()
255
+ * this.setSession(data.token, data.user)
256
+ * this.persist()
257
+ * return data
258
+ * }
259
+ * }
260
+ * ```
261
+ */
262
+ export class LocalStorageSessionAuthAdapter<TUser extends AuthUser = AuthUser> extends SessionAuthAdapter<TUser> {
263
+ /**
264
+ * Default configuration. Override to change defaults globally:
265
+ * @example
266
+ * LocalStorageSessionAuthAdapter.defaults.storageKey = 'my_app_auth'
267
+ */
268
+ static defaults: Required<LocalStorageAuthConfig> = {
269
+ storageKey: 'qdadm_auth'
270
+ }
271
+
272
+ protected _storageKey: string
273
+ protected _originalUser: TUser | null = null // Stores original user during impersonation
274
+
275
+ /**
276
+ * @param config - Configuration options or storage key string (for backward compatibility)
277
+ */
278
+ constructor(config?: LocalStorageAuthConfig | string) {
279
+ super()
280
+ // Support both string (legacy) and config object
281
+ if (typeof config === 'string') {
282
+ this._storageKey = config
283
+ } else {
284
+ this._storageKey = config?.storageKey ?? LocalStorageSessionAuthAdapter.defaults.storageKey
285
+ }
286
+ this._restore()
287
+ }
288
+
289
+ /**
290
+ * Restore session from localStorage on init
291
+ * @private
292
+ */
293
+ private _restore(): void {
294
+ try {
295
+ const stored = localStorage.getItem(this._storageKey)
296
+ if (stored) {
297
+ const { token, user, originalUser } = JSON.parse(stored) as StoredSessionData<TUser>
298
+ this._token = token
299
+ this._user = user
300
+ this._originalUser = originalUser ?? null
301
+ }
302
+ } catch {
303
+ // Invalid stored data, ignore
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Persist session to localStorage
309
+ * Call after login() to save session
310
+ * @protected
311
+ */
312
+ protected persist(): void {
313
+ if (this._token && this._user) {
314
+ const data: StoredSessionData<TUser> = {
315
+ token: this._token,
316
+ user: this._user
317
+ }
318
+ if (this._originalUser) {
319
+ data.originalUser = this._originalUser
320
+ }
321
+ localStorage.setItem(this._storageKey, JSON.stringify(data))
322
+ } else {
323
+ localStorage.removeItem(this._storageKey)
324
+ }
325
+ }
326
+
327
+ // Arrow functions to preserve `this` when used as callbacks
328
+ logout = (): void => {
329
+ this._originalUser = null
330
+ this.clearSession()
331
+ localStorage.removeItem(this._storageKey)
332
+ }
333
+
334
+ isAuthenticated = (): boolean => {
335
+ return !!this._token
336
+ }
337
+
338
+ getToken = (): string | null => {
339
+ return this._token
340
+ }
341
+
342
+ getUser = (): TUser | null => {
343
+ return this._user
344
+ }
345
+
346
+ // ─────────────────────────────────────────────────────────────────
347
+ // Impersonation support
348
+ // ─────────────────────────────────────────────────────────────────
349
+
350
+ /**
351
+ * Check if currently impersonating another user
352
+ */
353
+ isImpersonating = (): boolean => {
354
+ return this._originalUser !== null
355
+ }
356
+
357
+ /**
358
+ * Get the original user (admin) when impersonating
359
+ * @returns Original user or null if not impersonating
360
+ */
361
+ getOriginalUser = (): TUser | null => {
362
+ return this._originalUser
363
+ }
364
+
365
+ /**
366
+ * Start impersonating another user
367
+ *
368
+ * @param targetUser - User to impersonate
369
+ * @returns The impersonated user
370
+ */
371
+ impersonate = (targetUser: TUser): TUser => {
372
+ if (!this._user) {
373
+ throw new Error('Must be authenticated to impersonate')
374
+ }
375
+ if (this._originalUser) {
376
+ throw new Error('Already impersonating. Stop first.')
377
+ }
378
+
379
+ this._originalUser = this._user
380
+ this._user = targetUser
381
+ this.persist()
382
+
383
+ return targetUser
384
+ }
385
+
386
+ /**
387
+ * Stop impersonating and return to original user
388
+ *
389
+ * @returns The original user
390
+ */
391
+ stopImpersonating = (): TUser => {
392
+ if (!this._originalUser) {
393
+ throw new Error('Not currently impersonating')
394
+ }
395
+
396
+ const original = this._originalUser
397
+ this._user = this._originalUser
398
+ this._originalUser = null
399
+ this.persist()
400
+
401
+ return original
402
+ }
403
+
404
+ /**
405
+ * Destroy session - clears token and localStorage
406
+ * @override
407
+ */
408
+ destroySession = (): void => {
409
+ this._token = null
410
+ localStorage.removeItem(this._storageKey)
411
+ }
412
+
413
+ // ─────────────────────────────────────────────────────────────────
414
+ // Signal integration
415
+ // ─────────────────────────────────────────────────────────────────
416
+
417
+ /**
418
+ * Connect authAdapter to signals for reactive impersonation
419
+ *
420
+ * Call this during kernel boot to enable signal-driven impersonation.
421
+ * The adapter will listen to auth:impersonate and auth:impersonate:stop
422
+ * signals and update its internal state automatically.
423
+ *
424
+ * @param signals - Signals instance (from orchestrator.signals)
425
+ * @returns Cleanup function to unsubscribe
426
+ */
427
+ connectSignals(signals: SignalBus | null): () => void {
428
+ if (!signals) return () => {}
429
+
430
+ const cleanups: Array<() => void> = []
431
+
432
+ // Listen for impersonate signal
433
+ cleanups.push(signals.on('auth:impersonate', (event) => {
434
+ const data = event.data as { target?: TUser } | undefined
435
+ const targetUser = data?.target
436
+ if (targetUser && !this._originalUser) {
437
+ this._originalUser = this._user
438
+ this._user = targetUser
439
+ this.persist()
440
+ }
441
+ }))
442
+
443
+ // Listen for stop impersonation signal
444
+ cleanups.push(signals.on('auth:impersonate:stop', () => {
445
+ if (this._originalUser) {
446
+ this._user = this._originalUser
447
+ this._originalUser = null
448
+ this.persist()
449
+ }
450
+ }))
451
+
452
+ // Return cleanup function
453
+ return () => {
454
+ cleanups.forEach(cleanup => cleanup?.())
455
+ }
456
+ }
457
+ }
458
+
459
+ export default SessionAuthAdapter