@aphexcms/cms-core 2.0.3 → 2.0.5

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 (138) hide show
  1. package/dist/cache/adapters/in-memory-cache-adapter.d.ts +21 -0
  2. package/dist/cache/adapters/in-memory-cache-adapter.d.ts.map +1 -0
  3. package/dist/cache/adapters/in-memory-cache-adapter.js +50 -0
  4. package/dist/cache/document-cache.d.ts +18 -0
  5. package/dist/cache/document-cache.d.ts.map +1 -0
  6. package/dist/cache/document-cache.js +45 -0
  7. package/dist/cache/index.d.ts +4 -0
  8. package/dist/cache/index.d.ts.map +1 -0
  9. package/dist/cache/index.js +2 -0
  10. package/dist/cache/interfaces/cache.d.ts +18 -0
  11. package/dist/cache/interfaces/cache.d.ts.map +1 -0
  12. package/dist/cache/interfaces/cache.js +1 -0
  13. package/dist/client/index.d.ts +1 -0
  14. package/dist/client/index.d.ts.map +1 -1
  15. package/dist/client/index.js +1 -0
  16. package/dist/components/admin/SchemaField.svelte +14 -0
  17. package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -1
  18. package/dist/components/admin/fields/FileField.svelte +485 -0
  19. package/dist/components/admin/fields/FileField.svelte.d.ts +18 -0
  20. package/dist/components/admin/fields/FileField.svelte.d.ts.map +1 -0
  21. package/dist/components/admin/fields/ReferenceField.svelte +2 -2
  22. package/dist/components/fields/index.d.ts +1 -0
  23. package/dist/components/fields/index.d.ts.map +1 -1
  24. package/dist/components/fields/index.js +1 -0
  25. package/dist/graphql/schema.d.ts.map +1 -1
  26. package/dist/graphql/schema.js +15 -1
  27. package/dist/hooks.d.ts.map +1 -1
  28. package/dist/hooks.js +11 -0
  29. package/dist/lib/cache/adapters/in-memory-cache-adapter.d.ts +21 -0
  30. package/dist/lib/cache/adapters/in-memory-cache-adapter.d.ts.map +1 -0
  31. package/dist/lib/cache/adapters/in-memory-cache-adapter.js +51 -0
  32. package/dist/lib/cache/adapters/in-memory-cache-adapter.js.map +1 -0
  33. package/dist/lib/cache/document-cache.d.ts +18 -0
  34. package/dist/lib/cache/document-cache.d.ts.map +1 -0
  35. package/dist/lib/cache/document-cache.js +46 -0
  36. package/dist/lib/cache/document-cache.js.map +1 -0
  37. package/dist/lib/cache/index.d.ts +4 -0
  38. package/dist/lib/cache/index.d.ts.map +1 -0
  39. package/dist/lib/cache/index.js +3 -0
  40. package/dist/lib/cache/index.js.map +1 -0
  41. package/dist/lib/cache/interfaces/cache.d.ts +18 -0
  42. package/dist/lib/cache/interfaces/cache.d.ts.map +1 -0
  43. package/dist/lib/cache/interfaces/cache.js +2 -0
  44. package/dist/lib/cache/interfaces/cache.js.map +1 -0
  45. package/dist/lib/client/index.d.ts +1 -0
  46. package/dist/lib/client/index.d.ts.map +1 -1
  47. package/dist/lib/client/index.js +1 -0
  48. package/dist/lib/client/index.js.map +1 -1
  49. package/dist/lib/components/fields/index.d.ts +1 -0
  50. package/dist/lib/components/fields/index.d.ts.map +1 -1
  51. package/dist/lib/components/fields/index.js +1 -0
  52. package/dist/lib/components/fields/index.js.map +1 -1
  53. package/dist/lib/graphql/schema.d.ts.map +1 -1
  54. package/dist/lib/graphql/schema.js +15 -1
  55. package/dist/lib/graphql/schema.js.map +1 -1
  56. package/dist/lib/hooks.d.ts.map +1 -1
  57. package/dist/lib/hooks.js +11 -0
  58. package/dist/lib/hooks.js.map +1 -1
  59. package/dist/lib/local-api/collection-api.d.ts +5 -1
  60. package/dist/lib/local-api/collection-api.d.ts.map +1 -1
  61. package/dist/lib/local-api/collection-api.js +63 -10
  62. package/dist/lib/local-api/collection-api.js.map +1 -1
  63. package/dist/lib/local-api/index.d.ts +13 -0
  64. package/dist/lib/local-api/index.d.ts.map +1 -1
  65. package/dist/lib/local-api/index.js +26 -2
  66. package/dist/lib/local-api/index.js.map +1 -1
  67. package/dist/lib/routes/assets.d.ts.map +1 -1
  68. package/dist/lib/routes/assets.js +14 -0
  69. package/dist/lib/routes/assets.js.map +1 -1
  70. package/dist/lib/routes/documents-by-id.js +18 -18
  71. package/dist/lib/routes/documents-by-id.js.map +1 -1
  72. package/dist/lib/routes/documents-publish.js +12 -12
  73. package/dist/lib/routes/documents-publish.js.map +1 -1
  74. package/dist/lib/schema-utils/validator.d.ts.map +1 -1
  75. package/dist/lib/schema-utils/validator.js +1 -0
  76. package/dist/lib/schema-utils/validator.js.map +1 -1
  77. package/dist/lib/server/index.d.ts +1 -0
  78. package/dist/lib/server/index.d.ts.map +1 -1
  79. package/dist/lib/server/index.js +1 -0
  80. package/dist/lib/server/index.js.map +1 -1
  81. package/dist/lib/services/hierarchy-service.d.ts +26 -0
  82. package/dist/lib/services/hierarchy-service.d.ts.map +1 -0
  83. package/dist/lib/services/hierarchy-service.js +64 -0
  84. package/dist/lib/services/hierarchy-service.js.map +1 -0
  85. package/dist/lib/services/index.d.ts +1 -0
  86. package/dist/lib/services/index.d.ts.map +1 -1
  87. package/dist/lib/services/index.js +1 -0
  88. package/dist/lib/services/index.js.map +1 -1
  89. package/dist/lib/storage/adapters/local-storage-adapter.d.ts.map +1 -1
  90. package/dist/lib/storage/adapters/local-storage-adapter.js +0 -14
  91. package/dist/lib/storage/adapters/local-storage-adapter.js.map +1 -1
  92. package/dist/lib/storage/interfaces/storage.d.ts +0 -1
  93. package/dist/lib/storage/interfaces/storage.d.ts.map +1 -1
  94. package/dist/lib/types/asset.d.ts +9 -0
  95. package/dist/lib/types/asset.d.ts.map +1 -1
  96. package/dist/lib/types/config.d.ts +7 -0
  97. package/dist/lib/types/config.d.ts.map +1 -1
  98. package/dist/lib/types/schemas.d.ts +10 -2
  99. package/dist/lib/types/schemas.d.ts.map +1 -1
  100. package/dist/lib/utils/mime-detect.d.ts +22 -0
  101. package/dist/lib/utils/mime-detect.d.ts.map +1 -0
  102. package/dist/lib/utils/mime-detect.js +201 -0
  103. package/dist/lib/utils/mime-detect.js.map +1 -0
  104. package/dist/local-api/collection-api.d.ts +5 -1
  105. package/dist/local-api/collection-api.d.ts.map +1 -1
  106. package/dist/local-api/collection-api.js +63 -10
  107. package/dist/local-api/index.d.ts +13 -0
  108. package/dist/local-api/index.d.ts.map +1 -1
  109. package/dist/local-api/index.js +26 -2
  110. package/dist/routes/assets.d.ts.map +1 -1
  111. package/dist/routes/assets.js +14 -0
  112. package/dist/routes/documents-by-id.js +18 -18
  113. package/dist/routes/documents-publish.js +12 -12
  114. package/dist/schema-utils/validator.d.ts.map +1 -1
  115. package/dist/schema-utils/validator.js +1 -0
  116. package/dist/server/index.d.ts +1 -0
  117. package/dist/server/index.d.ts.map +1 -1
  118. package/dist/server/index.js +1 -0
  119. package/dist/services/hierarchy-service.d.ts +26 -0
  120. package/dist/services/hierarchy-service.d.ts.map +1 -0
  121. package/dist/services/hierarchy-service.js +63 -0
  122. package/dist/services/index.d.ts +1 -0
  123. package/dist/services/index.d.ts.map +1 -1
  124. package/dist/services/index.js +1 -0
  125. package/dist/storage/adapters/local-storage-adapter.d.ts.map +1 -1
  126. package/dist/storage/adapters/local-storage-adapter.js +0 -14
  127. package/dist/storage/interfaces/storage.d.ts +0 -1
  128. package/dist/storage/interfaces/storage.d.ts.map +1 -1
  129. package/dist/types/asset.d.ts +9 -0
  130. package/dist/types/asset.d.ts.map +1 -1
  131. package/dist/types/config.d.ts +7 -0
  132. package/dist/types/config.d.ts.map +1 -1
  133. package/dist/types/schemas.d.ts +10 -2
  134. package/dist/types/schemas.d.ts.map +1 -1
  135. package/dist/utils/mime-detect.d.ts +22 -0
  136. package/dist/utils/mime-detect.d.ts.map +1 -0
  137. package/dist/utils/mime-detect.js +200 -0
  138. package/package.json +1 -1
