@aphexcms/cms-core 0.1.1 → 0.1.4

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 (336) hide show
  1. package/dist/api/assets.d.ts +48 -0
  2. package/dist/api/assets.d.ts.map +1 -0
  3. package/dist/api/assets.js +52 -0
  4. package/dist/api/client.d.ts +37 -0
  5. package/dist/api/client.d.ts.map +1 -0
  6. package/dist/api/client.js +125 -0
  7. package/dist/api/documents.d.ts +56 -0
  8. package/dist/api/documents.d.ts.map +1 -0
  9. package/dist/api/documents.js +77 -0
  10. package/{src/api/index.ts → dist/api/index.d.ts} +1 -1
  11. package/dist/api/index.d.ts.map +1 -0
  12. package/dist/api/index.js +5 -0
  13. package/dist/api/organizations.d.ts +101 -0
  14. package/dist/api/organizations.d.ts.map +1 -0
  15. package/dist/api/organizations.js +92 -0
  16. package/dist/api/types.d.ts +23 -0
  17. package/dist/api/types.d.ts.map +1 -0
  18. package/dist/api/types.js +1 -0
  19. package/dist/auth/auth-errors.d.ts +7 -0
  20. package/dist/auth/auth-errors.d.ts.map +1 -0
  21. package/dist/auth/auth-errors.js +13 -0
  22. package/dist/auth/auth-hooks.d.ts +6 -0
  23. package/dist/auth/auth-hooks.d.ts.map +1 -0
  24. package/dist/auth/auth-hooks.js +108 -0
  25. package/dist/auth/provider.d.ts +17 -0
  26. package/dist/auth/provider.d.ts.map +1 -0
  27. package/dist/auth/provider.js +1 -0
  28. package/dist/client/index.d.ts +24 -0
  29. package/dist/client/index.d.ts.map +1 -0
  30. package/{src/client/index.ts → dist/client/index.js} +0 -16
  31. package/{src → dist}/components/AdminApp.svelte +7 -8
  32. package/dist/components/AdminApp.svelte.d.ts +24 -0
  33. package/dist/components/AdminApp.svelte.d.ts.map +1 -0
  34. package/dist/components/admin/AdminLayout.svelte.d.ts +15 -0
  35. package/dist/components/admin/AdminLayout.svelte.d.ts.map +1 -0
  36. package/dist/components/admin/DocumentEditor.svelte.d.ts +18 -0
  37. package/dist/components/admin/DocumentEditor.svelte.d.ts.map +1 -0
  38. package/dist/components/admin/DocumentTypesList.svelte.d.ts +14 -0
  39. package/dist/components/admin/DocumentTypesList.svelte.d.ts.map +1 -0
  40. package/dist/components/admin/ObjectModal.svelte.d.ts +15 -0
  41. package/dist/components/admin/ObjectModal.svelte.d.ts.map +1 -0
  42. package/dist/components/admin/SchemaField.svelte.d.ts +19 -0
  43. package/dist/components/admin/SchemaField.svelte.d.ts.map +1 -0
  44. package/dist/components/admin/fields/ArrayField.svelte.d.ts +12 -0
  45. package/dist/components/admin/fields/ArrayField.svelte.d.ts.map +1 -0
  46. package/dist/components/admin/fields/BooleanField.svelte.d.ts +13 -0
  47. package/dist/components/admin/fields/BooleanField.svelte.d.ts.map +1 -0
  48. package/dist/components/admin/fields/ImageField.svelte.d.ts +15 -0
  49. package/dist/components/admin/fields/ImageField.svelte.d.ts.map +1 -0
  50. package/dist/components/admin/fields/NumberField.svelte.d.ts +14 -0
  51. package/dist/components/admin/fields/NumberField.svelte.d.ts.map +1 -0
  52. package/dist/components/admin/fields/ReferenceField.svelte.d.ts +12 -0
  53. package/dist/components/admin/fields/ReferenceField.svelte.d.ts.map +1 -0
  54. package/dist/components/admin/fields/SlugField.svelte.d.ts +15 -0
  55. package/dist/components/admin/fields/SlugField.svelte.d.ts.map +1 -0
  56. package/dist/components/admin/fields/StringField.svelte.d.ts +14 -0
  57. package/dist/components/admin/fields/StringField.svelte.d.ts.map +1 -0
  58. package/dist/components/admin/fields/TextareaField.svelte.d.ts +14 -0
  59. package/dist/components/admin/fields/TextareaField.svelte.d.ts.map +1 -0
  60. package/dist/components/fields/index.d.ts +9 -0
  61. package/dist/components/fields/index.d.ts.map +1 -0
  62. package/dist/components/index.d.ts +7 -0
  63. package/dist/components/index.d.ts.map +1 -0
  64. package/{src/components/index.ts → dist/components/index.js} +0 -4
  65. package/dist/components/layout/OrganizationSwitcher.svelte.d.ts +11 -0
  66. package/dist/components/layout/OrganizationSwitcher.svelte.d.ts.map +1 -0
  67. package/{src → dist}/components/layout/Sidebar.svelte +7 -7
  68. package/dist/components/layout/Sidebar.svelte.d.ts +14 -0
  69. package/dist/components/layout/Sidebar.svelte.d.ts.map +1 -0
  70. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts +11 -0
  71. package/dist/components/layout/sidebar/AppSidebar.svelte.d.ts.map +1 -0
  72. package/dist/components/layout/sidebar/NavMain.svelte.d.ts +19 -0
  73. package/dist/components/layout/sidebar/NavMain.svelte.d.ts.map +1 -0
  74. package/dist/components/layout/sidebar/NavSecondary.svelte.d.ts +9 -0
  75. package/dist/components/layout/sidebar/NavSecondary.svelte.d.ts.map +1 -0
  76. package/dist/components/layout/sidebar/NavUser.svelte.d.ts +9 -0
  77. package/dist/components/layout/sidebar/NavUser.svelte.d.ts.map +1 -0
  78. package/dist/config.d.ts +3 -0
  79. package/dist/config.d.ts.map +1 -0
  80. package/dist/config.js +15 -0
  81. package/dist/db/adapters/index.d.ts +1 -0
  82. package/dist/db/adapters/index.d.ts.map +1 -0
  83. package/{src/db/adapters/index.ts → dist/db/adapters/index.js} +1 -0
  84. package/dist/db/index.d.ts +2 -0
  85. package/dist/db/index.d.ts.map +1 -0
  86. package/{src/db/index.ts → dist/db/index.js} +0 -1
  87. package/dist/db/interfaces/asset.d.ts +51 -0
  88. package/dist/db/interfaces/asset.d.ts.map +1 -0
  89. package/dist/db/interfaces/asset.js +1 -0
  90. package/dist/db/interfaces/document.d.ts +36 -0
  91. package/dist/db/interfaces/document.d.ts.map +1 -0
  92. package/dist/db/interfaces/document.js +1 -0
  93. package/dist/db/interfaces/index.d.ts +73 -0
  94. package/dist/db/interfaces/index.d.ts.map +1 -0
  95. package/dist/db/interfaces/index.js +1 -0
  96. package/dist/db/interfaces/organization.d.ts +27 -0
  97. package/dist/db/interfaces/organization.d.ts.map +1 -0
  98. package/dist/db/interfaces/organization.js +1 -0
  99. package/dist/db/interfaces/schema.d.ts +21 -0
  100. package/dist/db/interfaces/schema.d.ts.map +1 -0
  101. package/dist/db/interfaces/schema.js +1 -0
  102. package/dist/db/interfaces/user.d.ts +15 -0
  103. package/dist/db/interfaces/user.d.ts.map +1 -0
  104. package/dist/db/interfaces/user.js +1 -0
  105. package/dist/db/utils/reference-resolver.d.ts +18 -0
  106. package/dist/db/utils/reference-resolver.d.ts.map +1 -0
  107. package/dist/db/utils/reference-resolver.js +80 -0
  108. package/dist/define.d.ts +3 -0
  109. package/dist/define.d.ts.map +1 -0
  110. package/dist/define.js +4 -0
  111. package/dist/email/index.d.ts +2 -0
  112. package/dist/email/index.d.ts.map +1 -0
  113. package/{src/email/index.ts → dist/email/index.js} +0 -1
  114. package/dist/email/interfaces/email.d.ts +42 -0
  115. package/dist/email/interfaces/email.d.ts.map +1 -0
  116. package/dist/email/interfaces/email.js +1 -0
  117. package/dist/engine.d.ts +26 -0
  118. package/dist/engine.d.ts.map +1 -0
  119. package/dist/engine.js +66 -0
  120. package/dist/field-validation/rule.d.ts +51 -0
  121. package/dist/field-validation/rule.d.ts.map +1 -0
  122. package/dist/field-validation/rule.js +221 -0
  123. package/dist/field-validation/utils.d.ts +21 -0
  124. package/dist/field-validation/utils.d.ts.map +1 -0
  125. package/dist/field-validation/utils.js +66 -0
  126. package/dist/hooks.d.ts +23 -0
  127. package/dist/hooks.d.ts.map +1 -0
  128. package/dist/hooks.js +96 -0
  129. package/dist/index.d.ts +2 -0
  130. package/dist/index.d.ts.map +1 -0
  131. package/{src/index.ts → dist/index.js} +0 -1
  132. package/dist/routes/assets-by-id.d.ts +5 -0
  133. package/dist/routes/assets-by-id.d.ts.map +1 -0
  134. package/dist/routes/assets-by-id.js +138 -0
  135. package/dist/routes/assets-cdn.d.ts +3 -0
  136. package/dist/routes/assets-cdn.d.ts.map +1 -0
  137. package/dist/routes/assets-cdn.js +155 -0
  138. package/dist/routes/assets.d.ts +4 -0
  139. package/dist/routes/assets.d.ts.map +1 -0
  140. package/dist/routes/assets.js +94 -0
  141. package/dist/routes/documents-by-id.d.ts +5 -0
  142. package/dist/routes/documents-by-id.d.ts.map +1 -0
  143. package/dist/routes/documents-by-id.js +142 -0
  144. package/dist/routes/documents-publish.d.ts +4 -0
  145. package/dist/routes/documents-publish.d.ts.map +1 -0
  146. package/dist/routes/documents-publish.js +151 -0
  147. package/dist/routes/documents.d.ts +4 -0
  148. package/dist/routes/documents.d.ts.map +1 -0
  149. package/dist/routes/documents.js +131 -0
  150. package/dist/routes/index.d.ts +6 -0
  151. package/dist/routes/index.d.ts.map +1 -0
  152. package/{src/routes/index.ts → dist/routes/index.js} +0 -3
  153. package/dist/routes/organizations-by-id.d.ts +5 -0
  154. package/dist/routes/organizations-by-id.d.ts.map +1 -0
  155. package/dist/routes/organizations-by-id.js +187 -0
  156. package/dist/routes/organizations-invitations.d.ts +4 -0
  157. package/dist/routes/organizations-invitations.d.ts.map +1 -0
  158. package/dist/routes/organizations-invitations.js +125 -0
  159. package/dist/routes/organizations-members.d.ts +5 -0
  160. package/dist/routes/organizations-members.d.ts.map +1 -0
  161. package/dist/routes/organizations-members.js +206 -0
  162. package/dist/routes/organizations-switch.d.ts +3 -0
  163. package/dist/routes/organizations-switch.d.ts.map +1 -0
  164. package/dist/routes/organizations-switch.js +53 -0
  165. package/dist/routes/organizations.d.ts +4 -0
  166. package/dist/routes/organizations.d.ts.map +1 -0
  167. package/dist/routes/organizations.js +108 -0
  168. package/dist/routes/schemas-by-type.d.ts +3 -0
  169. package/dist/routes/schemas-by-type.d.ts.map +1 -0
  170. package/dist/routes/schemas-by-type.js +25 -0
  171. package/dist/routes/schemas.d.ts +3 -0
  172. package/dist/routes/schemas.d.ts.map +1 -0
  173. package/dist/routes/schemas.js +11 -0
  174. package/dist/routes-exports.d.ts +14 -0
  175. package/dist/routes-exports.d.ts.map +1 -0
  176. package/{src/routes-exports.ts → dist/routes-exports.js} +5 -28
  177. package/dist/schema-context.svelte.d.ts +10 -0
  178. package/dist/schema-context.svelte.d.ts.map +1 -0
  179. package/dist/schema-context.svelte.js +18 -0
  180. package/dist/schema-utils/cleanup.d.ts +21 -0
  181. package/dist/schema-utils/cleanup.d.ts.map +1 -0
  182. package/dist/schema-utils/cleanup.js +80 -0
  183. package/dist/schema-utils/index.d.ts +4 -0
  184. package/dist/schema-utils/index.d.ts.map +1 -0
  185. package/dist/schema-utils/utils.d.ts +30 -0
  186. package/dist/schema-utils/utils.d.ts.map +1 -0
  187. package/dist/schema-utils/utils.js +37 -0
  188. package/dist/schema-utils/validator.d.ts +6 -0
  189. package/dist/schema-utils/validator.d.ts.map +1 -0
  190. package/dist/schema-utils/validator.js +45 -0
  191. package/dist/server/index.d.ts +16 -0
  192. package/dist/server/index.d.ts.map +1 -0
  193. package/{src/server/index.ts → dist/server/index.js} +2 -14
  194. package/dist/services/asset-service.d.ts +86 -0
  195. package/dist/services/asset-service.d.ts.map +1 -0
  196. package/dist/services/asset-service.js +187 -0
  197. package/{src/services/index.ts → dist/services/index.d.ts} +1 -4
  198. package/dist/services/index.d.ts.map +1 -0
  199. package/dist/services/index.js +4 -0
  200. package/dist/storage/adapters/index.d.ts +2 -0
  201. package/dist/storage/adapters/index.d.ts.map +1 -0
  202. package/dist/storage/adapters/local-storage-adapter.d.ts +54 -0
  203. package/dist/storage/adapters/local-storage-adapter.d.ts.map +1 -0
  204. package/dist/storage/adapters/local-storage-adapter.js +187 -0
  205. package/dist/storage/index.d.ts +3 -0
  206. package/dist/storage/index.d.ts.map +1 -0
  207. package/{src/storage/index.ts → dist/storage/index.js} +0 -2
  208. package/dist/storage/interfaces/index.d.ts +2 -0
  209. package/dist/storage/interfaces/index.d.ts.map +1 -0
  210. package/dist/storage/interfaces/storage.d.ts +91 -0
  211. package/dist/storage/interfaces/storage.d.ts.map +1 -0
  212. package/dist/storage/interfaces/storage.js +1 -0
  213. package/dist/storage/providers/storage.d.ts +43 -0
  214. package/dist/storage/providers/storage.d.ts.map +1 -0
  215. package/dist/storage/providers/storage.js +64 -0
  216. package/dist/types/asset.d.ts +73 -0
  217. package/dist/types/asset.d.ts.map +1 -0
  218. package/dist/types/asset.js +2 -0
  219. package/dist/types/auth.d.ts +50 -0
  220. package/dist/types/auth.d.ts.map +1 -0
  221. package/dist/types/auth.js +41 -0
  222. package/dist/types/config.d.ts +47 -0
  223. package/dist/types/config.d.ts.map +1 -0
  224. package/dist/types/config.js +1 -0
  225. package/dist/types/document.d.ts +34 -0
  226. package/dist/types/document.d.ts.map +1 -0
  227. package/dist/types/document.js +1 -0
  228. package/dist/types/index.d.ts +9 -0
  229. package/dist/types/index.d.ts.map +1 -0
  230. package/dist/types/organization.d.ts +105 -0
  231. package/dist/types/organization.d.ts.map +1 -0
  232. package/dist/types/organization.js +3 -0
  233. package/dist/types/schemas.d.ts +114 -0
  234. package/dist/types/schemas.d.ts.map +1 -0
  235. package/dist/types/schemas.js +1 -0
  236. package/dist/types/sidebar.d.ts +33 -0
  237. package/dist/types/sidebar.d.ts.map +1 -0
  238. package/dist/types/sidebar.js +1 -0
  239. package/dist/types/user.d.ts +14 -0
  240. package/dist/types/user.d.ts.map +1 -0
  241. package/dist/types/user.js +1 -0
  242. package/dist/utils/content-hash.d.ts +22 -0
  243. package/dist/utils/content-hash.d.ts.map +1 -0
  244. package/dist/utils/content-hash.js +67 -0
  245. package/dist/utils/image-url.d.ts +88 -0
  246. package/dist/utils/image-url.d.ts.map +1 -0
  247. package/dist/utils/image-url.js +165 -0
  248. package/dist/utils/index.d.ts +6 -0
  249. package/dist/utils/index.d.ts.map +1 -0
  250. package/{src/utils/index.ts → dist/utils/index.js} +0 -3
  251. package/dist/utils/slug.d.ts +13 -0
  252. package/dist/utils/slug.d.ts.map +1 -0
  253. package/dist/utils/slug.js +30 -0
  254. package/package.json +12 -25
  255. package/src/api/assets.ts +0 -75
  256. package/src/api/client.ts +0 -150
  257. package/src/api/documents.ts +0 -102
  258. package/src/api/organizations.ts +0 -154
  259. package/src/api/types.ts +0 -34
  260. package/src/auth/auth-errors.ts +0 -23
  261. package/src/auth/auth-hooks.ts +0 -132
  262. package/src/auth/provider.ts +0 -25
  263. package/src/config.ts +0 -18
  264. package/src/db/interfaces/asset.ts +0 -61
  265. package/src/db/interfaces/document.ts +0 -53
  266. package/src/db/interfaces/index.ts +0 -98
  267. package/src/db/interfaces/organization.ts +0 -51
  268. package/src/db/interfaces/schema.ts +0 -13
  269. package/src/db/interfaces/user.ts +0 -16
  270. package/src/db/utils/reference-resolver.ts +0 -119
  271. package/src/define.ts +0 -7
  272. package/src/email/interfaces/email.ts +0 -45
  273. package/src/engine.ts +0 -85
  274. package/src/field-validation/rule.ts +0 -287
  275. package/src/field-validation/utils.ts +0 -91
  276. package/src/hooks.ts +0 -142
  277. package/src/lib/is-mobile.svelte.ts +0 -9
  278. package/src/lib/utils.ts +0 -13
  279. package/src/routes/assets-by-id.ts +0 -161
  280. package/src/routes/assets-cdn.ts +0 -185
  281. package/src/routes/assets.ts +0 -116
  282. package/src/routes/documents-by-id.ts +0 -188
  283. package/src/routes/documents-publish.ts +0 -211
  284. package/src/routes/documents.ts +0 -172
  285. package/src/routes/organizations-by-id.ts +0 -258
  286. package/src/routes/organizations-invitations.ts +0 -183
  287. package/src/routes/organizations-members.ts +0 -301
  288. package/src/routes/organizations-switch.ts +0 -74
  289. package/src/routes/organizations.ts +0 -146
  290. package/src/routes/schemas-by-type.ts +0 -35
  291. package/src/routes/schemas.ts +0 -19
  292. package/src/schema-context.svelte.ts +0 -24
  293. package/src/schema-utils/cleanup.ts +0 -116
  294. package/src/schema-utils/utils.ts +0 -47
  295. package/src/schema-utils/validator.ts +0 -58
  296. package/src/services/asset-service.ts +0 -256
  297. package/src/storage/adapters/local-storage-adapter.ts +0 -215
  298. package/src/storage/interfaces/storage.ts +0 -114
  299. package/src/storage/providers/storage.ts +0 -83
  300. package/src/types/asset.ts +0 -81
  301. package/src/types/auth.ts +0 -80
  302. package/src/types/config.ts +0 -45
  303. package/src/types/document.ts +0 -38
  304. package/src/types/organization.ts +0 -119
  305. package/src/types/schemas.ts +0 -151
  306. package/src/types/sidebar.ts +0 -37
  307. package/src/types/user.ts +0 -17
  308. package/src/utils/content-hash.ts +0 -75
  309. package/src/utils/image-url.ts +0 -204
  310. package/src/utils/slug.ts +0 -33
  311. /package/{src → dist}/app.d.ts +0 -0
  312. /package/{src → dist}/auth/MULTI_TENANCY_PLAN.md +0 -0
  313. /package/{src → dist}/components/admin/AdminLayout.svelte +0 -0
  314. /package/{src → dist}/components/admin/DocumentEditor.svelte +0 -0
  315. /package/{src → dist}/components/admin/DocumentTypesList.svelte +0 -0
  316. /package/{src → dist}/components/admin/ObjectModal.svelte +0 -0
  317. /package/{src → dist}/components/admin/SchemaField.svelte +0 -0
  318. /package/{src → dist}/components/admin/fields/ArrayField.svelte +0 -0
  319. /package/{src → dist}/components/admin/fields/BooleanField.svelte +0 -0
  320. /package/{src → dist}/components/admin/fields/ImageField.svelte +0 -0
  321. /package/{src → dist}/components/admin/fields/NumberField.svelte +0 -0
  322. /package/{src → dist}/components/admin/fields/ReferenceField.svelte +0 -0
  323. /package/{src → dist}/components/admin/fields/SlugField.svelte +0 -0
  324. /package/{src → dist}/components/admin/fields/StringField.svelte +0 -0
  325. /package/{src → dist}/components/admin/fields/TextareaField.svelte +0 -0
  326. /package/{src/components/fields/index.ts → dist/components/fields/index.js} +0 -0
  327. /package/{src → dist}/components/layout/OrganizationSwitcher.svelte +0 -0
  328. /package/{src → dist}/components/layout/sidebar/AppSidebar.svelte +0 -0
  329. /package/{src → dist}/components/layout/sidebar/NavMain.svelte +0 -0
  330. /package/{src → dist}/components/layout/sidebar/NavSecondary.svelte +0 -0
  331. /package/{src → dist}/components/layout/sidebar/NavUser.svelte +0 -0
  332. /package/{src → dist}/plugins/README.md +0 -0
  333. /package/{src/schema-utils/index.ts → dist/schema-utils/index.js} +0 -0
  334. /package/{src/storage/adapters/index.ts → dist/storage/adapters/index.js} +0 -0
  335. /package/{src/storage/interfaces/index.ts → dist/storage/interfaces/index.js} +0 -0
  336. /package/{src/types/index.ts → dist/types/index.js} +0 -0
