nextjs-cms 0.0.1 → 0.5.2

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 (302) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +289 -0
  3. package/dist/api/axios/axiosInstance.d.ts +2 -0
  4. package/dist/api/axios/axiosInstance.d.ts.map +1 -0
  5. package/dist/api/axios/axiosInstance.js +8 -0
  6. package/dist/api/index.d.ts +856 -0
  7. package/dist/api/index.d.ts.map +1 -0
  8. package/dist/api/index.js +12 -0
  9. package/dist/api/lib/serverActions.d.ts +240 -0
  10. package/dist/api/lib/serverActions.d.ts.map +1 -0
  11. package/dist/api/lib/serverActions.js +834 -0
  12. package/dist/api/root.d.ts +829 -0
  13. package/dist/api/root.d.ts.map +1 -0
  14. package/dist/api/root.js +30 -0
  15. package/dist/api/routers/accountSettings.d.ts +61 -0
  16. package/dist/api/routers/accountSettings.d.ts.map +1 -0
  17. package/dist/api/routers/accountSettings.js +108 -0
  18. package/dist/api/routers/admins.d.ts +106 -0
  19. package/dist/api/routers/admins.d.ts.map +1 -0
  20. package/dist/api/routers/admins.js +219 -0
  21. package/dist/api/routers/auth.d.ts +48 -0
  22. package/dist/api/routers/auth.d.ts.map +1 -0
  23. package/dist/api/routers/auth.js +25 -0
  24. package/dist/api/routers/categorySection.d.ts +104 -0
  25. package/dist/api/routers/categorySection.d.ts.map +1 -0
  26. package/dist/api/routers/categorySection.js +38 -0
  27. package/dist/api/routers/cmsSettings.d.ts +49 -0
  28. package/dist/api/routers/cmsSettings.d.ts.map +1 -0
  29. package/dist/api/routers/cmsSettings.js +51 -0
  30. package/dist/api/routers/cpanel.d.ts +84 -0
  31. package/dist/api/routers/cpanel.d.ts.map +1 -0
  32. package/dist/api/routers/cpanel.js +216 -0
  33. package/dist/api/routers/files.d.ts +48 -0
  34. package/dist/api/routers/files.d.ts.map +1 -0
  35. package/dist/api/routers/files.js +23 -0
  36. package/dist/api/routers/gallery.d.ts +36 -0
  37. package/dist/api/routers/gallery.d.ts.map +1 -0
  38. package/dist/api/routers/gallery.js +62 -0
  39. package/dist/api/routers/googleAnalytics.d.ts +31 -0
  40. package/dist/api/routers/googleAnalytics.d.ts.map +1 -0
  41. package/dist/api/routers/googleAnalytics.js +7 -0
  42. package/dist/api/routers/hasItemsSection.d.ts +140 -0
  43. package/dist/api/routers/hasItemsSection.d.ts.map +1 -0
  44. package/dist/api/routers/hasItemsSection.js +34 -0
  45. package/dist/api/routers/navigation.d.ts +52 -0
  46. package/dist/api/routers/navigation.d.ts.map +1 -0
  47. package/dist/api/routers/navigation.js +11 -0
  48. package/dist/api/routers/simpleSection.d.ts +58 -0
  49. package/dist/api/routers/simpleSection.d.ts.map +1 -0
  50. package/dist/api/routers/simpleSection.js +12 -0
  51. package/dist/api/trpc.d.ts +107 -0
  52. package/dist/api/trpc.d.ts.map +1 -0
  53. package/dist/api/trpc.js +72 -0
  54. package/dist/auth/axios/axiosInstance.d.ts +2 -0
  55. package/dist/auth/axios/axiosInstance.d.ts.map +1 -0
  56. package/dist/auth/axios/axiosInstance.js +8 -0
  57. package/dist/auth/csrf.d.ts +30 -0
  58. package/dist/auth/csrf.d.ts.map +1 -0
  59. package/dist/auth/csrf.js +76 -0
  60. package/dist/auth/hooks/index.d.ts +4 -0
  61. package/dist/auth/hooks/index.d.ts.map +1 -0
  62. package/dist/auth/hooks/index.js +3 -0
  63. package/dist/auth/hooks/useAxiosPrivate.d.ts +5 -0
  64. package/dist/auth/hooks/useAxiosPrivate.d.ts.map +1 -0
  65. package/dist/auth/hooks/useAxiosPrivate.js +74 -0
  66. package/dist/auth/hooks/useRefreshToken.d.ts +7 -0
  67. package/dist/auth/hooks/useRefreshToken.d.ts.map +1 -0
  68. package/dist/auth/hooks/useRefreshToken.js +79 -0
  69. package/dist/auth/index.d.ts +23 -0
  70. package/dist/auth/index.d.ts.map +1 -0
  71. package/dist/auth/index.js +44 -0
  72. package/dist/auth/jwt.d.ts +6 -0
  73. package/dist/auth/jwt.d.ts.map +1 -0
  74. package/dist/auth/jwt.js +25 -0
  75. package/dist/auth/lib/actions.d.ts +33 -0
  76. package/dist/auth/lib/actions.d.ts.map +1 -0
  77. package/dist/auth/lib/actions.js +209 -0
  78. package/dist/auth/lib/client.d.ts +4 -0
  79. package/dist/auth/lib/client.d.ts.map +1 -0
  80. package/dist/auth/lib/client.js +46 -0
  81. package/dist/auth/lib/index.d.ts +3 -0
  82. package/dist/auth/lib/index.d.ts.map +1 -0
  83. package/dist/auth/lib/index.js +2 -0
  84. package/dist/auth/react.d.ts +106 -0
  85. package/dist/auth/react.d.ts.map +1 -0
  86. package/dist/auth/react.js +347 -0
  87. package/dist/auth/trpc.d.ts +6 -0
  88. package/dist/auth/trpc.d.ts.map +1 -0
  89. package/dist/auth/trpc.js +81 -0
  90. package/dist/core/config/config-loader.d.ts +92 -0
  91. package/dist/core/config/config-loader.d.ts.map +1 -0
  92. package/dist/core/config/config-loader.js +230 -0
  93. package/dist/core/config/index.d.ts +3 -0
  94. package/dist/core/config/index.d.ts.map +1 -0
  95. package/dist/core/config/index.js +1 -0
  96. package/dist/core/config/loader.d.ts +2 -0
  97. package/dist/core/config/loader.d.ts.map +1 -0
  98. package/dist/core/config/loader.js +42 -0
  99. package/dist/core/db/index.d.ts +2 -0
  100. package/dist/core/db/index.d.ts.map +1 -0
  101. package/dist/core/db/index.js +1 -0
  102. package/dist/core/db/table-checker/DbTable.d.ts +6 -0
  103. package/dist/core/db/table-checker/DbTable.d.ts.map +1 -0
  104. package/dist/core/db/table-checker/DbTable.js +5 -0
  105. package/dist/core/db/table-checker/MysqlTable.d.ts +34 -0
  106. package/dist/core/db/table-checker/MysqlTable.d.ts.map +1 -0
  107. package/dist/core/db/table-checker/MysqlTable.js +102 -0
  108. package/dist/core/db/table-checker/index.d.ts +2 -0
  109. package/dist/core/db/table-checker/index.d.ts.map +1 -0
  110. package/dist/core/db/table-checker/index.js +1 -0
  111. package/dist/core/factories/FieldFactory.d.ts +124 -0
  112. package/dist/core/factories/FieldFactory.d.ts.map +1 -0
  113. package/dist/core/factories/FieldFactory.js +411 -0
  114. package/dist/core/factories/SectionFactory.d.ts +110 -0
  115. package/dist/core/factories/SectionFactory.d.ts.map +1 -0
  116. package/dist/core/factories/SectionFactory.js +415 -0
  117. package/dist/core/factories/index.d.ts +3 -0
  118. package/dist/core/factories/index.d.ts.map +1 -0
  119. package/dist/core/factories/index.js +2 -0
  120. package/dist/core/fields/checkbox.d.ts +63 -0
  121. package/dist/core/fields/checkbox.d.ts.map +1 -0
  122. package/dist/core/fields/checkbox.js +62 -0
  123. package/dist/core/fields/color.d.ts +84 -0
  124. package/dist/core/fields/color.d.ts.map +1 -0
  125. package/dist/core/fields/color.js +91 -0
  126. package/dist/core/fields/date.d.ts +100 -0
  127. package/dist/core/fields/date.d.ts.map +1 -0
  128. package/dist/core/fields/date.js +108 -0
  129. package/dist/core/fields/document.d.ts +180 -0
  130. package/dist/core/fields/document.d.ts.map +1 -0
  131. package/dist/core/fields/document.js +277 -0
  132. package/dist/core/fields/field-group.d.ts +18 -0
  133. package/dist/core/fields/field-group.d.ts.map +1 -0
  134. package/dist/core/fields/field-group.js +6 -0
  135. package/dist/core/fields/field.d.ts +126 -0
  136. package/dist/core/fields/field.d.ts.map +1 -0
  137. package/dist/core/fields/field.js +148 -0
  138. package/dist/core/fields/fileField.d.ts +15 -0
  139. package/dist/core/fields/fileField.d.ts.map +1 -0
  140. package/dist/core/fields/fileField.js +5 -0
  141. package/dist/core/fields/index.d.ts +65 -0
  142. package/dist/core/fields/index.d.ts.map +1 -0
  143. package/dist/core/fields/index.js +18 -0
  144. package/dist/core/fields/map.d.ts +167 -0
  145. package/dist/core/fields/map.d.ts.map +1 -0
  146. package/dist/core/fields/map.js +152 -0
  147. package/dist/core/fields/number.d.ts +186 -0
  148. package/dist/core/fields/number.d.ts.map +1 -0
  149. package/dist/core/fields/number.js +241 -0
  150. package/dist/core/fields/password.d.ts +109 -0
  151. package/dist/core/fields/password.d.ts.map +1 -0
  152. package/dist/core/fields/password.js +133 -0
  153. package/dist/core/fields/photo.d.ts +289 -0
  154. package/dist/core/fields/photo.d.ts.map +1 -0
  155. package/dist/core/fields/photo.js +410 -0
  156. package/dist/core/fields/richText.d.ts +295 -0
  157. package/dist/core/fields/richText.d.ts.map +1 -0
  158. package/dist/core/fields/richText.js +338 -0
  159. package/dist/core/fields/select.d.ts +366 -0
  160. package/dist/core/fields/select.d.ts.map +1 -0
  161. package/dist/core/fields/select.js +499 -0
  162. package/dist/core/fields/selectMultiple.d.ts +236 -0
  163. package/dist/core/fields/selectMultiple.d.ts.map +1 -0
  164. package/dist/core/fields/selectMultiple.js +417 -0
  165. package/dist/core/fields/tags.d.ts +131 -0
  166. package/dist/core/fields/tags.d.ts.map +1 -0
  167. package/dist/core/fields/tags.js +105 -0
  168. package/dist/core/fields/text.d.ts +136 -0
  169. package/dist/core/fields/text.d.ts.map +1 -0
  170. package/dist/core/fields/text.js +157 -0
  171. package/dist/core/fields/textArea.d.ts +107 -0
  172. package/dist/core/fields/textArea.d.ts.map +1 -0
  173. package/dist/core/fields/textArea.js +126 -0
  174. package/dist/core/fields/video.d.ts +148 -0
  175. package/dist/core/fields/video.d.ts.map +1 -0
  176. package/dist/core/fields/video.js +248 -0
  177. package/dist/core/helpers/entity.d.ts +8 -0
  178. package/dist/core/helpers/entity.d.ts.map +1 -0
  179. package/dist/core/helpers/entity.js +27 -0
  180. package/dist/core/helpers/index.d.ts +5 -0
  181. package/dist/core/helpers/index.d.ts.map +1 -0
  182. package/dist/core/helpers/index.js +3 -0
  183. package/dist/core/index.d.ts +8 -0
  184. package/dist/core/index.d.ts.map +1 -0
  185. package/dist/core/index.js +7 -0
  186. package/dist/core/sections/category.d.ts +283 -0
  187. package/dist/core/sections/category.d.ts.map +1 -0
  188. package/dist/core/sections/category.js +147 -0
  189. package/dist/core/sections/hasItems.d.ts +632 -0
  190. package/dist/core/sections/hasItems.d.ts.map +1 -0
  191. package/dist/core/sections/hasItems.js +144 -0
  192. package/dist/core/sections/index.d.ts +5 -0
  193. package/dist/core/sections/index.d.ts.map +1 -0
  194. package/dist/core/sections/index.js +4 -0
  195. package/dist/core/sections/section.d.ts +226 -0
  196. package/dist/core/sections/section.d.ts.map +1 -0
  197. package/dist/core/sections/section.js +341 -0
  198. package/dist/core/sections/simple.d.ts +99 -0
  199. package/dist/core/sections/simple.d.ts.map +1 -0
  200. package/dist/core/sections/simple.js +95 -0
  201. package/dist/core/security/dom.d.ts +11 -0
  202. package/dist/core/security/dom.d.ts.map +1 -0
  203. package/dist/core/security/dom.js +92 -0
  204. package/dist/core/submit/ItemEditSubmit.d.ts +76 -0
  205. package/dist/core/submit/ItemEditSubmit.d.ts.map +1 -0
  206. package/dist/core/submit/ItemEditSubmit.js +186 -0
  207. package/dist/core/submit/NewItemSubmit.d.ts +14 -0
  208. package/dist/core/submit/NewItemSubmit.d.ts.map +1 -0
  209. package/dist/core/submit/NewItemSubmit.js +93 -0
  210. package/dist/core/submit/SimpleSectionSubmit.d.ts +13 -0
  211. package/dist/core/submit/SimpleSectionSubmit.d.ts.map +1 -0
  212. package/dist/core/submit/SimpleSectionSubmit.js +93 -0
  213. package/dist/core/submit/index.d.ts +5 -0
  214. package/dist/core/submit/index.d.ts.map +1 -0
  215. package/dist/core/submit/index.js +4 -0
  216. package/dist/core/submit/submit.d.ts +116 -0
  217. package/dist/core/submit/submit.d.ts.map +1 -0
  218. package/dist/core/submit/submit.js +479 -0
  219. package/dist/core/types/index.d.ts +280 -0
  220. package/dist/core/types/index.d.ts.map +1 -0
  221. package/dist/core/types/index.js +1 -0
  222. package/dist/db/client.d.ts +9 -0
  223. package/dist/db/client.d.ts.map +1 -0
  224. package/dist/db/client.js +19 -0
  225. package/dist/db/config.d.ts +6 -0
  226. package/dist/db/config.d.ts.map +1 -0
  227. package/dist/db/config.js +22 -0
  228. package/dist/db/drizzle.config.d.ts +6 -0
  229. package/dist/db/drizzle.config.d.ts.map +1 -0
  230. package/dist/db/drizzle.config.js +18 -0
  231. package/dist/db/index.d.ts +3 -0
  232. package/dist/db/index.d.ts.map +1 -0
  233. package/dist/db/index.js +3 -0
  234. package/dist/db/schema.d.ts +639 -0
  235. package/dist/db/schema.d.ts.map +1 -0
  236. package/dist/db/schema.js +73 -0
  237. package/dist/index.d.ts +7 -1
  238. package/dist/index.d.ts.map +1 -1
  239. package/dist/index.js +7 -1
  240. package/dist/translations/dictionaries/ar.json +279 -0
  241. package/dist/translations/dictionaries/en.json +279 -0
  242. package/dist/translations/index.d.ts +3 -0
  243. package/dist/translations/index.d.ts.map +1 -0
  244. package/dist/translations/index.js +15 -0
  245. package/dist/utils/CpanelApi.d.ts +25 -0
  246. package/dist/utils/CpanelApi.d.ts.map +1 -0
  247. package/dist/utils/CpanelApi.js +64 -0
  248. package/dist/utils/constants.d.ts +14 -0
  249. package/dist/utils/constants.d.ts.map +1 -0
  250. package/dist/utils/constants.js +61 -0
  251. package/dist/utils/index.d.ts +5 -0
  252. package/dist/utils/index.d.ts.map +1 -0
  253. package/dist/utils/index.js +4 -0
  254. package/dist/utils/utils.d.ts +60 -0
  255. package/dist/utils/utils.d.ts.map +1 -0
  256. package/dist/utils/utils.js +132 -0
  257. package/dist/validators/checkbox.d.ts +4 -0
  258. package/dist/validators/checkbox.d.ts.map +1 -0
  259. package/dist/validators/checkbox.js +12 -0
  260. package/dist/validators/color.d.ts +4 -0
  261. package/dist/validators/color.d.ts.map +1 -0
  262. package/dist/validators/color.js +7 -0
  263. package/dist/validators/date.d.ts +4 -0
  264. package/dist/validators/date.d.ts.map +1 -0
  265. package/dist/validators/date.js +5 -0
  266. package/dist/validators/document.d.ts +4 -0
  267. package/dist/validators/document.d.ts.map +1 -0
  268. package/dist/validators/document.js +57 -0
  269. package/dist/validators/index.d.ts +15 -0
  270. package/dist/validators/index.d.ts.map +1 -0
  271. package/dist/validators/index.js +14 -0
  272. package/dist/validators/map.d.ts +4 -0
  273. package/dist/validators/map.d.ts.map +1 -0
  274. package/dist/validators/map.js +5 -0
  275. package/dist/validators/number.d.ts +4 -0
  276. package/dist/validators/number.d.ts.map +1 -0
  277. package/dist/validators/number.js +20 -0
  278. package/dist/validators/password.d.ts +4 -0
  279. package/dist/validators/password.d.ts.map +1 -0
  280. package/dist/validators/password.js +11 -0
  281. package/dist/validators/photo.d.ts +4 -0
  282. package/dist/validators/photo.d.ts.map +1 -0
  283. package/dist/validators/photo.js +100 -0
  284. package/dist/validators/richText.d.ts +4 -0
  285. package/dist/validators/richText.d.ts.map +1 -0
  286. package/dist/validators/richText.js +8 -0
  287. package/dist/validators/select-multiple.d.ts +10 -0
  288. package/dist/validators/select-multiple.d.ts.map +1 -0
  289. package/dist/validators/select-multiple.js +20 -0
  290. package/dist/validators/select.d.ts +4 -0
  291. package/dist/validators/select.d.ts.map +1 -0
  292. package/dist/validators/select.js +5 -0
  293. package/dist/validators/text.d.ts +4 -0
  294. package/dist/validators/text.d.ts.map +1 -0
  295. package/dist/validators/text.js +7 -0
  296. package/dist/validators/textarea.d.ts +4 -0
  297. package/dist/validators/textarea.d.ts.map +1 -0
  298. package/dist/validators/textarea.js +7 -0
  299. package/dist/validators/video.d.ts +4 -0
  300. package/dist/validators/video.d.ts.map +1 -0
  301. package/dist/validators/video.js +57 -0
  302. package/package.json +150 -6
