@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,209 @@
1
+ /**
2
+ * Storage Factory/Resolver Pattern
3
+ *
4
+ * Enables declarative storage configuration with auto-resolution.
5
+ */
6
+
7
+ import { IStorage } from './IStorage'
8
+ import { ApiStorage } from './ApiStorage'
9
+ import { LocalStorage } from './LocalStorage'
10
+ import { MemoryStorage } from './MemoryStorage'
11
+ import { MockApiStorage } from './MockApiStorage'
12
+ import { SdkStorage } from './SdkStorage'
13
+ import type { EntityRecord } from '../../types'
14
+
15
+ /**
16
+ * Storage class type
17
+ */
18
+ type StorageClass = typeof IStorage
19
+
20
+ /**
21
+ * Storage type registry - maps type names to storage classes
22
+ */
23
+ export const storageTypes: Record<string, StorageClass> = {
24
+ api: ApiStorage as unknown as StorageClass,
25
+ local: LocalStorage as unknown as StorageClass,
26
+ memory: MemoryStorage as unknown as StorageClass,
27
+ mock: MockApiStorage as unknown as StorageClass,
28
+ sdk: SdkStorage as unknown as StorageClass,
29
+ }
30
+
31
+ /**
32
+ * Parsed storage pattern result
33
+ */
34
+ export interface ParsedStoragePattern {
35
+ type: string
36
+ endpoint?: string
37
+ key?: string
38
+ entityName?: string
39
+ [key: string]: unknown
40
+ }
41
+
42
+ /**
43
+ * Storage configuration object
44
+ */
45
+ export interface StorageConfig {
46
+ type?: string
47
+ endpoint?: string
48
+ key?: string
49
+ entityName?: string
50
+ initialData?: unknown[]
51
+ [key: string]: unknown
52
+ }
53
+
54
+ /**
55
+ * Storage resolver function type
56
+ */
57
+ export type StorageResolver = <T extends EntityRecord = EntityRecord>(
58
+ config: StorageConfig,
59
+ entityName: string
60
+ ) => IStorage<T>
61
+
62
+ /**
63
+ * Parse string pattern 'type:endpoint' into config object
64
+ */
65
+ export function parseStoragePattern(pattern: string): ParsedStoragePattern | null {
66
+ if (typeof pattern !== 'string') return null
67
+
68
+ // Pattern: 'type:value'
69
+ const match = pattern.match(/^(\w+):(.+)$/)
70
+ if (match && match[1] && match[2]) {
71
+ const type = match[1]
72
+ const value = match[2]
73
+ // For local/mock, value is key/name; for api/sdk, value is endpoint
74
+ if (type === 'local' || type === 'memory') {
75
+ return { type, key: value }
76
+ }
77
+ if (type === 'mock') {
78
+ return { type, entityName: value }
79
+ }
80
+ return { type, endpoint: value }
81
+ }
82
+
83
+ // Plain string starting with '/' → assume API endpoint
84
+ if (pattern.startsWith('/')) {
85
+ return { type: 'api', endpoint: pattern }
86
+ }
87
+
88
+ return null
89
+ }
90
+
91
+ /**
92
+ * Default storage resolver - creates storage instance from config
93
+ */
94
+ export function defaultStorageResolver<T extends EntityRecord = EntityRecord>(
95
+ config: StorageConfig,
96
+ entityName: string
97
+ ): IStorage<T> {
98
+ const { type, ...rest } = config
99
+
100
+ if (!type) {
101
+ throw new Error('Storage config must have a type property')
102
+ }
103
+
104
+ const StorageClass = storageTypes[type]
105
+ if (!StorageClass) {
106
+ throw new Error(
107
+ `Unknown storage type: "${type}". Available: ${Object.keys(storageTypes).join(', ')}`
108
+ )
109
+ }
110
+
111
+ // Add entityName to config if not present (useful for mock/memory)
112
+ const resolvedConfig = { ...rest }
113
+ if (!resolvedConfig.entityName && !resolvedConfig.key) {
114
+ if (type === 'local' || type === 'memory') {
115
+ resolvedConfig.key = entityName
116
+ } else if (type === 'mock') {
117
+ resolvedConfig.entityName = entityName
118
+ }
119
+ }
120
+
121
+ // @ts-expect-error - StorageClass constructor accepts options object
122
+ return new StorageClass(resolvedConfig) as IStorage<T>
123
+ }
124
+
125
+ /**
126
+ * Duck-typed storage interface for detection
127
+ */
128
+ interface DuckTypedStorage {
129
+ list: (...args: unknown[]) => Promise<unknown>
130
+ get: (...args: unknown[]) => Promise<unknown>
131
+ }
132
+
133
+ /**
134
+ * Storage factory - normalizes input and delegates to resolver
135
+ *
136
+ * Handles:
137
+ * - IStorage instance → return directly
138
+ * - String pattern 'type:endpoint' → parse and resolve
139
+ * - Plain string '/endpoint' → treat as API endpoint
140
+ * - Config object → normalize and resolve
141
+ */
142
+ export function storageFactory<T extends EntityRecord = EntityRecord>(
143
+ config: IStorage<T> | string | StorageConfig,
144
+ entityName: string,
145
+ resolver: StorageResolver = defaultStorageResolver
146
+ ): IStorage<T> {
147
+ // Already a Storage instance → return directly
148
+ if (config instanceof IStorage) {
149
+ return config
150
+ }
151
+
152
+ // Also check for duck-typed storage (has list method and isn't a class)
153
+ if (
154
+ config &&
155
+ typeof config === 'object' &&
156
+ typeof (config as unknown as DuckTypedStorage).list === 'function' &&
157
+ typeof (config as unknown as DuckTypedStorage).get === 'function'
158
+ ) {
159
+ return config as unknown as IStorage<T>
160
+ }
161
+
162
+ // String pattern → parse
163
+ if (typeof config === 'string') {
164
+ const parsed = parseStoragePattern(config)
165
+ if (!parsed) {
166
+ throw new Error(`Invalid storage pattern: "${config}". Use 'type:value' or '/api/endpoint'`)
167
+ }
168
+ return resolver(parsed, entityName)
169
+ }
170
+
171
+ // Config object → normalize
172
+ if (config && typeof config === 'object') {
173
+ let normalizedConfig = config as StorageConfig
174
+ // If no type, infer from properties
175
+ if (!normalizedConfig.type) {
176
+ if (normalizedConfig.endpoint) {
177
+ normalizedConfig = { type: 'api', ...normalizedConfig }
178
+ } else if (normalizedConfig.key) {
179
+ normalizedConfig = { type: 'local', ...normalizedConfig }
180
+ } else if (normalizedConfig.initialData) {
181
+ normalizedConfig = { type: 'mock', ...normalizedConfig }
182
+ } else {
183
+ throw new Error(
184
+ `Cannot infer storage type from config: ${JSON.stringify(config)}. Add 'type' property.`
185
+ )
186
+ }
187
+ }
188
+ return resolver(normalizedConfig, entityName)
189
+ }
190
+
191
+ throw new Error(
192
+ `Invalid storage config: ${typeof config}. Expected string, object, or Storage instance.`
193
+ )
194
+ }
195
+
196
+ /**
197
+ * Create a custom storage factory with overridden resolver
198
+ */
199
+ export function createStorageFactory(
200
+ customResolver: StorageResolver
201
+ ): <T extends EntityRecord = EntityRecord>(
202
+ config: IStorage<T> | string | StorageConfig,
203
+ entityName: string
204
+ ) => IStorage<T> {
205
+ return <T extends EntityRecord = EntityRecord>(
206
+ config: IStorage<T> | string | StorageConfig,
207
+ entityName: string
208
+ ) => storageFactory(config, entityName, customResolver)
209
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Storage Adapters
3
+ *
4
+ * Boilerplate implementations for common storage backends.
5
+ * EntityManagers can use these or implement their own storage.
6
+ */
7
+
8
+ import type { StorageCapabilities } from '../../types'
9
+
10
+ /**
11
+ * Default capabilities for storages that don't declare their own.
12
+ * All capabilities default to false for safe degradation.
13
+ */
14
+ export const DEFAULT_STORAGE_CAPABILITIES: StorageCapabilities = {
15
+ supportsTotal: false,
16
+ supportsFilters: false,
17
+ supportsPagination: false,
18
+ supportsCaching: false,
19
+ }
20
+
21
+ /**
22
+ * Read capabilities from a storage instance.
23
+ * Accesses static `capabilities` property via constructor with fallback to defaults.
24
+ *
25
+ * @example
26
+ * const caps = getStorageCapabilities(myStorage)
27
+ * if (caps.supportsTotal) {
28
+ * // Storage provides accurate total count
29
+ * }
30
+ */
31
+ export function getStorageCapabilities(
32
+ storage: { constructor?: { capabilities?: Partial<StorageCapabilities> } } | null | undefined
33
+ ): StorageCapabilities {
34
+ const declared = storage?.constructor?.capabilities || {}
35
+ return {
36
+ ...DEFAULT_STORAGE_CAPABILITIES,
37
+ ...declared,
38
+ }
39
+ }
40
+
41
+ // Base class
42
+ export { IStorage } from './IStorage'
43
+
44
+ // Factory/Resolver
45
+ export {
46
+ storageFactory,
47
+ defaultStorageResolver,
48
+ createStorageFactory,
49
+ parseStoragePattern,
50
+ storageTypes,
51
+ } from './factory'
52
+ export type { ParsedStoragePattern, StorageConfig, StorageResolver } from './factory'
53
+
54
+ // Storage adapters
55
+ export { ApiStorage, createApiStorage } from './ApiStorage'
56
+ export type { HttpClient, RoutingContext, ApiStorageOptions } from './ApiStorage'
57
+
58
+ export { LocalStorage, createLocalStorage } from './LocalStorage'
59
+ export type { LocalStorageOptions } from './LocalStorage'
60
+
61
+ export { MemoryStorage, createMemoryStorage, StorageError } from './MemoryStorage'
62
+ export type { MemoryStorageOptions } from './MemoryStorage'
63
+
64
+ export { MockApiStorage, createMockApiStorage } from './MockApiStorage'
65
+ export type { MockApiStorageOptions } from './MockApiStorage'
66
+
67
+ export { SdkStorage, createSdkStorage } from './SdkStorage'
68
+ export type {
69
+ SdkInstance,
70
+ SdkMethodConfig,
71
+ SdkMethodTransforms,
72
+ SdkResponseFormat,
73
+ SdkMethods,
74
+ SdkStorageOptions,
75
+ } from './SdkStorage'
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Field Type Mapper
3
+ *
4
+ * Maps JSON Schema types and formats to UnifiedFieldSchema types.
5
+ * Extracted from Skybot and Faketual to eliminate duplication.
6
+ *
7
+ * Mapping priority:
8
+ * 1. Enum detection (schema.enum present -> 'select')
9
+ * 2. Custom mappings (user-provided)
10
+ * 3. Format-specific mappings (e.g., string + format:email -> email)
11
+ * 4. Base type mappings (e.g., string -> text)
12
+ *
13
+ * @module gen/FieldMapper
14
+ */
15
+
16
+ import type { UnifiedFieldType } from './schema'
17
+
18
+ /**
19
+ * Base type mappings: JSON Schema type -> UnifiedFieldSchema type
20
+ */
21
+ export const BASE_TYPE_MAPPINGS: Readonly<Record<string, UnifiedFieldType>> = Object.freeze({
22
+ string: 'text',
23
+ integer: 'number',
24
+ number: 'number',
25
+ boolean: 'boolean',
26
+ array: 'array',
27
+ object: 'object',
28
+ })
29
+
30
+ /**
31
+ * Format-specific mappings: JSON Schema format -> UnifiedFieldSchema type
32
+ *
33
+ * These take precedence over base type mappings when a format is specified.
34
+ */
35
+ export const FORMAT_MAPPINGS: Readonly<Record<string, UnifiedFieldType>> = Object.freeze({
36
+ email: 'email',
37
+ 'date-time': 'datetime',
38
+ date: 'date',
39
+ uri: 'url',
40
+ url: 'url',
41
+ uuid: 'uuid',
42
+ password: 'text',
43
+ })
44
+
45
+ /**
46
+ * Custom mappings override structure
47
+ */
48
+ export interface CustomMappings {
49
+ /** Custom type mappings { jsonSchemaType: unifiedType } */
50
+ types?: Record<string, string>
51
+ /** Custom format mappings { jsonSchemaFormat: unifiedType } */
52
+ formats?: Record<string, string>
53
+ }
54
+
55
+ /**
56
+ * Minimal JSON Schema property definition for type detection
57
+ */
58
+ export interface SchemaProperty {
59
+ /** JSON Schema type (string, integer, number, boolean, array, object) */
60
+ type?: string
61
+ /** JSON Schema format (email, date-time, uuid, etc.) */
62
+ format?: string
63
+ /** Enumeration values */
64
+ enum?: unknown[]
65
+ }
66
+
67
+ /**
68
+ * Get the UnifiedFieldSchema type for a JSON Schema property
69
+ *
70
+ * @param schema - JSON Schema property definition
71
+ * @param customMappings - Optional custom type/format mappings
72
+ * @returns Unified field type
73
+ *
74
+ * @example
75
+ * // Basic type mapping
76
+ * getDefaultType({ type: 'string' }) // 'text'
77
+ * getDefaultType({ type: 'integer' }) // 'number'
78
+ *
79
+ * @example
80
+ * // Format takes precedence
81
+ * getDefaultType({ type: 'string', format: 'email' }) // 'email'
82
+ * getDefaultType({ type: 'string', format: 'uuid' }) // 'uuid'
83
+ *
84
+ * @example
85
+ * // Enum detection
86
+ * getDefaultType({ type: 'string', enum: ['a', 'b'] }) // 'select'
87
+ *
88
+ * @example
89
+ * // Custom mappings
90
+ * getDefaultType({ type: 'string', format: 'phone' }, {
91
+ * formats: { phone: 'text' }
92
+ * }) // 'text'
93
+ */
94
+ export function getDefaultType(
95
+ schema: SchemaProperty | null | undefined,
96
+ customMappings: CustomMappings = {}
97
+ ): UnifiedFieldType | 'select' {
98
+ const { type, format } = schema || {}
99
+
100
+ // Check for enum pattern first (returns 'select')
101
+ if (Array.isArray(schema?.enum) && schema.enum.length > 0) {
102
+ return 'select'
103
+ }
104
+
105
+ // Check custom format mapping
106
+ if (format && customMappings.formats?.[format]) {
107
+ return customMappings.formats[format] as UnifiedFieldType
108
+ }
109
+
110
+ // Check default format mapping
111
+ if (format && FORMAT_MAPPINGS[format]) {
112
+ return FORMAT_MAPPINGS[format]
113
+ }
114
+
115
+ // Check custom type mapping
116
+ if (type && customMappings.types?.[type]) {
117
+ return customMappings.types[type] as UnifiedFieldType
118
+ }
119
+
120
+ // Fall back to base type mapping, default to 'text'
121
+ return (type && BASE_TYPE_MAPPINGS[type]) || 'text'
122
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Storage Profile Factory Pattern
3
+ *
4
+ * A storage profile factory is a function that creates storage instances for a given endpoint.
5
+ * This pattern enables DRY backend configuration by centralizing storage creation logic
6
+ * (API client, base URL, headers, error handling) in one place.
7
+ *
8
+ * Instead of configuring each entity's storage individually, you define a profile factory
9
+ * once and pass it to createManagers(), which calls it with each entity's endpoint.
10
+ *
11
+ * @module gen/StorageProfileFactory
12
+ */
13
+
14
+ import type { IStorage } from '../entity/storage/IStorage'
15
+ import type { EntityRecord } from '../types'
16
+
17
+ /**
18
+ * Storage Profile Factory Options
19
+ *
20
+ * Per-entity options passed when creating storage from a profile factory.
21
+ * Allows overriding profile defaults for specific entities.
22
+ */
23
+ export interface StorageProfileOptions {
24
+ /** Entity name (e.g., 'users', 'posts') */
25
+ entity?: string
26
+ /** Override the profile's HTTP client for this entity */
27
+ client?: unknown
28
+ /** Additional headers for this entity */
29
+ headers?: Record<string, string>
30
+ /** Force read-only mode (no create/update/delete) */
31
+ readOnly?: boolean
32
+ /** Custom per-entity options */
33
+ extensions?: Record<string, unknown>
34
+ }
35
+
36
+ /**
37
+ * Storage Profile Factory
38
+ *
39
+ * A function that creates storage instances for a given endpoint.
40
+ * The factory encapsulates common storage configuration (API client, auth, base URL)
41
+ * and creates properly-configured storage adapters for each entity.
42
+ *
43
+ * @example Basic factory using ApiStorage
44
+ * ```ts
45
+ * import axios from 'axios'
46
+ * import { ApiStorage } from '@quazardous/qdadm'
47
+ *
48
+ * // Define profile factory with shared configuration
49
+ * const apiProfile: StorageProfileFactory = (endpoint, options = {}) => {
50
+ * return new ApiStorage({
51
+ * endpoint,
52
+ * client: axios.create({
53
+ * baseURL: 'https://api.example.com',
54
+ * headers: { 'Authorization': `Bearer ${getToken()}` }
55
+ * }),
56
+ * ...options
57
+ * })
58
+ * }
59
+ *
60
+ * // Use with createManagers (T00317)
61
+ * const managers = createManagers(schemas, {
62
+ * storageProfile: apiProfile
63
+ * })
64
+ * ```
65
+ *
66
+ * @example Factory with dynamic client resolution
67
+ * ```ts
68
+ * // For Vue/inject pattern - client resolved at call time
69
+ * const lazyApiProfile: StorageProfileFactory = (endpoint, options = {}) => {
70
+ * return new ApiStorage({
71
+ * endpoint,
72
+ * getClient: () => inject('apiClient'),
73
+ * ...options
74
+ * })
75
+ * }
76
+ * ```
77
+ *
78
+ * @example Factory for local development
79
+ * ```ts
80
+ * import { MemoryStorage } from '@quazardous/qdadm'
81
+ *
82
+ * const memoryProfile: StorageProfileFactory = (endpoint, options = {}) => {
83
+ * const entity = options.entity || endpoint.replace(/^\//, '')
84
+ * return new MemoryStorage({
85
+ * initialData: mockData[entity] || []
86
+ * })
87
+ * }
88
+ * ```
89
+ */
90
+ export type StorageProfileFactory = (
91
+ endpoint: string,
92
+ options?: StorageProfileOptions
93
+ ) => IStorage<EntityRecord>
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Base Connector Interface
3
+ *
4
+ * Abstract base class for schema connectors. Connectors parse various sources
5
+ * (OpenAPI specs, manual definitions, etc.) into UnifiedEntitySchema format.
6
+ *
7
+ * Subclasses must implement the `parse(source)` method with source-specific logic.
8
+ *
9
+ * @module gen/connectors/BaseConnector
10
+ */
11
+
12
+ import type { UnifiedEntitySchema } from '../schema'
13
+
14
+ /**
15
+ * Connector options for customizing parsing behavior
16
+ */
17
+ export interface ConnectorOptions {
18
+ /** Connector instance name for debugging */
19
+ name?: string
20
+ /** Throw on validation errors instead of warning (default: false) */
21
+ strict?: boolean
22
+ /** Custom extension data passed to output schemas */
23
+ extensions?: Record<string, unknown>
24
+ }
25
+
26
+ /**
27
+ * Warning encountered during parsing
28
+ */
29
+ export interface ParseWarning {
30
+ /** Location in source where issue occurred */
31
+ path: string
32
+ /** Description of the issue */
33
+ message: string
34
+ /** Warning code for programmatic handling */
35
+ code?: string
36
+ }
37
+
38
+ /**
39
+ * Parse result with parsed schemas and any encountered issues
40
+ */
41
+ export interface ParseResult {
42
+ /** Parsed entity schemas */
43
+ schemas: UnifiedEntitySchema[]
44
+ /** Non-fatal issues encountered during parsing */
45
+ warnings: ParseWarning[]
46
+ }
47
+
48
+ /**
49
+ * Base connector class for parsing schema sources into UnifiedEntitySchema format.
50
+ *
51
+ * @abstract
52
+ * @example
53
+ * // Extending BaseConnector
54
+ * class ManualConnector extends BaseConnector {
55
+ * parse(source) {
56
+ * // Parse inline definitions
57
+ * return source.entities.map(e => this.transformEntity(e))
58
+ * }
59
+ * }
60
+ *
61
+ * @example
62
+ * // Using a connector
63
+ * const connector = new ManualConnector({ strict: true })
64
+ * const schemas = connector.parse(manualDefinitions)
65
+ */
66
+ export abstract class BaseConnector {
67
+ /** Connector instance name */
68
+ name: string
69
+
70
+ /** Whether to throw on validation errors */
71
+ strict: boolean
72
+
73
+ /** Custom extension data */
74
+ extensions: Record<string, unknown>
75
+
76
+ /**
77
+ * Create a new connector instance
78
+ *
79
+ * @param options - Connector configuration options
80
+ */
81
+ constructor(options: ConnectorOptions = {}) {
82
+ this.name = options.name || this.constructor.name
83
+ this.strict = options.strict ?? false
84
+ this.extensions = options.extensions || {}
85
+ }
86
+
87
+ /**
88
+ * Parse a schema source into UnifiedEntitySchema format.
89
+ *
90
+ * This method must be implemented by subclasses to handle source-specific
91
+ * parsing logic. The source parameter format depends on the connector type:
92
+ * - ManualConnector: Object with entity/field definitions
93
+ * - OpenAPIConnector: OpenAPI 3.x specification object
94
+ *
95
+ * @abstract
96
+ * @param source - Source data to parse (format depends on connector type)
97
+ * @returns Parsed entity schemas
98
+ * @throws If parsing fails (in strict mode) or source is invalid
99
+ *
100
+ * @example
101
+ * // ManualConnector source format
102
+ * connector.parse({
103
+ * entities: [
104
+ * { name: 'users', endpoint: '/api/users', fields: { ... } }
105
+ * ]
106
+ * })
107
+ *
108
+ * @example
109
+ * // OpenAPIConnector source format
110
+ * connector.parse({
111
+ * openapi: '3.0.0',
112
+ * paths: { '/api/users': { ... } },
113
+ * components: { schemas: { ... } }
114
+ * })
115
+ */
116
+ abstract parse(source: unknown): UnifiedEntitySchema[]
117
+
118
+ /**
119
+ * Parse with detailed result including warnings
120
+ *
121
+ * Alternative to `parse()` that returns warnings along with schemas.
122
+ * Useful when you want to handle non-fatal issues without strict mode.
123
+ *
124
+ * Default implementation wraps `parse()` with empty warnings array.
125
+ * Subclasses can override for more detailed reporting.
126
+ *
127
+ * @param source - Source data to parse
128
+ * @returns Schemas and any warnings encountered
129
+ */
130
+ parseWithWarnings(source: unknown): ParseResult {
131
+ return {
132
+ schemas: this.parse(source),
133
+ warnings: [],
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Validate connector is properly configured before parsing
139
+ *
140
+ * Override in subclasses to add custom validation.
141
+ *
142
+ * @returns True if connector is valid
143
+ * @throws If connector configuration is invalid
144
+ */
145
+ validate(): boolean {
146
+ return true
147
+ }
148
+ }
149
+
150
+ export default BaseConnector