@@ -35,11 +35,15 @@ export class CollectionAPI {
35
35
  databaseAdapter;
36
36
  _schema;
37
37
  permissions;
38
- constructor(collectionName, databaseAdapter, _schema, permissions) {
38
+ documentCache;
39
+ hierarchyService;
40
+ constructor(collectionName, databaseAdapter, _schema, permissions, documentCache, hierarchyService) {
39
41
  this.collectionName = collectionName;
40
42
  this.databaseAdapter = databaseAdapter;
41
43
  this._schema = _schema;
42
44
  this.permissions = permissions;
45
+ this.documentCache = documentCache;
46
+ this.hierarchyService = hierarchyService;
43
47
  // Validate collection exists
44
48
  this.permissions.validateCollection(collectionName);
45
49
  }
@@ -70,15 +74,32 @@ export class CollectionAPI {
70
74
  async find(context, options = {}) {
71
75
  // Permission check (unless overrideAccess)
72
76
  await this.permissions.canRead(context, this.collectionName);
73
- // Call adapter's advanced find method
74
- const result = await this.databaseAdapter.findManyDocAdvanced(context.organizationId, this.collectionName, options);
75
- // Transform documents to extract data based on perspective
76
77
  const perspective = options.perspective || 'draft';
78
+ // Check cache for published queries
79
+ if (perspective === 'published' && this.documentCache) {
80
+ const cached = await this.documentCache.getQuery(context.organizationId, this.collectionName, options);
81
+ if (cached)
82
+ return cached;
83
+ }
84
+ // Resolve org IDs via hierarchy service (cached) and pass directly —
85
+ // this avoids the adapter opening a transaction just to set RLS context
86
+ const findOptions = { ...options };
87
+ if (this.hierarchyService && !findOptions.filterOrganizationIds) {
88
+ const orgIds = await this.hierarchyService.getOrgIdsWithChildren(context.organizationId);
89
+ findOptions.filterOrganizationIds = orgIds;
90
+ }
91
+ const result = await this.databaseAdapter.findManyDocAdvanced(context.organizationId, this.collectionName, findOptions);
92
+ // Transform documents to extract data based on perspective
77
93
  const transformedDocs = result.docs.map((doc) => transformDocument(doc, perspective));
78
- return {
94
+ const findResult = {
79
95
  ...result,
80
96
  docs: transformedDocs
81
97
  };
98
+ // Populate cache for published queries
99
+ if (perspective === 'published' && this.documentCache) {
100
+ await this.documentCache.setQuery(context.organizationId, this.collectionName, options, findResult);
101
+ }
102
+ return findResult;
82
103
  }
83
104
  /**
84
105
  * Find a single document by ID
@@ -95,13 +116,29 @@ export class CollectionAPI {
95
116
  async findByID(context, id, options) {
96
117
  // Permission check (unless overrideAccess)
97
118
  await this.permissions.canRead(context, this.collectionName);
98
- const result = await this.databaseAdapter.findByDocIdAdvanced(context.organizationId, id, options);
119
+ const perspective = options?.perspective || 'draft';
120
+ // Check cache for published lookups
121
+ if (perspective === 'published' && this.documentCache) {
122
+ const cached = await this.documentCache.getDocument(context.organizationId, id);
123
+ if (cached)
124
+ return cached;
125
+ }
126
+ // Resolve org IDs via hierarchy service (cached) — avoids RLS transaction
127
+ const findOptions = { ...options };
128
+ if (this.hierarchyService && !findOptions.filterOrganizationIds) {
129
+ const orgIds = await this.hierarchyService.getOrgIdsWithChildren(context.organizationId);
130
+ findOptions.filterOrganizationIds = orgIds;
131
+ }
132
+ const result = await this.databaseAdapter.findByDocIdAdvanced(context.organizationId, id, findOptions);
99
133
  if (!result) {
100
134
  return null;
101
135
  }
102
- // Transform document to extract data based on perspective
103
- const perspective = options?.perspective || 'draft';
104
- return transformDocument(result, perspective);
136
+ const transformed = transformDocument(result, perspective);
137
+ // Populate cache for published lookups
138
+ if (perspective === 'published' && this.documentCache) {
139
+ await this.documentCache.setDocument(context.organizationId, id, transformed);
140
+ }
141
+ return transformed;
105
142
  }
106
143
  /**
107
144
  * Count documents matching a where clause
@@ -251,7 +288,13 @@ export class CollectionAPI {
251
288
  async delete(context, id) {
252
289
  // Permission check (unless overrideAccess)
253
290
  await this.permissions.canDelete(context, this.collectionName);
254
- return this.databaseAdapter.deleteDocById(context.organizationId, id);
291
+ const result = await this.databaseAdapter.deleteDocById(context.organizationId, id);
292
+ // Invalidate cache for deleted document
293
+ if (result && this.documentCache) {
294
+ await this.documentCache.invalidateDocument(context.organizationId, id);
295
+ await this.documentCache.invalidateCollection(context.organizationId, this.collectionName);
296
+ }
297
+ return result;
255
298
  }
256
299
  /**
257
300
  * Publish a document
@@ -285,6 +328,11 @@ export class CollectionAPI {
285
328
  if (!publishedDocument) {
286
329
  return null;
287
330
  }
331
+ // Invalidate cache for this document and all collection queries
332
+ if (this.documentCache) {
333
+ await this.documentCache.invalidateDocument(context.organizationId, id);
334
+ await this.documentCache.invalidateCollection(context.organizationId, this.collectionName);
335
+ }
288
336
  return transformDocument(publishedDocument, 'published');
289
337
  }
290
338
  /**
@@ -305,6 +353,11 @@ export class CollectionAPI {
305
353
  if (!document) {
306
354
  return null;
307
355
  }
356
+ // Invalidate cache — document is no longer published
357
+ if (this.documentCache) {
358
+ await this.documentCache.invalidateDocument(context.organizationId, id);
359
+ await this.documentCache.invalidateCollection(context.organizationId, this.collectionName);
360
+ }
308
361
  return transformDocument(document, 'draft');
309
362
  }
310
363
  }
@@ -1,7 +1,9 @@
1
1
  import type { CMSConfig } from '../types/config.js';
2
2
  import type { DatabaseAdapter } from '../db/index.js';
3
+ import type { FindOptions } from '../types/filters.js';
3
4
  import type { SchemaType } from '../types/schemas.js';
4
5
  import { CollectionAPI } from './collection-api.js';
6
+ import type { LocalAPIContext } from './types.js';
5
7
  /**
6
8
  * Collections map - provides type-safe access to all collections
7
9
  * This interface is meant to be augmented by generated types
@@ -44,6 +46,8 @@ export declare class LocalAPI {
44
46
  collections: Collections;
45
47
  private userAdapter;
46
48
  private systemAdapter;
49
+ private documentCache;
50
+ private hierarchyService;
47
51
  private permissions;
48
52
  private schemas;
49
53
  constructor(config: CMSConfig, userAdapter: DatabaseAdapter, systemAdapter?: DatabaseAdapter);
@@ -68,6 +72,15 @@ export declare class LocalAPI {
68
72
  * Get schema for a collection
69
73
  */
70
74
  getCollectionSchema(name: string): SchemaType | undefined;
75
+ /**
76
+ * Find a document by ID without knowing its collection type.
77
+ * Resolves org hierarchy and passes filterOrganizationIds to avoid RLS transactions.
78
+ * Returns the raw document with its type, or null if not found.
79
+ */
80
+ findDocumentById(context: LocalAPIContext, id: string, options?: Partial<FindOptions<unknown>>): Promise<{
81
+ type: string;
82
+ document: unknown;
83
+ } | null>;
71
84
  }
72
85
  /**
73
86
  * Create and initialize the Local API
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIjD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAE3B,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;CAGjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,QAAQ;IAQnB,OAAO,CAAC,MAAM;IAPR,WAAW,EAAE,WAAW,CAAqB;IACpD,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,OAAO,CAA0B;gBAGhC,MAAM,EAAE,SAAS,EACzB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe;IAmBhC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgC7B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;OAEG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;CAGzD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe,GAC7B,QAAQ,CAGV;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAKtC;AAGD,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/local-api/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAEjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,WAAW;IAE3B,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;CAGjD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAAa,QAAQ;IAUnB,OAAO,CAAC,MAAM;IATR,WAAW,EAAE,WAAW,CAAqB;IACpD,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,aAAa,CAAyB;IAC9C,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,OAAO,CAA0B;gBAGhC,MAAM,EAAE,SAAS,EACzB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe;IAqBhC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgC7B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAOlB;;OAEG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;OAEG;IACH,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIzD;;;;OAIG;IACG,gBAAgB,CACrB,OAAO,EAAE,eAAe,EACxB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,GACrC,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CAetD;AAKD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC7B,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,eAAe,EAC5B,aAAa,CAAC,EAAE,eAAe,GAC7B,QAAQ,CAGV;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,IAAI,QAAQ,CAKtC;AAGD,OAAO,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACnE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,6 +1,8 @@
1
1
  // local-api/index.ts
2
2
  //
3
3
  // Main Local API singleton and factory function
4
+ import { DocumentCache } from '../cache/index.js';
5
+ import { HierarchyService } from '../services/hierarchy-service.js';
4
6
  import { CollectionAPI } from './collection-api.js';
5
7
  import { PermissionChecker } from './permissions.js';
6
8
  /**
@@ -38,12 +40,16 @@ export class LocalAPI {
38
40
  collections = {};
39
41
  userAdapter;
40
42
  systemAdapter;
43
+ documentCache;
44
+ hierarchyService;
41
45
  permissions;
42
46
  schemas;
43
47
  constructor(config, userAdapter, systemAdapter) {
44
48
  this.config = config;
45
49
  this.userAdapter = userAdapter;
46
50
  this.systemAdapter = systemAdapter || null;
51
+ this.documentCache = config.cache ? new DocumentCache(config.cache) : null;
52
+ this.hierarchyService = new HierarchyService(userAdapter, config.cache);
47
53
  // Build schema map for quick lookups
48
54
  this.schemas = new Map(config.schemaTypes
49
55
  .filter((schema) => schema.type === 'document')
@@ -60,7 +66,7 @@ export class LocalAPI {
60
66
  const documentSchemas = this.config.schemaTypes.filter((s) => s.type === 'document');
61
67
  for (const schema of documentSchemas) {
62
68
  // Create a proxy that selects the correct adapter based on context
63
- const collectionAPI = new Proxy(new CollectionAPI(schema.name, this.userAdapter, schema, this.permissions), {
69
+ const collectionAPI = new Proxy(new CollectionAPI(schema.name, this.userAdapter, schema, this.permissions, this.documentCache, this.hierarchyService), {
64
70
  get: (target, prop) => {
65
71
  const method = target[prop];
66
72
  if (typeof method === 'function') {
@@ -69,7 +75,7 @@ export class LocalAPI {
69
75
  const context = args[0];
70
76
  const adapter = this.getAdapter(context);
71
77
  // Create new CollectionAPI with the correct adapter
72
- const api = new CollectionAPI(schema.name, adapter, schema, this.permissions);
78
+ const api = new CollectionAPI(schema.name, adapter, schema, this.permissions, this.documentCache, this.hierarchyService);
73
79
  // Call the method on the new instance
74
80
  return api[prop].apply(api, args);
75
81
  };
@@ -108,6 +114,24 @@ export class LocalAPI {
108
114
  getCollectionSchema(name) {
109
115
  return this.schemas.get(name);
110
116
  }
117
+ /**
118
+ * Find a document by ID without knowing its collection type.
119
+ * Resolves org hierarchy and passes filterOrganizationIds to avoid RLS transactions.
120
+ * Returns the raw document with its type, or null if not found.
121
+ */
122
+ async findDocumentById(context, id, options) {
123
+ const adapter = this.getAdapter(context);
124
+ const findOptions = { ...options };
125
+ // Resolve org IDs via hierarchy service to avoid withOrgContext transaction
126
+ if (this.hierarchyService) {
127
+ const orgIds = await this.hierarchyService.getOrgIdsWithChildren(context.organizationId);
128
+ findOptions.filterOrganizationIds = orgIds;
129
+ }
130
+ const rawDoc = await adapter.findByDocIdAdvanced(context.organizationId, id, findOptions);
131
+ if (!rawDoc)
132
+ return null;
133
+ return { type: rawDoc.type, document: rawDoc };
134
+ }
111
135
  }
112
136
  // Global Local API instance
113
137
  let localAPIInstance = null;
@@ -1 +1 @@
1
- {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,eAAO,MAAM,IAAI,EAAE,cAuElB,CAAC;AAEF,eAAO,MAAM,GAAG,EAAE,cAiEjB,CAAC"}
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../src/lib/routes/assets.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAIpD,eAAO,MAAM,IAAI,EAAE,cAuFlB,CAAC;AAEF,eAAO,MAAM,GAAG,EAAE,cAiEjB,CAAC"}
@@ -1,6 +1,7 @@
1
1
  // Aphex CMS Asset API Handlers
2
2
  import { json } from '@sveltejs/kit';
3
3
  import { cmsLogger } from '../utils/logger.js';
4
+ import { validateFile } from '../utils/mime-detect.js';
4
5
  export const POST = async ({ request, locals }) => {
5
6
  try {
6
7
  const { assetService } = locals.aphexCMS;
@@ -16,6 +17,19 @@ export const POST = async ({ request, locals }) => {
16
17
  // Convert file to buffer
17
18
  const arrayBuffer = await file.arrayBuffer();
18
19
  const buffer = Buffer.from(arrayBuffer);
20
+ // Get allowed MIME types and max size from form data (set by FileField component)
21
+ const allowedMimeTypesRaw = formData.get('allowedMimeTypes');
22
+ const maxSizeRaw = formData.get('maxSize');
23
+ const allowedMimeTypes = allowedMimeTypesRaw ? JSON.parse(allowedMimeTypesRaw) : undefined;
24
+ const maxSize = maxSizeRaw ? parseInt(maxSizeRaw, 10) : undefined;
25
+ // Validate file content (magic bytes, blocked types, allowed types)
26
+ const validation = validateFile(buffer, file.name, file.type, {
27
+ allowedMimeTypes,
28
+ maxSize
29
+ });
30
+ if (!validation.valid) {
31
+ return json({ success: false, error: validation.error }, { status: 400 });
32
+ }
19
33
  // Get optional metadata from form data
20
34
  const title = formData.get('title') || undefined;
21
35
  const description = formData.get('description') || undefined;
@@ -7,7 +7,7 @@ import { cmsLogger } from '../utils/logger.js';
7
7
  // TODO ENABLE CHILDREN ORG ACCESS BY DEFAULT - BECAUSE IF A PARENT ORG IS TRYING TO ACCESS A CHILD ORG. It should already have access to said id.
8
8
  export const GET = async ({ params, url, locals }) => {
9
9
  try {
10
- const { localAPI, databaseAdapter } = locals.aphexCMS;
10
+ const { localAPI } = locals.aphexCMS;
11
11
  const context = authToContext(locals.auth);
12
12
  const { id } = params;
13
13
  if (!id) {
@@ -17,18 +17,18 @@ export const GET = async ({ params, url, locals }) => {
17
17
  const depthParam = url.searchParams.get('depth');
18
18
  const depth = depthParam ? Math.max(0, Math.min(parseInt(depthParam), 5)) : 0;
19
19
  const perspective = url.searchParams.get('perspective') || 'draft';
20
- // First, fetch document to get its type (need this for collection-specific API)
21
- const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
22
- if (!rawDoc) {
20
+ // First, fetch document to get its type (uses hierarchy-aware lookup, no RLS transaction)
21
+ const result = await localAPI.findDocumentById(context, id);
22
+ if (!result) {
23
23
  return json({ success: false, error: 'Document not found' }, { status: 404 });
24
24
  }
25
25
  // Get collection API (TypeScript-safe)
26
- const collection = localAPI.collections[rawDoc.type];
26
+ const collection = localAPI.collections[result.type];
27
27
  if (!collection) {
28
28
  return json({
29
29
  success: false,
30
30
  error: 'Invalid document type',
31
- message: `Collection '${rawDoc.type}' not found`
31
+ message: `Collection '${result.type}' not found`
32
32
  }, { status: 400 });
33
33
  }
34
34
  // Now use LocalAPI for permission-checked retrieval
@@ -64,7 +64,7 @@ export const GET = async ({ params, url, locals }) => {
64
64
  // TODO ENABLE CHILDREN ORG ACCESS BY DEFAULT - BECAUSE IF A PARENT ORG IS TRYING TO ACCESS A CHILD ORG. It should already have access to said id.
65
65
  export const PUT = async ({ params, request, locals }) => {
66
66
  try {
67
- const { localAPI, databaseAdapter } = locals.aphexCMS;
67
+ const { localAPI } = locals.aphexCMS;
68
68
  const context = authToContext(locals.auth);
69
69
  const { id } = params;
70
70
  const body = await request.json();
@@ -73,18 +73,18 @@ export const PUT = async ({ params, request, locals }) => {
73
73
  }
74
74
  const documentData = body.draftData || body.data;
75
75
  const shouldPublish = body.publish || false;
76
- // Fetch document to get its type
77
- const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
78
- if (!rawDoc) {
76
+ // Fetch document to get its type (hierarchy-aware, no RLS transaction)
77
+ const found = await localAPI.findDocumentById(context, id);
78
+ if (!found) {
79
79
  return json({ success: false, error: 'Document not found' }, { status: 404 });
80
80
  }
81
81
  // Get collection API (TypeScript-safe)
82
- const collection = localAPI.collections[rawDoc.type];
82
+ const collection = localAPI.collections[found.type];
83
83
  if (!collection) {
84
84
  return json({
85
85
  success: false,
86
86
  error: 'Invalid document type',
87
- message: `Collection '${rawDoc.type}' not found`
87
+ message: `Collection '${found.type}' not found`
88
88
  }, { status: 400 });
89
89
  }
90
90
  // Update via LocalAPI (permission checks and validation happen inside)
@@ -127,24 +127,24 @@ export const PUT = async ({ params, request, locals }) => {
127
127
  // DELETE /api/documents/[id] - Delete document
128
128
  export const DELETE = async ({ params, locals }) => {
129
129
  try {
130
- const { localAPI, databaseAdapter } = locals.aphexCMS;
130
+ const { localAPI } = locals.aphexCMS;
131
131
  const context = authToContext(locals.auth);
132
132
  const { id } = params;
133
133
  if (!id) {
134
134
  return json({ success: false, error: 'Document ID is required' }, { status: 400 });
135
135
  }
136
- // Fetch document to get its type
137
- const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
138
- if (!rawDoc) {
136
+ // Fetch document to get its type (hierarchy-aware, no RLS transaction)
137
+ const result = await localAPI.findDocumentById(context, id);
138
+ if (!result) {
139
139
  return json({ success: false, error: 'Document not found' }, { status: 404 });
140
140
  }
141
141
  // Get collection API (TypeScript-safe)
142
- const collection = localAPI.collections[rawDoc.type];
142
+ const collection = localAPI.collections[result.type];
143
143
  if (!collection) {
144
144
  return json({
145
145
  success: false,
146
146
  error: 'Invalid document type',
147
- message: `Collection '${rawDoc.type}' not found`
147
+ message: `Collection '${result.type}' not found`
148
148
  }, { status: 400 });
149
149
  }
150
150
  // Delete via LocalAPI (permission checks happen inside)
@@ -6,7 +6,7 @@ import { cmsLogger } from '../utils/logger.js';
6
6
  // POST /api/documents/[id]/publish - Publish document
7
7
  export const POST = async ({ params, locals }) => {
8
8
  try {
9
- const { localAPI, databaseAdapter } = locals.aphexCMS;
9
+ const { localAPI } = locals.aphexCMS;
10
10
  const context = authToContext(locals.auth);
11
11
  const { id } = params;
12
12
  if (!id) {
@@ -16,9 +16,9 @@ export const POST = async ({ params, locals }) => {
16
16
  message: 'Document ID is required'
17
17
  }, { status: 400 });
18
18
  }
19
- // Fetch document to get its type
20
- const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
21
- if (!rawDoc) {
19
+ // Fetch document to get its type (hierarchy-aware, no RLS transaction)
20
+ const found = await localAPI.findDocumentById(context, id);
21
+ if (!found) {
22
22
  return json({
23
23
  success: false,
24
24
  error: 'Document not found',
@@ -26,12 +26,12 @@ export const POST = async ({ params, locals }) => {
26
26
  }, { status: 404 });
27
27
  }
28
28
  // Get collection API (TypeScript-safe)
29
- const collection = localAPI.collections[rawDoc.type];
29
+ const collection = localAPI.collections[found.type];
30
30
  if (!collection) {
31
31
  return json({
32
32
  success: false,
33
33
  error: 'Invalid document type',
34
- message: `Collection '${rawDoc.type}' not found`
34
+ message: `Collection '${found.type}' not found`
35
35
  }, { status: 400 });
36
36
  }
37
37
  // Publish via LocalAPI (permission checks and validation happen inside)
@@ -76,7 +76,7 @@ export const POST = async ({ params, locals }) => {
76
76
  // DELETE /api/documents/[id]/publish - Unpublish document
77
77
  export const DELETE = async ({ params, locals }) => {
78
78
  try {
79
- const { localAPI, databaseAdapter } = locals.aphexCMS;
79
+ const { localAPI } = locals.aphexCMS;
80
80
  const context = authToContext(locals.auth);
81
81
  const { id } = params;
82
82
  if (!id) {
@@ -86,9 +86,9 @@ export const DELETE = async ({ params, locals }) => {
86
86
  message: 'Document ID is required'
87
87
  }, { status: 400 });
88
88
  }
89
- // Fetch document to get its type
90
- const rawDoc = await databaseAdapter.findByDocIdAdvanced(context.organizationId, id);
91
- if (!rawDoc) {
89
+ // Fetch document to get its type (hierarchy-aware, no RLS transaction)
90
+ const found = await localAPI.findDocumentById(context, id);
91
+ if (!found) {
92
92
  return json({
93
93
  success: false,
94
94
  error: 'Document not found',
@@ -96,12 +96,12 @@ export const DELETE = async ({ params, locals }) => {
96
96
  }, { status: 404 });
97
97
  }
98
98
  // Get collection API (TypeScript-safe)
99
- const collection = localAPI.collections[rawDoc.type];
99
+ const collection = localAPI.collections[found.type];
100
100
  if (!collection) {
101
101
  return json({
102
102
  success: false,
103
103
  error: 'Invalid document type',
104
- message: `Collection '${rawDoc.type}' not found`
104
+ message: `Collection '${found.type}' not found`
105
105
  }, { status: 400 });
106
106
  }
107
107
  // Unpublish via LocalAPI (permission checks happen inside)
@@ -1 +1 @@
1
- {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/lib/schema-utils/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAS,MAAM,kBAAkB,CAAC;AAmB1D;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAoMpE"}
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/lib/schema-utils/validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAS,MAAM,kBAAkB,CAAC;AAmB1D;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,IAAI,CAqMpE"}
@@ -35,6 +35,7 @@ export function validateSchemaReferences(schemas) {
35
35
  'slug',
36
36
  'url',
37
37
  'image',
38
+ 'file',
38
39
  'date',
39
40
  'datetime',
40
41
  'reference'
@@ -1,5 +1,6 @@
1
1
  export * from '../types/index.js';
2
2
  export * from '../auth/provider.js';
3
+ export * from '../cache/index.js';
3
4
  export * from '../email/index.js';
4
5
  export { AuthError, type AuthErrorCode } from '../auth/auth-errors.js';
5
6
  export { createCMSConfig } from '../config.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/server/index.ts"],"names":[],"mappings":"AAIA,cAAc,gBAAgB,CAAC;AAE/B,cAAc,kBAAkB,CAAC;AAEjC,cAAc,gBAAgB,CAAC;AAG/B,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAG5D,cAAc,wBAAwB,CAAC;AAGvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAG7C,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAIzD,cAAc,mBAAmB,CAAC;AAGlC,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,EACN,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACN,QAAQ,EACR,cAAc,EACd,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/server/index.ts"],"names":[],"mappings":"AAIA,cAAc,gBAAgB,CAAC;AAE/B,cAAc,kBAAkB,CAAC;AAEjC,cAAc,gBAAgB,CAAC;AAE/B,cAAc,gBAAgB,CAAC;AAG/B,OAAO,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAG5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAG5D,cAAc,wBAAwB,CAAC;AAGvC,cAAc,kBAAkB,CAAC;AACjC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAG7C,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAIzD,cAAc,mBAAmB,CAAC;AAGlC,cAAc,uBAAuB,CAAC;AAGtC,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,EACN,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACN,QAAQ,EACR,cAAc,EACd,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,MAAM,oBAAoB,CAAC"}
@@ -3,6 +3,7 @@
3
3
  // Export all core types from the new central location
4
4
  export * from '../types/index.js';
5
5
  export * from '../auth/provider.js';
6
+ export * from '../cache/index.js';
6
7
  export * from '../email/index.js';
7
8
  // Authentication errors
8
9
  export { AuthError } from '../auth/auth-errors.js';
@@ -0,0 +1,26 @@
1
+ import type { CacheAdapter } from '../cache/index.js';
2
+ import type { DatabaseAdapter } from '../db/index.js';
3
+ /**
4
+ * HierarchyService — caches organization parent→child lookups
5
+ * using the shared CacheAdapter.
6
+ *
7
+ * Lives in cms-core so every adapter (PostgreSQL, SQLite, MongoDB)
8
+ * benefits from the same caching without reimplementing it.
9
+ */
10
+ export declare class HierarchyService {
11
+ private db;
12
+ private cache;
13
+ private ttl;
14
+ private static DEFAULT_TTL;
15
+ private inflight;
16
+ constructor(db: DatabaseAdapter, cache?: CacheAdapter | null, ttl?: number);
17
+ getChildOrganizations(parentOrganizationId: string): Promise<string[]>;
18
+ /**
19
+ * Get the parent org ID plus all its child org IDs.
20
+ * Convenience for building filterOrganizationIds arrays.
21
+ */
22
+ getOrgIdsWithChildren(organizationId: string): Promise<string[]>;
23
+ invalidate(parentOrganizationId: string): Promise<void>;
24
+ flush(): Promise<void>;
25
+ }
26
+ //# sourceMappingURL=hierarchy-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hierarchy-service.d.ts","sourceRoot":"","sources":["../../src/lib/services/hierarchy-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;;;;;GAMG;AACH,qBAAa,gBAAgB;IAK3B,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,GAAG;IANZ,OAAO,CAAC,MAAM,CAAC,WAAW,CAAM;IAChC,OAAO,CAAC,QAAQ,CAAwC;gBAG/C,EAAE,EAAE,eAAe,EACnB,KAAK,GAAE,YAAY,GAAG,IAAW,EACjC,GAAG,GAAE,MAAqC;IAG7C,qBAAqB,CAAC,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA8B5E;;;OAGG;IACG,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAKhE,UAAU,CAAC,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMvD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAK5B"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * HierarchyService — caches organization parent→child lookups
3
+ * using the shared CacheAdapter.
4
+ *
5
+ * Lives in cms-core so every adapter (PostgreSQL, SQLite, MongoDB)
6
+ * benefits from the same caching without reimplementing it.
7
+ */
8
+ export class HierarchyService {
9
+ db;
10
+ cache;
11
+ ttl;
12
+ static DEFAULT_TTL = 60; // 60 seconds
13
+ inflight = new Map();
14
+ constructor(db, cache = null, ttl = HierarchyService.DEFAULT_TTL) {
15
+ this.db = db;
16
+ this.cache = cache;
17
+ this.ttl = ttl;
18
+ }
19
+ async getChildOrganizations(parentOrganizationId) {
20
+ if (!this.db.hierarchyEnabled) {
21
+ return [];
22
+ }
23
+ const key = `hierarchy:${parentOrganizationId}`;
24
+ // Check cache first
25
+ if (this.cache) {
26
+ const cached = await this.cache.get(key);
27
+ if (cached)
28
+ return cached;
29
+ }
30
+ // Deduplicate concurrent requests — if a fetch is already in flight
31
+ // for this org, wait for that instead of hitting the DB again
32
+ const existing = this.inflight.get(key);
33
+ if (existing)
34
+ return existing;
35
+ const promise = this.db.getChildOrganizations(parentOrganizationId).then(async (ids) => {
36
+ if (this.cache) {
37
+ await this.cache.set(key, ids, this.ttl);
38
+ }
39
+ this.inflight.delete(key);
40
+ return ids;
41
+ });
42
+ this.inflight.set(key, promise);
43
+ return promise;
44
+ }
45
+ /**
46
+ * Get the parent org ID plus all its child org IDs.
47
+ * Convenience for building filterOrganizationIds arrays.
48
+ */
49
+ async getOrgIdsWithChildren(organizationId) {
50
+ const childIds = await this.getChildOrganizations(organizationId);
51
+ return childIds.length > 0 ? [organizationId, ...childIds] : [organizationId];
52
+ }
53
+ async invalidate(parentOrganizationId) {
54
+ if (this.cache) {
55
+ await this.cache.delete(`hierarchy:${parentOrganizationId}`);
56
+ }
57
+ }
58
+ async flush() {
59
+ if (this.cache) {
60
+ await this.cache.invalidateByPrefix('hierarchy:');
61
+ }
62
+ }
63
+ }
@@ -1,3 +1,4 @@
1
1
  export { AssetService } from './asset-service.js';
2
2
  export type { AssetUploadData, AssetFilters } from './asset-service.js';
3
+ export { HierarchyService } from './hierarchy-service.js';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/services/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/services/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -2,3 +2,4 @@
2
2
  // Business logic layer
3
3
  // Re-export services
4
4
  export { AssetService } from './asset-service.js';
5
+ export { HierarchyService } from './hierarchy-service.js';
@@ -1 +1 @@
1
- {"version":3,"file":"local-storage-adapter.d.ts","sourceRoot":"","sources":["../../../src/lib/storage/adapters/local-storage-adapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,MAAM,uBAAuB,CAAC;AAc/B;;GAEG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IACzD,QAAQ,CAAC,IAAI,WAAW;IACxB,OAAO,CAAC,MAAM,CAA0B;gBAE5B,MAAM,EAAE,aAAa;IAUjC;;OAEG;YACW,sBAAsB;IAcpC;;OAEG;IACH,OAAO,CAAC,aAAa;IAWrB;;OAEG;YACW,gBAAgB;IAU9B;;OAEG;IACG,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IA2CvD;;;OAGG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAU5C;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS5C;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAM5B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAyB/E;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAYnC"}
1
+ {"version":3,"file":"local-storage-adapter.d.ts","sourceRoot":"","sources":["../../../src/lib/storage/adapters/local-storage-adapter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACX,cAAc,EACd,aAAa,EACb,cAAc,EACd,WAAW,EACX,MAAM,uBAAuB,CAAC;AAK/B;;GAEG;AACH,qBAAa,mBAAoB,YAAW,cAAc;IACzD,QAAQ,CAAC,IAAI,WAAW;IACxB,OAAO,CAAC,MAAM,CAA0B;gBAE5B,MAAM,EAAE,aAAa;IASjC;;OAEG;YACW,sBAAsB;IAcpC;;OAEG;IACH,OAAO,CAAC,aAAa;IAWrB;;OAEG;YACW,gBAAgB;IAU9B;;OAEG;IACG,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAoCvD;;;OAGG;IACG,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAK9C;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAU5C;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS5C;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAM5B;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAyB/E;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;CAYnC"}
@@ -3,15 +3,6 @@ import { writeFile, mkdir, unlink, stat, readdir } from 'fs/promises';
3
3
  import { join, dirname } from 'path';
4
4
  import { cmsLogger } from '../../utils/logger.js';
5
5
  const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
6
- const DEFAULT_ALLOWED_TYPES = [
7
- 'image/jpeg',
8
- 'image/png',
9
- 'image/webp',
10
- 'image/gif',
11
- 'image/avif',
12
- 'application/pdf',
13
- 'text/plain'
14
- ];
15
6
  /**
16
7
  * Pure local file system storage adapter - only handles files
17
8
  */
@@ -23,7 +14,6 @@ export class LocalStorageAdapter {
23
14
  basePath: config.basePath,
24
15
  baseUrl: config.baseUrl || '',
25
16
  maxFileSize: config.maxFileSize || DEFAULT_MAX_FILE_SIZE,
26
- allowedTypes: config.allowedTypes || DEFAULT_ALLOWED_TYPES,
27
17
  options: config.options || {}
28
18
  };
29
19
  }
@@ -71,10 +61,6 @@ export class LocalStorageAdapter {
71
61
  * Store a file and return storage info
72
62
  */
73
63
  async store(data) {
74
- // Validate file type
75
- if (!this.config.allowedTypes.includes(data.mimeType)) {
76
- throw new Error(`Invalid file type: ${data.mimeType}. Allowed types: ${this.config.allowedTypes.join(', ')}`);
77
- }
78
64
  // Validate file size
79
65
  if (data.size > this.config.maxFileSize) {
80
66
  throw new Error(`File too large: ${data.size} bytes. Maximum size: ${this.config.maxFileSize} bytes`);
@@ -13,7 +13,6 @@ export interface StorageConfig {
13
13
  basePath: string;
14
14
  baseUrl?: string;
15
15
  maxFileSize?: number;
16
- allowedTypes?: string[];
17
16
  options?: {
18
17
  [key: string]: any;
19
18
  };