@@ -1,47 +0,0 @@
1
- import type { SchemaType } from '../types/schemas.js';
2
-
3
- /**
4
- * Schema utility functions that work with a schema registry
5
- * These functions accept schemas as parameters to avoid package-level dependencies
6
- */
7
-
8
- /**
9
- * Get a schema type by name from a collection of schemas
10
- */
11
- export function getSchemaByName(schemas: SchemaType[], name: string): SchemaType | null {
12
- return schemas.find((schema) => schema.name === name) || null;
13
- }
14
-
15
- /**
16
- * Get all available object types (for array field dropdowns)
17
- */
18
- export function getObjectTypes(schemas: SchemaType[]): SchemaType[] {
19
- return schemas.filter((schema) => schema.type === 'object');
20
- }
21
-
22
- /**
23
- * Get all available document types
24
- */
25
- export function getDocumentTypes(schemas: SchemaType[]): SchemaType[] {
26
- return schemas.filter((schema) => schema.type === 'document');
27
- }
28
-
29
- /**
30
- * Check if a schema type exists
31
- */
32
- export function schemaExists(schemas: SchemaType[], name: string): boolean {
33
- return schemas.some((schema) => schema.name === name);
34
- }
35
-
36
- /**
37
- * Get the available types for an array field
38
- */
39
- export function getArrayTypes(
40
- schemas: SchemaType[],
41
- arrayField: { of?: Array<{ type: string }> }
42
- ): SchemaType[] {
43
- if (!arrayField.of) return [];
44
-
45
- const typeNames = arrayField.of.map((item) => item.type);
46
- return schemas.filter((schema) => typeNames.includes(schema.name));
47
- }
@@ -1,58 +0,0 @@
1
- import type { SchemaType, Field } from '../types/schemas.js';
2
-
3
- /**
4
- * Validate all schema references to ensure they exist
5
- */
6
- export function validateSchemaReferences(schemas: SchemaType[]): void {
7
- const schemaNames = new Set(schemas.map((schema) => schema.name));
8
- const errors: string[] = [];
9
-
10
- function validateField(field: Field, parentSchema: string): void {
11
- // Check array field references
12
- if (field.type === 'array' && field.of) {
13
- for (const arrayType of field.of) {
14
- if (!schemaNames.has(arrayType.type)) {
15
- errors.push(
16
- `Schema "${parentSchema}" field "${field.name}" references unknown type "${arrayType.type}"`
17
- );
18
- }
19
- }
20
- }
21
-
22
- // Check object field references (if they reference external types)
23
- if (field.type === 'object' && typeof field.fields === 'string') {
24
- if (!schemaNames.has(field.fields)) {
25
- errors.push(
26
- `Schema "${parentSchema}" field "${field.name}" references unknown object type "${field.fields}"`
27
- );
28
- }
29
- }
30
-
31
- // Recursively check nested fields -- cast type later or something. cba
32
- if ('fields' in field && Array.isArray(field.fields)) {
33
- for (const nestedField of field.fields) {
34
- validateField(nestedField, parentSchema);
35
- }
36
- }
37
- }
38
-
39
- // Validate each schema
40
- for (const schema of schemas) {
41
- if (schema.fields) {
42
- for (const field of schema.fields) {
43
- validateField(field, schema.name);
44
- }
45
- }
46
- }
47
-
48
- // Throw error if any validation issues found
49
- if (errors.length > 0) {
50
- console.error('\nSchema Validation Errors:');
51
- errors.forEach((error) => console.error(error));
52
-
53
- // Just throw the errors directly
54
- throw new Error(errors.join('\n'));
55
- }
56
-
57
- console.log('✅ Schema validation passed - all references are valid');
58
- }
@@ -1,256 +0,0 @@
1
- // Asset service - orchestrates storage and database operations
2
- import sharp from 'sharp';
3
- import type { StorageAdapter } from '../storage/interfaces/storage.js';
4
- import type { DatabaseAdapter } from '../db/interfaces/index.js';
5
- import type { Asset } from '../types/index.js';
6
-
7
- export interface AssetUploadData {
8
- buffer: Buffer;
9
- originalFilename: string;
10
- mimeType: string;
11
- size: number;
12
- title?: string;
13
- description?: string;
14
- alt?: string;
15
- creditLine?: string;
16
- createdBy?: string; // User ID who uploaded this asset
17
- metadata?: {
18
- schemaType?: string; // e.g., 'newsletterLanding'
19
- fieldPath?: string; // e.g., 'logo' or 'seo.metaImage'
20
- [key: string]: any; // Allow additional metadata
21
- };
22
- }
23
-
24
- export interface AssetFilters {
25
- assetType?: 'image' | 'file';
26
- mimeType?: string;
27
- search?: string;
28
- limit?: number;
29
- offset?: number;
30
- }
31
-
32
- /**
33
- * Asset service - coordinates storage and database operations
34
- * Maintains separation of concerns while providing unified asset management
35
- */
36
- export class AssetService {
37
- constructor(
38
- private storage: StorageAdapter,
39
- private database: DatabaseAdapter
40
- ) {}
41
-
42
- /**
43
- * Upload and store an asset
44
- */
45
- async uploadAsset(organizationId: string, data: AssetUploadData): Promise<Asset> {
46
- // Determine asset type
47
- const assetType = data.mimeType.startsWith('image/') ? 'image' : 'file';
48
-
49
- // Extract image metadata if it's an image
50
- let width: number | undefined;
51
- let height: number | undefined;
52
- let metadata: any = {
53
- // Include field metadata for privacy checking
54
- ...data.metadata
55
- };
56
-
57
- if (assetType === 'image') {
58
- try {
59
- const imageMetadata = await sharp(data.buffer).metadata();
60
- width = imageMetadata.width;
61
- height = imageMetadata.height;
62
-
63
- // Merge image metadata with field metadata
64
- metadata = {
65
- ...metadata, // Keep schemaType and fieldPath
66
- format: imageMetadata.format,
67
- space: imageMetadata.space,
68
- channels: imageMetadata.channels,
69
- density: imageMetadata.density,
70
- hasProfile: imageMetadata.hasProfile,
71
- hasAlpha: imageMetadata.hasAlpha
72
- };
73
-
74
- // Add dominant color
75
- const stats = await sharp(data.buffer).stats();
76
- metadata.dominantColor = stats.dominant;
77
- } catch (error) {
78
- console.warn('Could not extract image metadata:', error);
79
- }
80
- }
81
-
82
- // 1. Store file using storage adapter
83
- const storageFile = await this.storage.store({
84
- buffer: data.buffer,
85
- filename: data.originalFilename,
86
- mimeType: data.mimeType,
87
- size: data.size
88
- });
89
-
90
- // 2. Save asset metadata using database adapter
91
- try {
92
- const asset = await this.database.createAsset({
93
- assetType,
94
- filename: storageFile.path.split('/').pop() || data.originalFilename,
95
- originalFilename: data.originalFilename,
96
- mimeType: data.mimeType,
97
- size: data.size,
98
- url: storageFile.url || '', // Empty for local storage initially
99
- path: storageFile.path,
100
- storageAdapter: this.storage.name,
101
- organizationId,
102
- width,
103
- height,
104
- metadata,
105
- title: data.title || undefined,
106
- description: data.description || undefined,
107
- alt: data.alt || undefined,
108
- creditLine: data.creditLine || undefined,
109
- createdBy: data.createdBy
110
- });
111
-
112
- // If using local storage, generate and store the CDN URL with the real asset ID
113
- if (!storageFile.url) {
114
- const cdnUrl = `/media/${asset.id}/${encodeURIComponent(asset.originalFilename)}`;
115
-
116
- // Update both the object and the database
117
- asset.url = cdnUrl;
118
- await this.database.updateAsset(organizationId, asset.id, { url: cdnUrl });
119
- }
120
-
121
- return asset;
122
- } catch (error) {
123
- // If database save fails, clean up the stored file
124
- await this.storage.delete(storageFile.path);
125
- throw error;
126
- }
127
- }
128
-
129
- /**
130
- * Find asset by ID
131
- */
132
- async findAssetById(organizationId: string, id: string): Promise<Asset | null> {
133
- return await this.database.findAssetById(organizationId, id);
134
- }
135
-
136
- /**
137
- * Find asset by ID globally (bypasses organization filter for public asset access)
138
- * Only available on PostgreSQL adapter with RLS bypass
139
- */
140
- async findAssetByIdGlobal(id: string): Promise<Asset | null> {
141
- // Check if the adapter has the global method
142
- if (
143
- 'findAssetByIdGlobal' in this.database &&
144
- typeof this.database.findAssetByIdGlobal === 'function'
145
- ) {
146
- console.log('[AssetService] Using findAssetByIdGlobal from adapter');
147
- return await this.database.findAssetByIdGlobal(id);
148
- }
149
- // Fallback: not supported
150
- console.warn('[AssetService] findAssetByIdGlobal not supported by this database adapter');
151
- console.warn('[AssetService] Database adapter type:', this.database.constructor.name);
152
- console.warn(
153
- '[AssetService] Available methods:',
154
- Object.getOwnPropertyNames(Object.getPrototypeOf(this.database))
155
- );
156
- return null;
157
- }
158
-
159
- /**
160
- * Find multiple assets with filtering
161
- */
162
- async findAssets(organizationId: string, filters: AssetFilters = {}): Promise<Asset[]> {
163
- return await this.database.findAssets(organizationId, filters);
164
- }
165
-
166
- /**
167
- * Delete asset (both file and database record)
168
- *
169
- * Note: If the asset was stored by a different adapter (e.g., switching from R2 to local),
170
- * file deletion may fail. The database record will still be removed for a clean state.
171
- */
172
- async deleteAsset(organizationId: string, id: string): Promise<boolean> {
173
- const asset = await this.database.findAssetById(organizationId, id);
174
- if (!asset) {
175
- return false;
176
- }
177
-
178
- // Try to delete file from storage
179
- // If the asset was stored by a different adapter, this may fail
180
- if (asset.storageAdapter === this.storage.name) {
181
- // Same adapter - delete should work
182
- try {
183
- await this.storage.delete(asset.path);
184
- } catch (error) {
185
- console.warn(`Failed to delete file from storage: ${asset.path}`, error);
186
- }
187
- } else {
188
- // Different adapter - log warning but continue with database cleanup
189
- console.warn(
190
- `Asset ${id} was stored by '${asset.storageAdapter}' but current adapter is '${this.storage.name}'. ` +
191
- `File at ${asset.path} may need manual cleanup.`
192
- );
193
- }
194
-
195
- // Always delete database record for clean state
196
- return await this.database.deleteAsset(organizationId, id);
197
- }
198
-
199
- /**
200
- * Update asset metadata
201
- */
202
- async updateAssetMetadata(
203
- organizationId: string,
204
- id: string,
205
- metadata: {
206
- title?: string;
207
- description?: string;
208
- alt?: string;
209
- creditLine?: string;
210
- updatedBy?: string; // User ID who updated this asset
211
- }
212
- ): Promise<Asset | null> {
213
- return await this.database.updateAsset(organizationId, id, metadata);
214
- }
215
-
216
- /**
217
- * Get asset statistics
218
- */
219
- async getAssetStats(organizationId: string): Promise<{
220
- totalAssets: number;
221
- totalImages: number;
222
- totalFiles: number;
223
- totalSize: number;
224
- }> {
225
- const [totalAssets, assetsByType, totalSize] = await Promise.all([
226
- this.database.countAssets(organizationId),
227
- this.database.countAssetsByType(organizationId),
228
- this.database.getTotalAssetsSize(organizationId)
229
- ]);
230
-
231
- return {
232
- totalAssets,
233
- totalImages: assetsByType.image || 0,
234
- totalFiles: assetsByType.file || 0,
235
- totalSize
236
- };
237
- }
238
-
239
- /**
240
- * Get health status of both storage and database
241
- */
242
- async getHealthStatus(): Promise<{
243
- storage: boolean;
244
- database: boolean;
245
- }> {
246
- const [storageHealthy, databaseHealthy] = await Promise.all([
247
- this.storage.isHealthy(),
248
- this.database.isHealthy()
249
- ]);
250
-
251
- return {
252
- storage: storageHealthy,
253
- database: databaseHealthy
254
- };
255
- }
256
- }
@@ -1,215 +0,0 @@
1
- // Pure local file system storage adapter - no database operations
2
- import { writeFile, mkdir, unlink, stat, readdir } from 'fs/promises';
3
- import { join, dirname } from 'path';
4
- import type {
5
- StorageAdapter,
6
- StorageConfig,
7
- UploadFileData,
8
- StorageFile
9
- } from '../interfaces/storage.js';
10
-
11
- const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
12
- const DEFAULT_ALLOWED_TYPES = [
13
- 'image/jpeg',
14
- 'image/png',
15
- 'image/webp',
16
- 'image/gif',
17
- 'image/avif',
18
- 'application/pdf',
19
- 'text/plain'
20
- ];
21
-
22
- /**
23
- * Pure local file system storage adapter - only handles files
24
- */
25
- export class LocalStorageAdapter implements StorageAdapter {
26
- readonly name = 'local';
27
- private config: Required<StorageConfig>;
28
-
29
- constructor(config: StorageConfig) {
30
- this.config = {
31
- basePath: config.basePath,
32
- baseUrl: config.baseUrl || '',
33
- maxFileSize: config.maxFileSize || DEFAULT_MAX_FILE_SIZE,
34
- allowedTypes: config.allowedTypes || DEFAULT_ALLOWED_TYPES,
35
- options: config.options || {}
36
- };
37
- }
38
-
39
- /**
40
- * Generate unique filename preserving original name
41
- */
42
- private async generateUniqueFilename(originalFilename: string): Promise<string> {
43
- const { name, ext } = this.parseFilename(originalFilename);
44
- let filename = originalFilename;
45
- let counter = 1;
46
-
47
- // Check if file exists on disk
48
- while (await this.fileExistsOnDisk(filename)) {
49
- filename = `${name} (${counter})${ext}`;
50
- counter++;
51
- }
52
-
53
- return filename;
54
- }
55
-
56
- /**
57
- * Parse filename into name and extension
58
- */
59
- private parseFilename(filename: string): { name: string; ext: string } {
60
- const lastDotIndex = filename.lastIndexOf('.');
61
- if (lastDotIndex === -1) {
62
- return { name: filename, ext: '' };
63
- }
64
- return {
65
- name: filename.substring(0, lastDotIndex),
66
- ext: filename.substring(lastDotIndex)
67
- };
68
- }
69
-
70
- /**
71
- * Check if file exists on disk
72
- */
73
- private async fileExistsOnDisk(filename: string): Promise<boolean> {
74
- try {
75
- const filePath = join(this.config.basePath, filename);
76
- await stat(filePath);
77
- return true;
78
- } catch {
79
- return false;
80
- }
81
- }
82
-
83
- /**
84
- * Store a file and return storage info
85
- */
86
- async store(data: UploadFileData): Promise<StorageFile> {
87
- // Validate file type
88
- if (!this.config.allowedTypes.includes(data.mimeType)) {
89
- throw new Error(
90
- `Invalid file type: ${data.mimeType}. Allowed types: ${this.config.allowedTypes.join(', ')}`
91
- );
92
- }
93
-
94
- // Validate file size
95
- if (data.size > this.config.maxFileSize) {
96
- throw new Error(
97
- `File too large: ${data.size} bytes. Maximum size: ${this.config.maxFileSize} bytes`
98
- );
99
- }
100
-
101
- // Generate unique filename
102
- const filename = await this.generateUniqueFilename(data.filename);
103
- const filePath = join(this.config.basePath, filename);
104
-
105
- // Store internal path only - URL will be generated by API with asset ID
106
- // This forces all access through /assets/{id} for proper authorization
107
- const url = ''; // Will be populated as /assets/{assetId}/{filename} by the API
108
-
109
- console.log('[LocalStorageAdapter] Storing file:', {
110
- filename,
111
- filePath,
112
- note: 'URL will be generated as /assets/{assetId}/{filename}',
113
- basePath: this.config.basePath
114
- });
115
-
116
- // Ensure storage directory exists
117
- await mkdir(dirname(filePath), { recursive: true });
118
-
119
- // Save file to disk
120
- await writeFile(filePath, data.buffer);
121
-
122
- return {
123
- path: filePath,
124
- url, // Empty - API will generate clean URL with asset ID
125
- size: data.size
126
- };
127
- }
128
-
129
- /**
130
- * Read a file from storage
131
- * Used by API endpoint to serve files
132
- */
133
- async getObject(path: string): Promise<Buffer> {
134
- const { readFile } = await import('fs/promises');
135
- return await readFile(path);
136
- }
137
-
138
- /**
139
- * Delete a file from storage
140
- */
141
- async delete(path: string): Promise<boolean> {
142
- try {
143
- await unlink(path);
144
- return true;
145
- } catch (error) {
146
- console.warn('Could not delete file from disk:', error);
147
- return false;
148
- }
149
- }
150
-
151
- /**
152
- * Check if file exists
153
- */
154
- async exists(path: string): Promise<boolean> {
155
- try {
156
- await stat(path);
157
- return true;
158
- } catch {
159
- return false;
160
- }
161
- }
162
-
163
- /**
164
- * Get public URL for a file path
165
- */
166
- getUrl(path: string): string {
167
- // Extract filename from path and construct URL
168
- const filename = path.split('/').pop() || '';
169
- return `${this.config.baseUrl}/${encodeURIComponent(filename)}`;
170
- }
171
-
172
- /**
173
- * Get storage information
174
- */
175
- async getStorageInfo(): Promise<{ totalSize: number; availableSpace?: number }> {
176
- try {
177
- // Calculate total size of files in storage directory
178
- const files = await readdir(this.config.basePath);
179
- let totalSize = 0;
180
-
181
- for (const file of files) {
182
- try {
183
- const filePath = join(this.config.basePath, file);
184
- const stats = await stat(filePath);
185
- if (stats.isFile()) {
186
- totalSize += stats.size;
187
- }
188
- } catch {
189
- // Skip files that can't be read
190
- }
191
- }
192
-
193
- return { totalSize };
194
- } catch (error) {
195
- console.error('Error getting storage info:', error);
196
- return { totalSize: 0 };
197
- }
198
- }
199
-
200
- /**
201
- * Health check - test if we can write to storage
202
- */
203
- async isHealthy(): Promise<boolean> {
204
- try {
205
- const testFile = join(this.config.basePath, `health-check-${Date.now()}.tmp`);
206
- await mkdir(dirname(testFile), { recursive: true });
207
- await writeFile(testFile, 'health check');
208
- await unlink(testFile);
209
- return true;
210
- } catch (error) {
211
- console.error('Storage health check failed:', error);
212
- return false;
213
- }
214
- }
215
- }
@@ -1,114 +0,0 @@
1
- // Pure storage interface for file operations only
2
- export interface StorageFile {
3
- path: string;
4
- url: string;
5
- size: number;
6
- }
7
-
8
- export interface UploadFileData {
9
- buffer: Buffer;
10
- filename: string;
11
- mimeType: string;
12
- size: number;
13
- }
14
-
15
- export interface StorageConfig {
16
- basePath: string;
17
- baseUrl?: string;
18
- maxFileSize?: number;
19
- allowedTypes?: string[];
20
- options?: {
21
- [key: string]: any;
22
- };
23
- }
24
-
25
- export interface StorageObjectMetadata {
26
- key: string;
27
- size: number;
28
- lastModified: Date;
29
- contentType?: string;
30
- etag?: string;
31
- }
32
-
33
- export interface ListObjectsOptions {
34
- prefix?: string;
35
- maxKeys?: number;
36
- continuationToken?: string;
37
- }
38
-
39
- export interface ListObjectsResult {
40
- objects: StorageObjectMetadata[];
41
- isTruncated: boolean;
42
- continuationToken?: string;
43
- }
44
-
45
- /**
46
- * Pure storage interface - only handles file operations
47
- * No database operations - completely agnostic
48
- */
49
- export interface StorageAdapter {
50
- // Adapter identifier (used to track which adapter stored each file)
51
- readonly name: string;
52
-
53
- // Core file operations (required)
54
- store(data: UploadFileData): Promise<StorageFile>;
55
- delete(path: string): Promise<boolean>;
56
- exists(path: string): Promise<boolean>;
57
- getUrl(path: string): string;
58
-
59
- // Storage info
60
- getStorageInfo(): Promise<{
61
- totalSize: number;
62
- availableSpace?: number;
63
- }>;
64
-
65
- // Health check
66
- isHealthy(): Promise<boolean>;
67
-
68
- // Connection management (optional)
69
- connect?(): Promise<void>;
70
- disconnect?(): Promise<void>;
71
-
72
- // Extended file operations (optional - for advanced storage features)
73
-
74
- /**
75
- * Retrieve file contents as Buffer
76
- * Useful for: image processing, transformations, backups, migrations
77
- */
78
- getObject?(path: string): Promise<Buffer>;
79
-
80
- /**
81
- * List objects in storage with optional filtering
82
- * Useful for: admin UI file browser, asset management, cleanup operations
83
- */
84
- listObjects?(options?: ListObjectsOptions): Promise<ListObjectsResult>;
85
-
86
- /**
87
- * Copy/duplicate an object within storage
88
- * Useful for: versioning, backups, image variant generation
89
- */
90
- copyObject?(sourcePath: string, destPath: string): Promise<boolean>;
91
-
92
- /**
93
- * Get object metadata without downloading the file
94
- * Useful for: verifying file integrity, checking size/type
95
- */
96
- getObjectMetadata?(path: string): Promise<StorageObjectMetadata>;
97
-
98
- /**
99
- * Generate a signed URL for temporary access to a file
100
- * Useful for: access control, private files, secure downloads
101
- * @param path - File path
102
- * @param expiresIn - Expiration time in seconds (default: 3600)
103
- * @returns Signed URL that expires after the specified time
104
- */
105
- getSignedUrl?(path: string, expiresIn?: number): Promise<string>;
106
- }
107
-
108
- /**
109
- * Storage provider factory interface
110
- */
111
- export interface StorageProvider {
112
- name: string;
113
- createAdapter(config: StorageConfig): StorageAdapter;
114
- }