qdadm 0.31.0 → 0.32.0

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.
@@ -65,13 +65,26 @@ export class MockApiStorage extends IStorage {
65
65
  return MockApiStorage.capabilities.supportsCaching
66
66
  }
67
67
 
68
+ /**
69
+ * Instance capabilities getter.
70
+ * Merges static capabilities with instance-specific ones like requiresAuth.
71
+ * @returns {object}
72
+ */
73
+ get capabilities() {
74
+ return {
75
+ ...MockApiStorage.capabilities,
76
+ requiresAuth: !!this._authCheck
77
+ }
78
+ }
79
+
68
80
  constructor(options = {}) {
69
81
  super()
70
82
  const {
71
83
  entityName,
72
84
  idField = 'id',
73
85
  generateId = () => Date.now().toString(36) + Math.random().toString(36).substr(2),
74
- initialData = null
86
+ initialData = null,
87
+ authCheck = null // Optional: () => boolean - throws 401 if returns false
75
88
  } = options
76
89
 
77
90
  if (!entityName) {
@@ -83,11 +96,24 @@ export class MockApiStorage extends IStorage {
83
96
  this.generateId = generateId
84
97
  this._storageKey = `mockapi_${entityName}_data`
85
98
  this._data = new Map()
99
+ this._authCheck = authCheck
86
100
 
87
101
  // Load from localStorage or seed with initialData
88
102
  this._loadFromStorage(initialData)
89
103
  }
90
104
 
105
+ /**
106
+ * Check authentication if authCheck is configured
107
+ * @throws {Error} 401 error if not authenticated
108
+ */
109
+ _checkAuth() {
110
+ if (this._authCheck && !this._authCheck()) {
111
+ const error = new Error(`Unauthorized: Authentication required to access ${this.entityName}`)
112
+ error.status = 401
113
+ throw error
114
+ }
115
+ }
116
+
91
117
  /**
92
118
  * Load data from localStorage, seed with initialData if empty
93
119
  * @param {Array|null} initialData - Data to seed if storage is empty
@@ -158,6 +184,7 @@ export class MockApiStorage extends IStorage {
158
184
  * @returns {Promise<{ items: Array, total: number }>}
159
185
  */
160
186
  async list(params = {}) {
187
+ this._checkAuth()
161
188
  const { page = 1, page_size = 20, sort_by, sort_order = 'asc', filters = {}, search } = params
162
189
 
163
190
  let items = this._getAll()
@@ -213,6 +240,7 @@ export class MockApiStorage extends IStorage {
213
240
  * @returns {Promise<object>}
214
241
  */
215
242
  async get(id) {
243
+ this._checkAuth()
216
244
  const item = this._data.get(String(id))
217
245
  if (!item) {
218
246
  const error = new Error(`Entity not found: ${id}`)
@@ -228,6 +256,7 @@ export class MockApiStorage extends IStorage {
228
256
  * @returns {Promise<Array<object>>}
229
257
  */
230
258
  async getMany(ids) {
259
+ this._checkAuth()
231
260
  if (!ids || ids.length === 0) return []
232
261
  const results = []
233
262
  for (const id of ids) {
@@ -282,6 +311,7 @@ export class MockApiStorage extends IStorage {
282
311
  * @returns {Promise<object>}
283
312
  */
284
313
  async create(data) {
314
+ this._checkAuth()
285
315
  const id = data[this.idField] || this.generateId()
286
316
  const newItem = {
287
317
  ...data,
@@ -300,6 +330,7 @@ export class MockApiStorage extends IStorage {
300
330
  * @returns {Promise<object>}
301
331
  */
302
332
  async update(id, data) {
333
+ this._checkAuth()
303
334
  if (!this._data.has(String(id))) {
304
335
  const error = new Error(`Entity not found: ${id}`)
305
336
  error.status = 404
@@ -322,6 +353,7 @@ export class MockApiStorage extends IStorage {
322
353
  * @returns {Promise<object>}
323
354
  */
324
355
  async patch(id, data) {
356
+ this._checkAuth()
325
357
  const existing = this._data.get(String(id))
326
358
  if (!existing) {
327
359
  const error = new Error(`Entity not found: ${id}`)
@@ -344,6 +376,7 @@ export class MockApiStorage extends IStorage {
344
376
  * @returns {Promise<void>}
345
377
  */
346
378
  async delete(id) {
379
+ this._checkAuth()
347
380
  if (!this._data.has(String(id))) {
348
381
  const error = new Error(`Entity not found: ${id}`)
349
382
  error.status = 404
@@ -54,6 +54,7 @@ import { createZoneRegistry } from '../zones/ZoneRegistry.js'
54
54
  import { registerStandardZones } from '../zones/zones.js'
55
55
  import { createHookRegistry } from '../hooks/HookRegistry.js'
56
56
  import { createSecurityChecker } from '../entity/auth/SecurityChecker.js'
57
+ import { authFactory, CompositeAuthAdapter } from '../entity/auth/index.js'
57
58
  import { createManagers } from '../entity/factory.js'
58
59
  import { defaultStorageResolver } from '../entity/storage/factory.js'
59
60
  import { createDeferredRegistry } from '../deferred/DeferredRegistry.js'
@@ -139,6 +140,33 @@ export class Kernel {
139
140
  this._pendingComponents = new Map()
140
141
  }
141
142
 
143
+ /**
144
+ * Resolve entityAuthAdapter through authFactory
145
+ *
146
+ * Enables multiple configuration styles:
147
+ * - Instance passthrough (backward compatible): authAdapter instance
148
+ * - String pattern: 'permissive', 'jwt' (if registered in authTypes)
149
+ * - Config object: { type: 'jwt', ... }
150
+ * - Composite: { default: adapter, mapping: { 'entity': adapter } }
151
+ *
152
+ * @private
153
+ */
154
+ _resolveEntityAuthAdapter() {
155
+ const { entityAuthAdapter, authTypes } = this.options
156
+
157
+ // No adapter configured → nothing to resolve
158
+ if (entityAuthAdapter == null) return
159
+
160
+ // Build factory context with CompositeAuthAdapter available
161
+ const context = {
162
+ authTypes: authTypes || {},
163
+ CompositeAuthAdapter
164
+ }
165
+
166
+ // Resolve through factory (handles all input types)
167
+ this.options.entityAuthAdapter = authFactory(entityAuthAdapter, context)
168
+ }
169
+
142
170
  /**
143
171
  * Create and configure the Vue app
144
172
  *
@@ -149,6 +177,8 @@ export class Kernel {
149
177
  * @returns {App} Vue app instance ready to mount
150
178
  */
151
179
  createApp() {
180
+ // 0. Resolve entityAuthAdapter (supports instance, string, config, composite)
181
+ this._resolveEntityAuthAdapter()
152
182
  // 1. Create services first (modules need them)
153
183
  this._createSignalBus()
154
184
  this._createHookRegistry()
@@ -192,6 +222,8 @@ export class Kernel {
192
222
  * @returns {Promise<App>} Vue app instance ready to mount
193
223
  */
194
224
  async createAppAsync() {
225
+ // 0. Resolve entityAuthAdapter (supports instance, string, config, composite)
226
+ this._resolveEntityAuthAdapter()
195
227
  // 1. Create services first (modules need them)
196
228
  this._createSignalBus()
197
229
  this._createHookRegistry()