@quazardous/qdadm 2.2.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quazardous/qdadm",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Vue 3 framework for admin dashboards with PrimeVue",
5
5
  "author": "quazardous",
6
6
  "license": "MIT",
@@ -90,7 +90,9 @@ export function applyCacheMethods(EntityManagerClass: { prototype: any }): void
90
90
  Object.defineProperty(proto, 'overflow', {
91
91
  get(this: Self): boolean {
92
92
  const cache = this._cache
93
- return cache.valid && cache.total > cache.items.length
93
+ // overflowed: total exceeded effectiveThreshold, cache was never
94
+ // filled (#1204) — the cache is unusable either way.
95
+ return cache.overflowed === true || (cache.valid && cache.total > cache.items.length)
94
96
  },
95
97
  configurable: true,
96
98
  })
@@ -398,6 +400,7 @@ export function applyCacheMethods(EntityManagerClass: { prototype: any }): void
398
400
  cache.valid = false
399
401
  cache.items = []
400
402
  cache.total = 0
403
+ cache.overflowed = false
401
404
  cache.loadedAt = null
402
405
  this._cacheLoading = null
403
406
  // Also clear detail cache
@@ -182,6 +182,7 @@ export function applyCrudMethods(EntityManagerClass: { prototype: any }): void {
182
182
  cache.items = items
183
183
  cache.total = total
184
184
  cache.valid = true
185
+ cache.overflowed = false
185
186
  cache.loadedAt = Date.now()
186
187
  // Resolve parent fields for search (book.title, user.username, etc.)
187
188
  await this._resolveSearchFields(items)
@@ -191,6 +192,11 @@ export function applyCrudMethods(EntityManagerClass: { prototype: any }): void {
191
192
  // Fire-and-forget: load full cache in background
192
193
  this._loadCacheInBackground()
193
194
  }
195
+ } else if (canUseCache && this.isCacheEnabled && total > this.effectiveThreshold) {
196
+ // Entity outgrew the threshold: the cache can never hold it. Mark it
197
+ // so query() stops filtering an empty cache and goes to the API
198
+ // instead of returning an empty list (#1204).
199
+ cache.overflowed = true
194
200
  }
195
201
 
196
202
  return { items, total, fromCache: false }
@@ -31,7 +31,7 @@ export function applyQueryMethods(EntityManagerClass: { prototype: any }): void
31
31
  // Ensure cache is filled (via list) — skip for dynamic endpoints
32
32
  // since list() disables caching when resolveStorage provides an endpoint
33
33
  const cache = this._cache
34
- if (!isDynamicEndpoint && !cache.valid && this.isCacheEnabled) {
34
+ if (!isDynamicEndpoint && !cache.valid && !cache.overflowed && this.isCacheEnabled) {
35
35
  await this.list(
36
36
  { page_size: this.effectiveThreshold },
37
37
  routingContext ?? undefined
@@ -40,8 +40,11 @@ export function applyQueryMethods(EntityManagerClass: { prototype: any }): void
40
40
 
41
41
  let result: ListResult<any>
42
42
 
43
- // Dynamic endpoint, overflow, or cache disabled → use API directly
44
- if (isDynamicEndpoint || this.overflow || !this.isCacheEnabled) {
43
+ // Dynamic endpoint, overflow, cache disabled, or cache still not
44
+ // valid (fill failed / threshold exceeded) → use API directly. The
45
+ // cache is an optional layer: it must never turn into an empty
46
+ // result when it can't hold the data (#1204).
47
+ if (isDynamicEndpoint || this.overflow || !this.isCacheEnabled || !cache.valid) {
45
48
  result = await this.list(params, routingContext ?? undefined)
46
49
  } else {
47
50
  // Full cache available - filter locally
@@ -114,6 +114,7 @@ export class EntityManager<T extends EntityRecord = EntityRecord> {
114
114
  total: 0,
115
115
  loadedAt: null,
116
116
  valid: false,
117
+ overflowed: false,
117
118
  }
118
119
  protected _cacheLoading: Promise<boolean> | null = null
119
120
 
@@ -26,6 +26,8 @@ export interface CacheState<T> {
26
26
  total: number
27
27
  loadedAt: number | null
28
28
  valid: boolean
29
+ /** Entity total exceeded effectiveThreshold — cache can't hold it (#1204). */
30
+ overflowed?: boolean
29
31
  }
30
32
 
31
33
  /**
@@ -1,46 +0,0 @@
1
- /**
2
- * TestManager - Auto-generated EntityManager
3
- *
4
- * Generated by qdadm/gen/generateManagers
5
- * DO NOT EDIT MANUALLY - Changes will be overwritten
6
- *
7
- * Entity: test
8
- * Endpoint: /test
9
- */
10
-
11
- import { EntityManager } from '@quazardous/qdadm'
12
- import type { EntityRecord } from '@quazardous/qdadm'
13
- import { ApiStorage } from '@quazardous/qdadm'
14
-
15
- export interface TestEntity extends EntityRecord {
16
- }
17
-
18
- /**
19
- * Schema definition for test
20
- */
21
- export const testSchema = {
22
- name: "test",
23
- endpoint: "/test",
24
- fields: {}
25
- } as const
26
-
27
- /**
28
- * Storage options for test
29
- */
30
- const storageOptions = {
31
- endpoint: "/test"
32
- }
33
-
34
- /**
35
- * TestManager instance
36
- *
37
- * Provides CRUD operations for test entity.
38
- */
39
- export const testManager = new EntityManager<TestEntity>({
40
- ...{
41
- name: "test",
42
- idField: "id",
43
- fields: {}
44
- },
45
- storage: new ApiStorage<TestEntity>(storageOptions)
46
- })
@@ -212,7 +212,11 @@ export function applyRoutingMethods(KernelClass: { prototype: any }): void {
212
212
  }
213
213
 
214
214
  const entity = to.meta?.entity as string | undefined
215
- if (entity && this.orchestrator) {
215
+ // Entity not registered → nothing to check yet (e.g. child route
216
+ // declared before its manager loads) — allow. This is the ONLY
217
+ // allowed pass-through; a failing access check must fail CLOSED,
218
+ // not silently allow (#1190).
219
+ if (entity && this.orchestrator && this.orchestrator.isRegistered(entity)) {
216
220
  try {
217
221
  const manager = this.orchestrator.get(entity)
218
222
  if (manager && !manager.canRead()) {
@@ -232,8 +236,13 @@ export function applyRoutingMethods(KernelClass: { prototype: any }): void {
232
236
  next({ path: '/' })
233
237
  return
234
238
  }
235
- } catch {
236
- // Entity not registered - allow navigation
239
+ } catch (err) {
240
+ console.error(
241
+ `[qdadm] Access check failed for ${to.path} (entity: ${entity}) — denying navigation:`,
242
+ err
243
+ )
244
+ next({ path: '/' })
245
+ return
237
246
  }
238
247
  }
239
248