@@ -0,0 +1,289 @@
1
+ import type { BaseFieldConfig } from './field';
2
+ import { entityKind } from '../helpers';
3
+ import * as z from 'zod';
4
+ import { FileField } from './fileField';
5
+ declare const configSchema: z.ZodObject<{
6
+ /** Whether to add watermark to images */
7
+ watermark: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
8
+ blurPlaceholder: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
9
+ size: z.ZodOptional<z.ZodObject<{
10
+ width: z.ZodNumber;
11
+ height: z.ZodNumber;
12
+ /**
13
+ * @true The image will not have dimensions constraints,
14
+ * and will be cropped to fit the specified dimensions
15
+ *
16
+ * @false The user will be forced to upload an image with the specified dimensions
17
+ *
18
+ * @example
19
+ * size: {
20
+ * width: 1200,
21
+ * height: 450,
22
+ * crop: true, // No constraints
23
+ * }
24
+ */
25
+ crop: z.ZodBoolean;
26
+ }, z.core.$strict>>;
27
+ thumbnail: z.ZodOptional<z.ZodObject<{
28
+ width: z.ZodNumber;
29
+ height: z.ZodNumber;
30
+ crop: z.ZodBoolean;
31
+ quality: z.ZodOptional<z.ZodNumber>;
32
+ }, z.core.$strict>>;
33
+ /**
34
+ * Maximum file size
35
+ * @example
36
+ * maxFileSize: {
37
+ * size: 512,
38
+ * unit: 'kb',
39
+ * }
40
+ */
41
+ maxFileSize: z.ZodOptional<z.ZodObject<{
42
+ size: z.ZodNumber;
43
+ unit: z.ZodEnum<{
44
+ kb: "kb";
45
+ mb: "mb";
46
+ }>;
47
+ }, z.core.$strict>>;
48
+ /**
49
+ * Allowed image types
50
+ * @example
51
+ * type: ['jpeg', 'png', 'webp']
52
+ * @default ['jpeg']
53
+ * @link https://sharp.pixelplumbing.com/api-output#toformat
54
+ * @hint 'jpg' is an alias for 'jpeg'
55
+ */
56
+ type: z.ZodOptional<z.ZodArray<z.ZodEnum<{
57
+ webp: "webp";
58
+ jpg: "jpg";
59
+ jpeg: "jpeg";
60
+ png: "png";
61
+ }>>>;
62
+ /**
63
+ * Remove the extension from the file name
64
+ * @default true
65
+ */
66
+ removeExtension: z.ZodOptional<z.ZodBoolean>;
67
+ }, z.core.$strict>;
68
+ type Config = z.infer<typeof configSchema>;
69
+ export declare class PhotoField extends FileField<'photo', Config> {
70
+ static readonly [entityKind]: string;
71
+ readonly watermark: boolean | null | undefined;
72
+ readonly blurPlaceholder: boolean | null | undefined;
73
+ readonly size: {
74
+ width: number;
75
+ height: number;
76
+ crop: boolean;
77
+ } | undefined;
78
+ readonly maxFileSize: {
79
+ size: number;
80
+ unit: 'kb' | 'mb';
81
+ };
82
+ readonly mimeType: string[];
83
+ readonly extensions: string[];
84
+ readonly thumbnail: {
85
+ width: number;
86
+ height: number;
87
+ crop: boolean;
88
+ quality?: number;
89
+ };
90
+ readonly removeExtension: boolean;
91
+ readonly uploadsFolder: string;
92
+ /**
93
+ * _file is the file object if it's present
94
+ * Whereas the value is the path to the file
95
+ */
96
+ private _file;
97
+ private _sharpImage;
98
+ private _folder;
99
+ private _allowedExtensions;
100
+ constructor(config: BaseFieldConfig<Config>, file?: File);
101
+ exportForClient(): {
102
+ thumbnail: {
103
+ width: number;
104
+ height: number;
105
+ crop: boolean;
106
+ quality?: number;
107
+ };
108
+ size: {
109
+ width: number;
110
+ height: number;
111
+ crop: boolean;
112
+ } | undefined;
113
+ maxFileSize: {
114
+ size: number;
115
+ unit: "kb" | "mb";
116
+ };
117
+ extensions: string[];
118
+ mimeType: string[];
119
+ type: "photo";
120
+ name: string;
121
+ label: string;
122
+ required: boolean;
123
+ conditionalFields: import("../types").ConditionalField[];
124
+ readonly: boolean;
125
+ defaultValue: any;
126
+ value: any;
127
+ };
128
+ /**
129
+ * Write the file to the disk
130
+ */
131
+ writeToFile(): Promise<void>;
132
+ postSubmit(folder: string): Promise<void>;
133
+ postSubmitRollback(): Promise<void>;
134
+ /**
135
+ * Get the value of the field
136
+ */
137
+ getValue(): string;
138
+ setFileName(value: string): void;
139
+ setValue(value: any): void;
140
+ setFile(file: File): void;
141
+ checkRequired(): void;
142
+ /**
143
+ * Prepare the field for submission
144
+ */
145
+ prepareForSubmission(): Promise<void>;
146
+ }
147
+ export type PhotoFieldClientConfig = ReturnType<PhotoField['exportForClient']>;
148
+ declare const optionsSchema: z.ZodObject<{
149
+ /** Whether to add watermark to images */
150
+ watermark: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
151
+ blurPlaceholder: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
152
+ size: z.ZodOptional<z.ZodObject<{
153
+ width: z.ZodNumber;
154
+ height: z.ZodNumber;
155
+ /**
156
+ * @true The image will not have dimensions constraints,
157
+ * and will be cropped to fit the specified dimensions
158
+ *
159
+ * @false The user will be forced to upload an image with the specified dimensions
160
+ *
161
+ * @example
162
+ * size: {
163
+ * width: 1200,
164
+ * height: 450,
165
+ * crop: true, // No constraints
166
+ * }
167
+ */
168
+ crop: z.ZodBoolean;
169
+ }, z.core.$strict>>;
170
+ thumbnail: z.ZodOptional<z.ZodObject<{
171
+ width: z.ZodNumber;
172
+ height: z.ZodNumber;
173
+ crop: z.ZodBoolean;
174
+ quality: z.ZodOptional<z.ZodNumber>;
175
+ }, z.core.$strict>>;
176
+ /**
177
+ * Maximum file size
178
+ * @example
179
+ * maxFileSize: {
180
+ * size: 512,
181
+ * unit: 'kb',
182
+ * }
183
+ */
184
+ maxFileSize: z.ZodOptional<z.ZodObject<{
185
+ size: z.ZodNumber;
186
+ unit: z.ZodEnum<{
187
+ kb: "kb";
188
+ mb: "mb";
189
+ }>;
190
+ }, z.core.$strict>>;
191
+ /**
192
+ * Allowed image types
193
+ * @example
194
+ * type: ['jpeg', 'png', 'webp']
195
+ * @default ['jpeg']
196
+ * @link https://sharp.pixelplumbing.com/api-output#toformat
197
+ * @hint 'jpg' is an alias for 'jpeg'
198
+ */
199
+ type: z.ZodOptional<z.ZodArray<z.ZodEnum<{
200
+ webp: "webp";
201
+ jpg: "jpg";
202
+ jpeg: "jpeg";
203
+ png: "png";
204
+ }>>>;
205
+ /**
206
+ * Remove the extension from the file name
207
+ * @default true
208
+ */
209
+ removeExtension: z.ZodOptional<z.ZodBoolean>;
210
+ name: z.ZodString;
211
+ label: z.ZodOptional<z.ZodString>;
212
+ required: z.ZodOptional<z.ZodBoolean>;
213
+ defaultValue: z.ZodOptional<z.ZodAny>;
214
+ order: z.ZodOptional<z.ZodNumber>;
215
+ conditionalRules: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types").ConditionalRule, import("../types").ConditionalRule>>>;
216
+ adminGenerated: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodLiteral<false>, z.ZodLiteral<"readonly">]>>;
217
+ }, z.core.$strict>;
218
+ declare const photoFieldConfigSchema: z.ZodObject<{
219
+ type: z.ZodLiteral<"photo">;
220
+ build: z.ZodFunction<z.core.$ZodFunctionArgs, z.ZodCustom<PhotoField, PhotoField>>;
221
+ /** Whether to add watermark to images */
222
+ watermark: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
223
+ blurPlaceholder: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
224
+ size: z.ZodOptional<z.ZodObject<{
225
+ width: z.ZodNumber;
226
+ height: z.ZodNumber;
227
+ /**
228
+ * @true The image will not have dimensions constraints,
229
+ * and will be cropped to fit the specified dimensions
230
+ *
231
+ * @false The user will be forced to upload an image with the specified dimensions
232
+ *
233
+ * @example
234
+ * size: {
235
+ * width: 1200,
236
+ * height: 450,
237
+ * crop: true, // No constraints
238
+ * }
239
+ */
240
+ crop: z.ZodBoolean;
241
+ }, z.core.$strict>>;
242
+ thumbnail: z.ZodOptional<z.ZodObject<{
243
+ width: z.ZodNumber;
244
+ height: z.ZodNumber;
245
+ crop: z.ZodBoolean;
246
+ quality: z.ZodOptional<z.ZodNumber>;
247
+ }, z.core.$strict>>;
248
+ /**
249
+ * Maximum file size
250
+ * @example
251
+ * maxFileSize: {
252
+ * size: 512,
253
+ * unit: 'kb',
254
+ * }
255
+ */
256
+ maxFileSize: z.ZodOptional<z.ZodObject<{
257
+ size: z.ZodNumber;
258
+ unit: z.ZodEnum<{
259
+ kb: "kb";
260
+ mb: "mb";
261
+ }>;
262
+ }, z.core.$strict>>;
263
+ /**
264
+ * Remove the extension from the file name
265
+ * @default true
266
+ */
267
+ removeExtension: z.ZodOptional<z.ZodBoolean>;
268
+ name: z.ZodString;
269
+ label: z.ZodOptional<z.ZodString>;
270
+ required: z.ZodOptional<z.ZodBoolean>;
271
+ defaultValue: z.ZodOptional<z.ZodAny>;
272
+ order: z.ZodOptional<z.ZodNumber>;
273
+ conditionalRules: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types").ConditionalRule, import("../types").ConditionalRule>>>;
274
+ adminGenerated: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodLiteral<false>, z.ZodLiteral<"readonly">]>>;
275
+ }, z.core.$strict>;
276
+ /**
277
+ * Photo field configuration type
278
+ * This is a plain object that can be serialized and used anywhere
279
+ * The build() method allows creating a PhotoField instance when needed
280
+ */
281
+ export type PhotoFieldConfig = z.infer<typeof photoFieldConfigSchema>;
282
+ /**
283
+ * Helper function to create a photo field configuration
284
+ * Returns a config object with a build() method that can be serialized and used anywhere
285
+ * @param field
286
+ */
287
+ export declare function photoField(field: z.infer<typeof optionsSchema>): PhotoFieldConfig;
288
+ export {};
289
+ //# sourceMappingURL=photo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"photo.d.ts","sourceRoot":"","sources":["../../../src/core/fields/photo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,410 @@
1
+ import { baseFieldConfigSchema } from './field';
2
+ import { entityKind } from '../helpers';
3
+ import * as z from 'zod';
4
+ import sharp from 'sharp';
5
+ import path from 'path';
6
+ import fs from 'fs';
7
+ import { customAlphabet } from 'nanoid';
8
+ import { FileField } from './fileField';
9
+ import { humanReadableFileSize } from '../../utils';
10
+ import { getCMSConfig } from '../config';
11
+ const cmsConfig = getCMSConfig();
12
+ const sizeSchema = z.strictObject({
13
+ width: z.number().describe('Image width in pixels'),
14
+ height: z.number().describe('Image height in pixels'),
15
+ /**
16
+ * @true The image will not have dimensions constraints,
17
+ * and will be cropped to fit the specified dimensions
18
+ *
19
+ * @false The user will be forced to upload an image with the specified dimensions
20
+ *
21
+ * @example
22
+ * size: {
23
+ * width: 1200,
24
+ * height: 450,
25
+ * crop: true, // No constraints
26
+ * }
27
+ */
28
+ crop: z.boolean().describe('Whether to crop the image to fit dimensions'),
29
+ });
30
+ const thumbnailSchema = z.strictObject({
31
+ width: z.number().describe('Thumbnail width in pixels'),
32
+ height: z.number().describe('Thumbnail height in pixels'),
33
+ crop: z.boolean().describe('Whether to crop the thumbnail'),
34
+ quality: z.number().optional().describe('Thumbnail quality (0-100)'),
35
+ });
36
+ const maxFileSizeSchema = z.strictObject({
37
+ size: z.number().describe('Maximum file size'),
38
+ unit: z.enum(['kb', 'mb']).describe('Size unit'),
39
+ });
40
+ const configSchema = z.strictObject({
41
+ /** Whether to add watermark to images */
42
+ watermark: z.boolean().nullable().optional(),
43
+ blurPlaceholder: z.boolean().nullable().optional(),
44
+ size: sizeSchema.optional(),
45
+ thumbnail: thumbnailSchema.optional(),
46
+ /**
47
+ * Maximum file size
48
+ * @example
49
+ * maxFileSize: {
50
+ * size: 512,
51
+ * unit: 'kb',
52
+ * }
53
+ */
54
+ maxFileSize: maxFileSizeSchema.optional(),
55
+ /**
56
+ * Allowed image types
57
+ * @example
58
+ * type: ['jpeg', 'png', 'webp']
59
+ * @default ['jpeg']
60
+ * @link https://sharp.pixelplumbing.com/api-output#toformat
61
+ * @hint 'jpg' is an alias for 'jpeg'
62
+ */
63
+ type: z.array(z.enum(['jpeg', 'jpg', 'png', 'webp'])).optional(),
64
+ /**
65
+ * Remove the extension from the file name
66
+ * @default true
67
+ */
68
+ removeExtension: z.boolean().optional(),
69
+ });
70
+ export class PhotoField extends FileField {
71
+ static [entityKind] = 'PhotoField';
72
+ watermark;
73
+ blurPlaceholder;
74
+ size;
75
+ maxFileSize;
76
+ mimeType;
77
+ extensions;
78
+ thumbnail;
79
+ removeExtension;
80
+ uploadsFolder = cmsConfig.files.upload.uploadPath;
81
+ /**
82
+ * _file is the file object if it's present
83
+ * Whereas the value is the path to the file
84
+ */
85
+ _file = undefined;
86
+ _sharpImage = undefined;
87
+ _folder;
88
+ _allowedExtensions;
89
+ constructor(config, file) {
90
+ super(config, 'photo');
91
+ if (file) {
92
+ this._file = file;
93
+ }
94
+ this.watermark = config.watermark;
95
+ this.blurPlaceholder = config.blurPlaceholder;
96
+ this.size = config.size;
97
+ this.maxFileSize = config.maxFileSize ?? { size: 2, unit: 'mb' };
98
+ this.thumbnail = config.thumbnail ?? cmsConfig.files.images.thumbnail;
99
+ this.removeExtension = config.removeExtension ?? true;
100
+ this.extensions = config.type ?? ['jpeg'];
101
+ /**
102
+ * Replace 'jpg' with 'jpeg'
103
+ */
104
+ this.extensions = this.extensions.map((e) => (e === 'jpg' ? 'jpeg' : e));
105
+ /**
106
+ * Extract the mime types from the extensions
107
+ */
108
+ this.mimeType = this.extensions.map((e) => {
109
+ if (e === 'jpeg')
110
+ return 'image/jpeg';
111
+ if (e === 'png')
112
+ return 'image/png';
113
+ if (e === 'webp')
114
+ return 'image/webp';
115
+ throw new Error(`Invalid image extension provided: ${e}`);
116
+ });
117
+ /**
118
+ * Set the allowed extensions, add jpg if jpeg is present
119
+ */
120
+ this._allowedExtensions = this.extensions;
121
+ if (this.extensions.includes('jpeg')) {
122
+ this._allowedExtensions.push('jpg');
123
+ }
124
+ }
125
+ exportForClient() {
126
+ return {
127
+ ...super.exportForClient(),
128
+ thumbnail: this.thumbnail,
129
+ // watermark: this.watermark,
130
+ // blurPlaceholder: this.blurPlaceholder,
131
+ size: this.size,
132
+ maxFileSize: this.maxFileSize,
133
+ extensions: this._allowedExtensions,
134
+ mimeType: this.mimeType,
135
+ };
136
+ }
137
+ /**
138
+ * Write the file to the disk
139
+ */
140
+ async writeToFile() {
141
+ if (!this._folder) {
142
+ throw new Error(`${this.label}: Folder is not set. Make sure to set the folder by calling postSubmit() before writing the file to disk`);
143
+ }
144
+ if (!this._sharpImage) {
145
+ throw new Error(`${this.label}: Image is not set. Make sure to call prepareForSubmission() before writing the file to disk`);
146
+ }
147
+ try {
148
+ /**
149
+ * If .photos, and 'sectionName' folders don't exist, create them
150
+ */
151
+ const photosFolder = path.join(this.uploadsFolder, '.photos', this._folder);
152
+ const thumbsFolder = path.join(this.uploadsFolder, '.thumbs', this._folder);
153
+ if (!fs.existsSync(photosFolder)) {
154
+ fs.mkdirSync(photosFolder, { recursive: true });
155
+ }
156
+ if (!fs.existsSync(thumbsFolder)) {
157
+ fs.mkdirSync(thumbsFolder, { recursive: true });
158
+ }
159
+ /**
160
+ * Check if the image needs to be resized and write the file to disk
161
+ */
162
+ if (this.size && this.size.crop) {
163
+ await this._sharpImage
164
+ .clone()
165
+ .resize({
166
+ width: this.size.width,
167
+ height: this.size.height,
168
+ fit: 'cover',
169
+ })
170
+ .webp()
171
+ .toFile(path.join(this.uploadsFolder, '.photos', this._folder, this.value));
172
+ }
173
+ else {
174
+ await this._sharpImage
175
+ .clone()
176
+ .toFile(path.join(this.uploadsFolder, '.photos', this._folder, this.value));
177
+ }
178
+ /**
179
+ * Also, write a thumbnail
180
+ */
181
+ await this._sharpImage
182
+ .clone()
183
+ .resize({
184
+ width: this.thumbnail.width,
185
+ height: this.thumbnail.height,
186
+ fit: this.thumbnail.crop ? 'cover' : 'contain',
187
+ })
188
+ .webp({
189
+ quality: this.thumbnail.quality || 80,
190
+ })
191
+ .toFile(path.join(this.uploadsFolder, '.thumbs', this._folder, this.value));
192
+ }
193
+ catch (error) {
194
+ throw new Error(`${this.label}: Error writing file to disk ${error.message}`);
195
+ }
196
+ }
197
+ async postSubmit(folder) {
198
+ if (!this._file)
199
+ return;
200
+ this._folder = folder;
201
+ await this.writeToFile();
202
+ }
203
+ async postSubmitRollback() {
204
+ if (!this._file)
205
+ return;
206
+ if (!this._folder) {
207
+ throw new Error(`${this.label}: Folder is not set. Make sure to set the folder before writing the file to disk`);
208
+ }
209
+ try {
210
+ const pathToFile = path.join(this.uploadsFolder, '.photos', this._folder, this.value);
211
+ await fs.promises.unlink(pathToFile);
212
+ }
213
+ catch (error) {
214
+ throw new Error(`${this.label}: Error deleting file from disk`);
215
+ }
216
+ }
217
+ /**
218
+ * Get the value of the field
219
+ */
220
+ getValue() {
221
+ return this.value;
222
+ }
223
+ setFileName(value) {
224
+ this.value = value;
225
+ }
226
+ setValue(value) {
227
+ if (typeof value === 'string') {
228
+ this.setFileName(value);
229
+ return;
230
+ }
231
+ this.setFile(value);
232
+ }
233
+ setFile(file) {
234
+ if (!file || file.size === 0 || file.name?.trim() === '')
235
+ return;
236
+ this._file = file;
237
+ }
238
+ checkRequired() {
239
+ /**
240
+ * Check if the field is required
241
+ * If it is, check if the file is present
242
+ * If it's not, throw an error
243
+ */
244
+ if (this.required) {
245
+ if (!this._file?.type || !this._file?.name || !this._file?.size) {
246
+ throw new Error(`Field ${this.label} is required`);
247
+ }
248
+ }
249
+ }
250
+ /**
251
+ * Prepare the field for submission
252
+ */
253
+ async prepareForSubmission() {
254
+ /**
255
+ * Check if the file is present
256
+ */
257
+ if (!this._file)
258
+ return;
259
+ /**
260
+ * Check extension
261
+ */
262
+ let ext = this._file.name.split('.').pop();
263
+ // Treat jpg as jpeg
264
+ if (ext === 'jpg')
265
+ ext = 'jpeg';
266
+ if (!ext || !this.extensions.includes(ext)) {
267
+ throw new Error(`${this.label}: Invalid file type or extension. Allowed extensions: ${this.extensions.join(', ')}`);
268
+ }
269
+ /**
270
+ * Construct the image
271
+ */
272
+ const arrayBuffer = await this._file.arrayBuffer();
273
+ const buffer = Buffer.from(arrayBuffer);
274
+ /**
275
+ * Check mime type
276
+ */
277
+ if (!this.mimeType.includes(this._file.type)) {
278
+ throw new Error(`${this.label}: Invalid file type or extension. Allowed extensions: ${this.extensions.join(', ')}`);
279
+ }
280
+ /**
281
+ * Check actual mime type
282
+ */
283
+ const { fileTypeFromBuffer } = await import('file-type');
284
+ const actualMimeType = await fileTypeFromBuffer(buffer);
285
+ if (!actualMimeType ||
286
+ !this.extensions.includes(actualMimeType.ext) ||
287
+ !this.mimeType.includes(actualMimeType.mime)) {
288
+ throw new Error(`${this.label}: Invalid file type or extension. Allowed extensions: ${this.extensions.join(', ')}`);
289
+ }
290
+ /**
291
+ * Disable caching for the image to avoid unlink issues
292
+ */
293
+ sharp.cache({ files: 0 });
294
+ sharp.cache(false);
295
+ const image = sharp(buffer);
296
+ /**
297
+ * Get the metadata
298
+ * Fast access to (uncached) image metadata without decoding any compressed pixel data.
299
+ * This is read from the header of the input image.
300
+ * It does not take into consideration any operations to be applied to the output image, such as resize or rotate.
301
+ * @link: https://sharp.pixelplumbing.com/api-input#metadata
302
+ */
303
+ const metadata = await image.metadata();
304
+ /**
305
+ * Check the actual file size (buffer size)
306
+ */
307
+ const fileSize = buffer.length;
308
+ if (!fileSize || !metadata.size) {
309
+ throw new Error(`Field ${this.label} is required`);
310
+ }
311
+ /**
312
+ * Check the file size
313
+ */
314
+ if (fileSize > this.maxFileSize.size * (this.maxFileSize.unit === 'kb' ? 1024 : 1024 * 1024)) {
315
+ throw new Error(`${this.label}: File size (${humanReadableFileSize(fileSize)}) exceeds the maximum allowed size of ${this.maxFileSize.size} ${this.maxFileSize.unit}`);
316
+ }
317
+ /**
318
+ * Don't just trust the file extension
319
+ * Check the format
320
+ */
321
+ if (!metadata.format || !this.extensions.includes(metadata.format)) {
322
+ throw new Error(`${this.label}: Invalid file type or extension. Allowed extensions: ${this.extensions.join(', ')}`);
323
+ }
324
+ /**
325
+ * Check stat
326
+ */
327
+ try {
328
+ await image.stats();
329
+ }
330
+ catch (error) {
331
+ throw new Error(`File is corrupted`);
332
+ }
333
+ /**
334
+ * Convert the image to webp
335
+ */
336
+ try {
337
+ image.toFormat('webp').withExif({});
338
+ }
339
+ catch (error) {
340
+ throw new Error(`File is corrupted`);
341
+ }
342
+ /**
343
+ * Check the size
344
+ */
345
+ if (this.size) {
346
+ /**
347
+ * Check if the image does not need to be cropped (dimensions are constrained)
348
+ */
349
+ if (!this.size.crop) {
350
+ /**
351
+ * Just resize the image?
352
+ */
353
+ /**
354
+ * Check if the size matches the required size
355
+ */
356
+ if (metadata.width !== this.size.width || metadata.height !== this.size.height) {
357
+ throw new Error(`${this.label}: Uploaded image size (${metadata.width}x${metadata.height} pixels) does not match the required size: ${this.size.width}x${this.size.height} pixels`);
358
+ }
359
+ }
360
+ }
361
+ /**
362
+ * Generate a random name for the file
363
+ */
364
+ this.value = customAlphabet('1234567890abcdef', 21)();
365
+ this.value = this.removeExtension ? this.value : this.value + '.webp';
366
+ /*if (saveFilesWithExtensions) {
367
+ /!**
368
+ * Currently we're forcing webp output.
369
+ * I've to add the ability for developers to choose the output format
370
+ *!/
371
+ this.value += '.webp'
372
+ }*/
373
+ /**
374
+ * Set the sharp image
375
+ */
376
+ this._sharpImage = image;
377
+ }
378
+ }
379
+ const optionsSchema = z.strictObject({
380
+ ...baseFieldConfigSchema.shape,
381
+ ...configSchema.shape,
382
+ });
383
+ const photoFieldConfigSchema = z.strictObject({
384
+ ...optionsSchema.shape,
385
+ type: z.literal('photo').describe('The type of the field'),
386
+ build: z.function().output(z.instanceof(PhotoField)).describe('Build a PhotoField instance from this config'),
387
+ });
388
+ /**
389
+ * Helper function to create a photo field configuration
390
+ * Returns a config object with a build() method that can be serialized and used anywhere
391
+ * @param field
392
+ */
393
+ export function photoField(field) {
394
+ /**
395
+ * Validate the field config
396
+ */
397
+ const result = optionsSchema.safeParse(field);
398
+ if (!result.success) {
399
+ throw new Error(`[Field: ${field.name}]: ${z.prettifyError(result.error)}`);
400
+ }
401
+ const config = {
402
+ ...field,
403
+ type: 'photo',
404
+ build() {
405
+ // Use the original field config directly (it doesn't have build() method)
406
+ return new PhotoField(field);
407
+ },
408
+ };
409
+ return config;
410
+ }