nextjs-cms 0.0.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +289 -0
- package/dist/api/axios/axiosInstance.d.ts +2 -0
- package/dist/api/axios/axiosInstance.d.ts.map +1 -0
- package/dist/api/axios/axiosInstance.js +8 -0
- package/dist/api/index.d.ts +856 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +12 -0
- package/dist/api/lib/serverActions.d.ts +240 -0
- package/dist/api/lib/serverActions.d.ts.map +1 -0
- package/dist/api/lib/serverActions.js +834 -0
- package/dist/api/root.d.ts +829 -0
- package/dist/api/root.d.ts.map +1 -0
- package/dist/api/root.js +30 -0
- package/dist/api/routers/accountSettings.d.ts +61 -0
- package/dist/api/routers/accountSettings.d.ts.map +1 -0
- package/dist/api/routers/accountSettings.js +108 -0
- package/dist/api/routers/admins.d.ts +106 -0
- package/dist/api/routers/admins.d.ts.map +1 -0
- package/dist/api/routers/admins.js +219 -0
- package/dist/api/routers/auth.d.ts +48 -0
- package/dist/api/routers/auth.d.ts.map +1 -0
- package/dist/api/routers/auth.js +25 -0
- package/dist/api/routers/categorySection.d.ts +104 -0
- package/dist/api/routers/categorySection.d.ts.map +1 -0
- package/dist/api/routers/categorySection.js +38 -0
- package/dist/api/routers/cmsSettings.d.ts +49 -0
- package/dist/api/routers/cmsSettings.d.ts.map +1 -0
- package/dist/api/routers/cmsSettings.js +51 -0
- package/dist/api/routers/cpanel.d.ts +84 -0
- package/dist/api/routers/cpanel.d.ts.map +1 -0
- package/dist/api/routers/cpanel.js +216 -0
- package/dist/api/routers/files.d.ts +48 -0
- package/dist/api/routers/files.d.ts.map +1 -0
- package/dist/api/routers/files.js +23 -0
- package/dist/api/routers/gallery.d.ts +36 -0
- package/dist/api/routers/gallery.d.ts.map +1 -0
- package/dist/api/routers/gallery.js +62 -0
- package/dist/api/routers/googleAnalytics.d.ts +31 -0
- package/dist/api/routers/googleAnalytics.d.ts.map +1 -0
- package/dist/api/routers/googleAnalytics.js +7 -0
- package/dist/api/routers/hasItemsSection.d.ts +140 -0
- package/dist/api/routers/hasItemsSection.d.ts.map +1 -0
- package/dist/api/routers/hasItemsSection.js +34 -0
- package/dist/api/routers/navigation.d.ts +52 -0
- package/dist/api/routers/navigation.d.ts.map +1 -0
- package/dist/api/routers/navigation.js +11 -0
- package/dist/api/routers/simpleSection.d.ts +58 -0
- package/dist/api/routers/simpleSection.d.ts.map +1 -0
- package/dist/api/routers/simpleSection.js +12 -0
- package/dist/api/trpc.d.ts +107 -0
- package/dist/api/trpc.d.ts.map +1 -0
- package/dist/api/trpc.js +72 -0
- package/dist/auth/axios/axiosInstance.d.ts +2 -0
- package/dist/auth/axios/axiosInstance.d.ts.map +1 -0
- package/dist/auth/axios/axiosInstance.js +8 -0
- package/dist/auth/csrf.d.ts +30 -0
- package/dist/auth/csrf.d.ts.map +1 -0
- package/dist/auth/csrf.js +76 -0
- package/dist/auth/hooks/index.d.ts +4 -0
- package/dist/auth/hooks/index.d.ts.map +1 -0
- package/dist/auth/hooks/index.js +3 -0
- package/dist/auth/hooks/useAxiosPrivate.d.ts +5 -0
- package/dist/auth/hooks/useAxiosPrivate.d.ts.map +1 -0
- package/dist/auth/hooks/useAxiosPrivate.js +74 -0
- package/dist/auth/hooks/useRefreshToken.d.ts +7 -0
- package/dist/auth/hooks/useRefreshToken.d.ts.map +1 -0
- package/dist/auth/hooks/useRefreshToken.js +79 -0
- package/dist/auth/index.d.ts +23 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +44 -0
- package/dist/auth/jwt.d.ts +6 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +25 -0
- package/dist/auth/lib/actions.d.ts +33 -0
- package/dist/auth/lib/actions.d.ts.map +1 -0
- package/dist/auth/lib/actions.js +209 -0
- package/dist/auth/lib/client.d.ts +4 -0
- package/dist/auth/lib/client.d.ts.map +1 -0
- package/dist/auth/lib/client.js +46 -0
- package/dist/auth/lib/index.d.ts +3 -0
- package/dist/auth/lib/index.d.ts.map +1 -0
- package/dist/auth/lib/index.js +2 -0
- package/dist/auth/react.d.ts +106 -0
- package/dist/auth/react.d.ts.map +1 -0
- package/dist/auth/react.js +347 -0
- package/dist/auth/trpc.d.ts +6 -0
- package/dist/auth/trpc.d.ts.map +1 -0
- package/dist/auth/trpc.js +81 -0
- package/dist/core/config/config-loader.d.ts +92 -0
- package/dist/core/config/config-loader.d.ts.map +1 -0
- package/dist/core/config/config-loader.js +230 -0
- package/dist/core/config/index.d.ts +3 -0
- package/dist/core/config/index.d.ts.map +1 -0
- package/dist/core/config/index.js +1 -0
- package/dist/core/config/loader.d.ts +2 -0
- package/dist/core/config/loader.d.ts.map +1 -0
- package/dist/core/config/loader.js +42 -0
- package/dist/core/db/index.d.ts +2 -0
- package/dist/core/db/index.d.ts.map +1 -0
- package/dist/core/db/index.js +1 -0
- package/dist/core/db/table-checker/DbTable.d.ts +6 -0
- package/dist/core/db/table-checker/DbTable.d.ts.map +1 -0
- package/dist/core/db/table-checker/DbTable.js +5 -0
- package/dist/core/db/table-checker/MysqlTable.d.ts +34 -0
- package/dist/core/db/table-checker/MysqlTable.d.ts.map +1 -0
- package/dist/core/db/table-checker/MysqlTable.js +102 -0
- package/dist/core/db/table-checker/index.d.ts +2 -0
- package/dist/core/db/table-checker/index.d.ts.map +1 -0
- package/dist/core/db/table-checker/index.js +1 -0
- package/dist/core/factories/FieldFactory.d.ts +124 -0
- package/dist/core/factories/FieldFactory.d.ts.map +1 -0
- package/dist/core/factories/FieldFactory.js +411 -0
- package/dist/core/factories/SectionFactory.d.ts +110 -0
- package/dist/core/factories/SectionFactory.d.ts.map +1 -0
- package/dist/core/factories/SectionFactory.js +415 -0
- package/dist/core/factories/index.d.ts +3 -0
- package/dist/core/factories/index.d.ts.map +1 -0
- package/dist/core/factories/index.js +2 -0
- package/dist/core/fields/checkbox.d.ts +63 -0
- package/dist/core/fields/checkbox.d.ts.map +1 -0
- package/dist/core/fields/checkbox.js +62 -0
- package/dist/core/fields/color.d.ts +84 -0
- package/dist/core/fields/color.d.ts.map +1 -0
- package/dist/core/fields/color.js +91 -0
- package/dist/core/fields/date.d.ts +100 -0
- package/dist/core/fields/date.d.ts.map +1 -0
- package/dist/core/fields/date.js +108 -0
- package/dist/core/fields/document.d.ts +180 -0
- package/dist/core/fields/document.d.ts.map +1 -0
- package/dist/core/fields/document.js +277 -0
- package/dist/core/fields/field-group.d.ts +18 -0
- package/dist/core/fields/field-group.d.ts.map +1 -0
- package/dist/core/fields/field-group.js +6 -0
- package/dist/core/fields/field.d.ts +126 -0
- package/dist/core/fields/field.d.ts.map +1 -0
- package/dist/core/fields/field.js +148 -0
- package/dist/core/fields/fileField.d.ts +15 -0
- package/dist/core/fields/fileField.d.ts.map +1 -0
- package/dist/core/fields/fileField.js +5 -0
- package/dist/core/fields/index.d.ts +65 -0
- package/dist/core/fields/index.d.ts.map +1 -0
- package/dist/core/fields/index.js +18 -0
- package/dist/core/fields/map.d.ts +167 -0
- package/dist/core/fields/map.d.ts.map +1 -0
- package/dist/core/fields/map.js +152 -0
- package/dist/core/fields/number.d.ts +186 -0
- package/dist/core/fields/number.d.ts.map +1 -0
- package/dist/core/fields/number.js +241 -0
- package/dist/core/fields/password.d.ts +109 -0
- package/dist/core/fields/password.d.ts.map +1 -0
- package/dist/core/fields/password.js +133 -0
- package/dist/core/fields/photo.d.ts +289 -0
- package/dist/core/fields/photo.d.ts.map +1 -0
- package/dist/core/fields/photo.js +410 -0
- package/dist/core/fields/richText.d.ts +295 -0
- package/dist/core/fields/richText.d.ts.map +1 -0
- package/dist/core/fields/richText.js +338 -0
- package/dist/core/fields/select.d.ts +366 -0
- package/dist/core/fields/select.d.ts.map +1 -0
- package/dist/core/fields/select.js +499 -0
- package/dist/core/fields/selectMultiple.d.ts +236 -0
- package/dist/core/fields/selectMultiple.d.ts.map +1 -0
- package/dist/core/fields/selectMultiple.js +417 -0
- package/dist/core/fields/tags.d.ts +131 -0
- package/dist/core/fields/tags.d.ts.map +1 -0
- package/dist/core/fields/tags.js +105 -0
- package/dist/core/fields/text.d.ts +136 -0
- package/dist/core/fields/text.d.ts.map +1 -0
- package/dist/core/fields/text.js +157 -0
- package/dist/core/fields/textArea.d.ts +107 -0
- package/dist/core/fields/textArea.d.ts.map +1 -0
- package/dist/core/fields/textArea.js +126 -0
- package/dist/core/fields/video.d.ts +148 -0
- package/dist/core/fields/video.d.ts.map +1 -0
- package/dist/core/fields/video.js +248 -0
- package/dist/core/helpers/entity.d.ts +8 -0
- package/dist/core/helpers/entity.d.ts.map +1 -0
- package/dist/core/helpers/entity.js +27 -0
- package/dist/core/helpers/index.d.ts +5 -0
- package/dist/core/helpers/index.d.ts.map +1 -0
- package/dist/core/helpers/index.js +3 -0
- package/dist/core/index.d.ts +8 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +7 -0
- package/dist/core/sections/category.d.ts +283 -0
- package/dist/core/sections/category.d.ts.map +1 -0
- package/dist/core/sections/category.js +147 -0
- package/dist/core/sections/hasItems.d.ts +632 -0
- package/dist/core/sections/hasItems.d.ts.map +1 -0
- package/dist/core/sections/hasItems.js +144 -0
- package/dist/core/sections/index.d.ts +5 -0
- package/dist/core/sections/index.d.ts.map +1 -0
- package/dist/core/sections/index.js +4 -0
- package/dist/core/sections/section.d.ts +226 -0
- package/dist/core/sections/section.d.ts.map +1 -0
- package/dist/core/sections/section.js +341 -0
- package/dist/core/sections/simple.d.ts +99 -0
- package/dist/core/sections/simple.d.ts.map +1 -0
- package/dist/core/sections/simple.js +95 -0
- package/dist/core/security/dom.d.ts +11 -0
- package/dist/core/security/dom.d.ts.map +1 -0
- package/dist/core/security/dom.js +92 -0
- package/dist/core/submit/ItemEditSubmit.d.ts +76 -0
- package/dist/core/submit/ItemEditSubmit.d.ts.map +1 -0
- package/dist/core/submit/ItemEditSubmit.js +186 -0
- package/dist/core/submit/NewItemSubmit.d.ts +14 -0
- package/dist/core/submit/NewItemSubmit.d.ts.map +1 -0
- package/dist/core/submit/NewItemSubmit.js +93 -0
- package/dist/core/submit/SimpleSectionSubmit.d.ts +13 -0
- package/dist/core/submit/SimpleSectionSubmit.d.ts.map +1 -0
- package/dist/core/submit/SimpleSectionSubmit.js +93 -0
- package/dist/core/submit/index.d.ts +5 -0
- package/dist/core/submit/index.d.ts.map +1 -0
- package/dist/core/submit/index.js +4 -0
- package/dist/core/submit/submit.d.ts +116 -0
- package/dist/core/submit/submit.d.ts.map +1 -0
- package/dist/core/submit/submit.js +479 -0
- package/dist/core/types/index.d.ts +280 -0
- package/dist/core/types/index.d.ts.map +1 -0
- package/dist/core/types/index.js +1 -0
- package/dist/db/client.d.ts +9 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +19 -0
- package/dist/db/config.d.ts +6 -0
- package/dist/db/config.d.ts.map +1 -0
- package/dist/db/config.js +22 -0
- package/dist/db/drizzle.config.d.ts +6 -0
- package/dist/db/drizzle.config.d.ts.map +1 -0
- package/dist/db/drizzle.config.js +18 -0
- package/dist/db/index.d.ts +3 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +3 -0
- package/dist/db/schema.d.ts +639 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +73 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/translations/dictionaries/ar.json +279 -0
- package/dist/translations/dictionaries/en.json +279 -0
- package/dist/translations/index.d.ts +3 -0
- package/dist/translations/index.d.ts.map +1 -0
- package/dist/translations/index.js +15 -0
- package/dist/utils/CpanelApi.d.ts +25 -0
- package/dist/utils/CpanelApi.d.ts.map +1 -0
- package/dist/utils/CpanelApi.js +64 -0
- package/dist/utils/constants.d.ts +14 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +61 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +4 -0
- package/dist/utils/utils.d.ts +60 -0
- package/dist/utils/utils.d.ts.map +1 -0
- package/dist/utils/utils.js +132 -0
- package/dist/validators/checkbox.d.ts +4 -0
- package/dist/validators/checkbox.d.ts.map +1 -0
- package/dist/validators/checkbox.js +12 -0
- package/dist/validators/color.d.ts +4 -0
- package/dist/validators/color.d.ts.map +1 -0
- package/dist/validators/color.js +7 -0
- package/dist/validators/date.d.ts +4 -0
- package/dist/validators/date.d.ts.map +1 -0
- package/dist/validators/date.js +5 -0
- package/dist/validators/document.d.ts +4 -0
- package/dist/validators/document.d.ts.map +1 -0
- package/dist/validators/document.js +57 -0
- package/dist/validators/index.d.ts +15 -0
- package/dist/validators/index.d.ts.map +1 -0
- package/dist/validators/index.js +14 -0
- package/dist/validators/map.d.ts +4 -0
- package/dist/validators/map.d.ts.map +1 -0
- package/dist/validators/map.js +5 -0
- package/dist/validators/number.d.ts +4 -0
- package/dist/validators/number.d.ts.map +1 -0
- package/dist/validators/number.js +20 -0
- package/dist/validators/password.d.ts +4 -0
- package/dist/validators/password.d.ts.map +1 -0
- package/dist/validators/password.js +11 -0
- package/dist/validators/photo.d.ts +4 -0
- package/dist/validators/photo.d.ts.map +1 -0
- package/dist/validators/photo.js +100 -0
- package/dist/validators/richText.d.ts +4 -0
- package/dist/validators/richText.d.ts.map +1 -0
- package/dist/validators/richText.js +8 -0
- package/dist/validators/select-multiple.d.ts +10 -0
- package/dist/validators/select-multiple.d.ts.map +1 -0
- package/dist/validators/select-multiple.js +20 -0
- package/dist/validators/select.d.ts +4 -0
- package/dist/validators/select.d.ts.map +1 -0
- package/dist/validators/select.js +5 -0
- package/dist/validators/text.d.ts +4 -0
- package/dist/validators/text.d.ts.map +1 -0
- package/dist/validators/text.js +7 -0
- package/dist/validators/textarea.d.ts +4 -0
- package/dist/validators/textarea.d.ts.map +1 -0
- package/dist/validators/textarea.js +7 -0
- package/dist/validators/video.d.ts +4 -0
- package/dist/validators/video.d.ts.map +1 -0
- package/dist/validators/video.js +57 -0
- package/package.json +150 -6
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { entityKind } from '../helpers';
|
|
2
|
+
import { numberField } from '../fields';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import * as z from 'zod';
|
|
5
|
+
import { getCMSConfig } from '../config/config-loader';
|
|
6
|
+
const cmsConfig = getCMSConfig();
|
|
7
|
+
export const fieldConfigSchema = z.custom();
|
|
8
|
+
export const FieldGroupConfigSchema = z.custom();
|
|
9
|
+
const variantSchema = z.custom();
|
|
10
|
+
const hooksSchema = z.custom();
|
|
11
|
+
export const baseSectionOptionsSchema = z.strictObject({
|
|
12
|
+
name: z.string().describe('The name of the section'),
|
|
13
|
+
order: z.number(),
|
|
14
|
+
icon: z.string().optional(),
|
|
15
|
+
readonly: z.boolean().default(false).optional(),
|
|
16
|
+
gallery: z
|
|
17
|
+
.strictObject({
|
|
18
|
+
db: z.strictObject({
|
|
19
|
+
tableName: z.string(),
|
|
20
|
+
identifierField: z.string().optional(),
|
|
21
|
+
photoNameField: z.string().optional(),
|
|
22
|
+
metaField: z.string().optional(),
|
|
23
|
+
}),
|
|
24
|
+
watermark: z.boolean().optional(),
|
|
25
|
+
thumbnail: z
|
|
26
|
+
.strictObject({
|
|
27
|
+
width: z.number(),
|
|
28
|
+
height: z.number(),
|
|
29
|
+
crop: z.boolean(),
|
|
30
|
+
quality: z.number(),
|
|
31
|
+
})
|
|
32
|
+
.optional(),
|
|
33
|
+
})
|
|
34
|
+
.optional(),
|
|
35
|
+
db: z.strictObject({
|
|
36
|
+
table: z.string(),
|
|
37
|
+
identifier: fieldConfigSchema.optional(),
|
|
38
|
+
orderByField: fieldConfigSchema.optional(),
|
|
39
|
+
primaryKey: z.array(fieldConfigSchema).optional(),
|
|
40
|
+
foreignKeys: z
|
|
41
|
+
.array(z.strictObject({
|
|
42
|
+
columns: z.array(fieldConfigSchema),
|
|
43
|
+
foreignColumns: z.array(fieldConfigSchema),
|
|
44
|
+
name: z.string(),
|
|
45
|
+
}))
|
|
46
|
+
.optional(),
|
|
47
|
+
index: z
|
|
48
|
+
.array(z.strictObject({
|
|
49
|
+
columns: z.array(fieldConfigSchema),
|
|
50
|
+
name: z.string().optional(),
|
|
51
|
+
}))
|
|
52
|
+
.optional(),
|
|
53
|
+
unique: z
|
|
54
|
+
.array(z.strictObject({
|
|
55
|
+
columns: z.array(fieldConfigSchema),
|
|
56
|
+
name: z.string().optional(),
|
|
57
|
+
}))
|
|
58
|
+
.optional(),
|
|
59
|
+
fulltext: z
|
|
60
|
+
.array(z.strictObject({
|
|
61
|
+
columns: z.array(fieldConfigSchema),
|
|
62
|
+
name: z.string().optional(),
|
|
63
|
+
}))
|
|
64
|
+
.optional(),
|
|
65
|
+
}),
|
|
66
|
+
fields: z.array(fieldConfigSchema),
|
|
67
|
+
fieldGroups: z.array(FieldGroupConfigSchema).optional(),
|
|
68
|
+
variants: z.array(variantSchema).optional(),
|
|
69
|
+
hooks: hooksSchema.optional(),
|
|
70
|
+
});
|
|
71
|
+
/**
|
|
72
|
+
* This is the options schema for the helper functions that create section configs
|
|
73
|
+
* We omit the fields and fieldGroups properties from the baseSectionOptionsSchema
|
|
74
|
+
* and change the fields to be an array of field configs or field group configs
|
|
75
|
+
*/
|
|
76
|
+
export const baseHelperFunctionOptionsSchema = z.strictObject({
|
|
77
|
+
...baseSectionOptionsSchema.omit({ fields: true, fieldGroups: true }).shape,
|
|
78
|
+
fields: z
|
|
79
|
+
.array(fieldConfigSchema)
|
|
80
|
+
.min(1)
|
|
81
|
+
.describe('The fields of the section')
|
|
82
|
+
.or(z
|
|
83
|
+
.array(FieldGroupConfigSchema)
|
|
84
|
+
.min(1)
|
|
85
|
+
.describe('The field groups of the section')
|
|
86
|
+
.describe('The fields or field groups of the section')),
|
|
87
|
+
});
|
|
88
|
+
export function validateSectionConfig(config) {
|
|
89
|
+
const result = baseSectionOptionsSchema.safeParse(config);
|
|
90
|
+
if (!result.success) {
|
|
91
|
+
throw new Error(result.error.message);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export class Section {
|
|
95
|
+
static [entityKind] = 'Section';
|
|
96
|
+
name;
|
|
97
|
+
order;
|
|
98
|
+
icon;
|
|
99
|
+
readonly = false;
|
|
100
|
+
gallery;
|
|
101
|
+
_fields = undefined;
|
|
102
|
+
_fieldGroupConfigs = undefined;
|
|
103
|
+
_hasFieldGroups;
|
|
104
|
+
_fieldGroups = undefined;
|
|
105
|
+
_fieldConfigs;
|
|
106
|
+
/**
|
|
107
|
+
* A helper property to access the field configs.
|
|
108
|
+
* Sometimes it's handy to access the field configs without building the actual fields array.
|
|
109
|
+
*/
|
|
110
|
+
get fieldConfigs() {
|
|
111
|
+
return this._fieldConfigs ?? [];
|
|
112
|
+
}
|
|
113
|
+
get fieldGroups() {
|
|
114
|
+
return this._fieldGroups ?? [];
|
|
115
|
+
}
|
|
116
|
+
get fields() {
|
|
117
|
+
if (this._fields === undefined) {
|
|
118
|
+
const errorMessage = `Fields are not yer built for section "${this.name}". Please run buildFields() before accessing the fields property.`;
|
|
119
|
+
const reportMessage = 'If this is a system bug, please open a bug report on GitHub.';
|
|
120
|
+
console.error('\n');
|
|
121
|
+
console.error(chalk.bold.red('[Section Error]', errorMessage));
|
|
122
|
+
console.error(chalk.bold.bgRed(reportMessage));
|
|
123
|
+
console.error('\n');
|
|
124
|
+
throw new Error(errorMessage);
|
|
125
|
+
}
|
|
126
|
+
return this._fields;
|
|
127
|
+
}
|
|
128
|
+
set fields(value) {
|
|
129
|
+
this._fields = value;
|
|
130
|
+
}
|
|
131
|
+
db;
|
|
132
|
+
variants;
|
|
133
|
+
hooks;
|
|
134
|
+
constructor(config) {
|
|
135
|
+
// this.id = config.id
|
|
136
|
+
this.name = config.name;
|
|
137
|
+
this.order = config.order;
|
|
138
|
+
this.icon = config.icon;
|
|
139
|
+
this.readonly = config.readonly ?? false;
|
|
140
|
+
/**
|
|
141
|
+
* Resolve gallery config
|
|
142
|
+
*/
|
|
143
|
+
if (config.gallery) {
|
|
144
|
+
this.gallery = {
|
|
145
|
+
db: {
|
|
146
|
+
tableName: config.gallery.db.tableName,
|
|
147
|
+
referenceIdentifierField: config.gallery.db.identifierField ?? 'reference_id',
|
|
148
|
+
photoNameField: config.gallery.db.photoNameField ?? 'photo',
|
|
149
|
+
metaField: config.gallery.db.metaField ?? 'meta',
|
|
150
|
+
},
|
|
151
|
+
watermark: config.gallery.watermark ?? cmsConfig.files.images.watermark,
|
|
152
|
+
thumbnail: config.gallery.thumbnail ?? cmsConfig.files.images.thumbnail,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Check for db identifier field config
|
|
157
|
+
*/
|
|
158
|
+
if (!config.db.identifier) {
|
|
159
|
+
// Create default id field config
|
|
160
|
+
const idFieldConfig = numberField({
|
|
161
|
+
name: 'id',
|
|
162
|
+
label: 'ID',
|
|
163
|
+
required: true,
|
|
164
|
+
hasAutoIncrement: true,
|
|
165
|
+
order: 0,
|
|
166
|
+
});
|
|
167
|
+
config.db.identifier = idFieldConfig;
|
|
168
|
+
config.fields.unshift(idFieldConfig);
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
/**
|
|
172
|
+
* Make sure the identifier field config is a number or text field config
|
|
173
|
+
*/
|
|
174
|
+
if (config.db.identifier.type !== 'number' && config.db.identifier.type !== 'text') {
|
|
175
|
+
const message = `[Section: ${this.name}]: DB identifier field config must be a number or text field. Got type: ${config.db.identifier.type}`;
|
|
176
|
+
console.error(chalk.red.bold(message));
|
|
177
|
+
console.error(chalk.yellow('Please make sure the identifier field config is a number or text field.'));
|
|
178
|
+
throw new Error(message);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Resolve db configuration fields
|
|
183
|
+
* If user doesn't specify primaryKey, default to identifier field
|
|
184
|
+
*/
|
|
185
|
+
this.db = {
|
|
186
|
+
table: config.db.table,
|
|
187
|
+
identifier: config.db.identifier,
|
|
188
|
+
orderByField: config.db.orderByField,
|
|
189
|
+
primaryKey: config.db.primaryKey ?? [config.db.identifier],
|
|
190
|
+
foreignKeys: config.db.foreignKeys,
|
|
191
|
+
fulltext: config.db.fulltext,
|
|
192
|
+
index: config.db.index,
|
|
193
|
+
unique: config.db.unique ?? [],
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Add the identifier field to the unique array if it's not already there
|
|
197
|
+
*/
|
|
198
|
+
if (!this.db.unique.some((unique) => unique.columns.includes(this.db.identifier))) {
|
|
199
|
+
/**
|
|
200
|
+
* Add the identifier field as the first item in the unique array
|
|
201
|
+
*/
|
|
202
|
+
this.db.unique.unshift({ columns: [this.db.identifier] });
|
|
203
|
+
}
|
|
204
|
+
this._fieldConfigs = config.fields;
|
|
205
|
+
this._fieldGroupConfigs = config.fieldGroups;
|
|
206
|
+
// Cache the hasFieldGroups check once during construction (immutable after this point)
|
|
207
|
+
this._hasFieldGroups = (this._fieldGroupConfigs?.length ?? 0) > 0;
|
|
208
|
+
this.variants = config.variants;
|
|
209
|
+
this.hooks = config.hooks;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Returns whether this section has field groups.
|
|
213
|
+
* Value is cached during construction for optimal performance.
|
|
214
|
+
*/
|
|
215
|
+
get hasFieldGroups() {
|
|
216
|
+
return this._hasFieldGroups;
|
|
217
|
+
}
|
|
218
|
+
buildFieldGroups() {
|
|
219
|
+
const configs = this._fieldGroupConfigs;
|
|
220
|
+
if (!configs?.length)
|
|
221
|
+
return;
|
|
222
|
+
const fieldGroups = [];
|
|
223
|
+
const fields = [];
|
|
224
|
+
configs.forEach((groupConfig, index) => {
|
|
225
|
+
const fields = groupConfig.fields.map((fieldConfig) => this.resolveField(fieldConfig));
|
|
226
|
+
fieldGroups.push({
|
|
227
|
+
id: index + 1,
|
|
228
|
+
title: groupConfig.title,
|
|
229
|
+
order: groupConfig.order,
|
|
230
|
+
fields,
|
|
231
|
+
});
|
|
232
|
+
fields.push(...fields);
|
|
233
|
+
});
|
|
234
|
+
this._fieldGroups = fieldGroups;
|
|
235
|
+
this._fields = fields;
|
|
236
|
+
}
|
|
237
|
+
buildFields() {
|
|
238
|
+
if (this.hasFieldGroups) {
|
|
239
|
+
this.buildFieldGroups();
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (this._fieldConfigs === undefined) {
|
|
243
|
+
throw new Error(`This section: (${this.name}) has no fields. Please set at least one field.`);
|
|
244
|
+
}
|
|
245
|
+
this._fields = this._fieldConfigs.map((input) => this.resolveField(input));
|
|
246
|
+
/**
|
|
247
|
+
* Create one group to hold all the fields
|
|
248
|
+
*/
|
|
249
|
+
this._fieldGroups = [
|
|
250
|
+
{
|
|
251
|
+
id: 1,
|
|
252
|
+
title: '',
|
|
253
|
+
order: 1,
|
|
254
|
+
fields: this._fields,
|
|
255
|
+
},
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Helper function to resolve a field config to an instance
|
|
260
|
+
* by calling the build() method on the config
|
|
261
|
+
* @param config - The field config to resolve
|
|
262
|
+
* @returns The field instance
|
|
263
|
+
* @throws An error if the field config does not have a build() method
|
|
264
|
+
*/
|
|
265
|
+
resolveField(config) {
|
|
266
|
+
// Config must have a build() method
|
|
267
|
+
if (typeof config.build === 'function') {
|
|
268
|
+
return config.build();
|
|
269
|
+
}
|
|
270
|
+
throw new Error(`[Section: ${this.name}]: Field "${config.name}" config must have a build() method. Use helper functions like textField(), numberField(), etc. to create field configs.`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Helper function to ensure a section config has a db identifier field
|
|
275
|
+
* If db.identifier is missing, creates a default 'id' number field config
|
|
276
|
+
* and prepends it to fields and sets it as db.identifier
|
|
277
|
+
*
|
|
278
|
+
* @param section - Section config that may or may not have db.identifier
|
|
279
|
+
* @returns Section config with db.identifier guaranteed to exist
|
|
280
|
+
*/
|
|
281
|
+
export function ensureSectionDbIdentifier(section) {
|
|
282
|
+
// If identifier already exists, return as-is
|
|
283
|
+
if (section.db.identifier) {
|
|
284
|
+
return section;
|
|
285
|
+
}
|
|
286
|
+
// Create default id field config
|
|
287
|
+
const idFieldConfig = numberField({
|
|
288
|
+
name: 'id',
|
|
289
|
+
label: 'ID',
|
|
290
|
+
required: true,
|
|
291
|
+
hasAutoIncrement: true,
|
|
292
|
+
order: 0,
|
|
293
|
+
});
|
|
294
|
+
// Return modified config with identifier
|
|
295
|
+
return {
|
|
296
|
+
...section,
|
|
297
|
+
fields: [idFieldConfig, ...section.fields],
|
|
298
|
+
db: {
|
|
299
|
+
...section.db,
|
|
300
|
+
identifier: idFieldConfig,
|
|
301
|
+
},
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Helper function to normalize gallery config with default values
|
|
306
|
+
* Sets defaults for identifierField, photoNameField, and metaField if not provided
|
|
307
|
+
*
|
|
308
|
+
* @param section - Section config that may or may not have gallery config
|
|
309
|
+
* @returns Section config with normalized gallery config
|
|
310
|
+
*/
|
|
311
|
+
export function ensureSectionGallery(section) {
|
|
312
|
+
// If no gallery config, return as-is
|
|
313
|
+
if (!section.gallery) {
|
|
314
|
+
return section;
|
|
315
|
+
}
|
|
316
|
+
// Return config with normalized gallery
|
|
317
|
+
return {
|
|
318
|
+
...section,
|
|
319
|
+
gallery: {
|
|
320
|
+
...section.gallery,
|
|
321
|
+
db: {
|
|
322
|
+
tableName: section.gallery.db.tableName,
|
|
323
|
+
identifierField: section.gallery.db.identifierField ?? 'reference_id',
|
|
324
|
+
photoNameField: section.gallery.db.photoNameField ?? 'photo',
|
|
325
|
+
metaField: section.gallery.db.metaField ?? 'meta',
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
export function resolveFieldsAndFieldGroups(config) {
|
|
331
|
+
if (config.fields.length) {
|
|
332
|
+
return {
|
|
333
|
+
fields: config.fields,
|
|
334
|
+
fieldGroups: [],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
fields: config.fieldGroups.flatMap((group) => group.fields),
|
|
339
|
+
fieldGroups: config.fieldGroups,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { entityKind } from '../helpers';
|
|
2
|
+
import type { BaseSectionOptions } from './section';
|
|
3
|
+
import { Section } from './section';
|
|
4
|
+
import { FieldGroupConfig, type FieldConfig } from '../fields';
|
|
5
|
+
import * as z from 'zod';
|
|
6
|
+
declare const configSchema: z.ZodObject<{
|
|
7
|
+
title: z.ZodString;
|
|
8
|
+
db: z.ZodObject<{
|
|
9
|
+
table: z.ZodString;
|
|
10
|
+
}, z.core.$strict>;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
type Config = z.infer<typeof configSchema>;
|
|
13
|
+
export declare class SimpleSection extends Section<Config> {
|
|
14
|
+
static readonly [entityKind] = "SimpleSection";
|
|
15
|
+
readonly type = "simple";
|
|
16
|
+
title: string;
|
|
17
|
+
constructor(config: BaseSectionOptions<Config>);
|
|
18
|
+
}
|
|
19
|
+
declare const optionsSchema: z.ZodObject<{
|
|
20
|
+
title: z.ZodString;
|
|
21
|
+
db: z.ZodObject<{
|
|
22
|
+
table: z.ZodString;
|
|
23
|
+
}, z.core.$strict>;
|
|
24
|
+
fields: z.ZodUnion<[z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>, z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>]>;
|
|
25
|
+
variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types").Variant, import("../types").Variant>>>;
|
|
26
|
+
name: z.ZodString;
|
|
27
|
+
order: z.ZodNumber;
|
|
28
|
+
readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
29
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
30
|
+
gallery: z.ZodOptional<z.ZodObject<{
|
|
31
|
+
db: z.ZodObject<{
|
|
32
|
+
tableName: z.ZodString;
|
|
33
|
+
identifierField: z.ZodOptional<z.ZodString>;
|
|
34
|
+
photoNameField: z.ZodOptional<z.ZodString>;
|
|
35
|
+
metaField: z.ZodOptional<z.ZodString>;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
watermark: z.ZodOptional<z.ZodBoolean>;
|
|
38
|
+
thumbnail: z.ZodOptional<z.ZodObject<{
|
|
39
|
+
width: z.ZodNumber;
|
|
40
|
+
height: z.ZodNumber;
|
|
41
|
+
crop: z.ZodBoolean;
|
|
42
|
+
quality: z.ZodNumber;
|
|
43
|
+
}, z.core.$strict>>;
|
|
44
|
+
}, z.core.$strict>>;
|
|
45
|
+
hooks: z.ZodOptional<z.ZodCustom<import("./section").Hooks, import("./section").Hooks>>;
|
|
46
|
+
}, z.core.$strict>;
|
|
47
|
+
declare const simpleSectionConfigSchema: z.ZodObject<{
|
|
48
|
+
type: z.ZodLiteral<"simple">;
|
|
49
|
+
fields: z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>;
|
|
50
|
+
build: z.ZodFunction<z.core.$ZodFunctionArgs, z.ZodCustom<SimpleSection, SimpleSection>>;
|
|
51
|
+
fieldGroups: z.ZodOptional<z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>>;
|
|
52
|
+
title: z.ZodString;
|
|
53
|
+
db: z.ZodObject<{
|
|
54
|
+
table: z.ZodString;
|
|
55
|
+
}, z.core.$strict>;
|
|
56
|
+
variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types").Variant, import("../types").Variant>>>;
|
|
57
|
+
name: z.ZodString;
|
|
58
|
+
order: z.ZodNumber;
|
|
59
|
+
readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
60
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
61
|
+
gallery: z.ZodOptional<z.ZodObject<{
|
|
62
|
+
db: z.ZodObject<{
|
|
63
|
+
tableName: z.ZodString;
|
|
64
|
+
identifierField: z.ZodOptional<z.ZodString>;
|
|
65
|
+
photoNameField: z.ZodOptional<z.ZodString>;
|
|
66
|
+
metaField: z.ZodOptional<z.ZodString>;
|
|
67
|
+
}, z.core.$strict>;
|
|
68
|
+
watermark: z.ZodOptional<z.ZodBoolean>;
|
|
69
|
+
thumbnail: z.ZodOptional<z.ZodObject<{
|
|
70
|
+
width: z.ZodNumber;
|
|
71
|
+
height: z.ZodNumber;
|
|
72
|
+
crop: z.ZodBoolean;
|
|
73
|
+
quality: z.ZodNumber;
|
|
74
|
+
}, z.core.$strict>>;
|
|
75
|
+
}, z.core.$strict>>;
|
|
76
|
+
hooks: z.ZodOptional<z.ZodCustom<import("./section").Hooks, import("./section").Hooks>>;
|
|
77
|
+
}, z.core.$strict>;
|
|
78
|
+
export type SimpleSectionOptions = z.infer<typeof optionsSchema>;
|
|
79
|
+
/**
|
|
80
|
+
* Simple section configuration
|
|
81
|
+
* This is the output of the simpleSection() helper function, it that can be serialized and used anywhere
|
|
82
|
+
* The build() method allows creating a SimpleSection instance when needed
|
|
83
|
+
*/
|
|
84
|
+
export type SimpleSectionConfig = z.infer<typeof simpleSectionConfigSchema>;
|
|
85
|
+
/**
|
|
86
|
+
* Helper function to create a simple section configuration
|
|
87
|
+
* Returns a config object with a build() method that can be serialized and used anywhere
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* ```typescript
|
|
91
|
+
* const sectionConfig = simpleSection({ name: 'settings', ... })
|
|
92
|
+
* const sectionInstance = sectionConfig.build()
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* @param section
|
|
96
|
+
*/
|
|
97
|
+
export declare function simpleSection(section: SimpleSectionOptions): SimpleSectionConfig;
|
|
98
|
+
export {};
|
|
99
|
+
//# sourceMappingURL=simple.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"simple.d.ts","sourceRoot":"","sources":["../../../src/core/sections/simple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EAAmC,OAAO,EAAE,MAAM,WAAW,CAAA;AACpE,OAAO,EAAE,gBAAgB,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAA;AAC9D,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAGxB,QAAA,MAAM,YAAY;;;;;kBAiBhB,CAAA;AAEF,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAA;AAE1C,qBAAa,aAAc,SAAQ,OAAO,CAAC,MAAM,CAAC;IAC9C,gBAAyB,CAAC,UAAU,CAAC,mBAAkB;IACvD,SAAkB,IAAI,YAAW;IACxB,KAAK,EAAE,MAAM,CAAA;gBAEV,MAAM,EAAE,kBAAkB,CAAC,MAAM,CAAC;CAIjD;AAED,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAGjB,CAAA;AAOF,QAAA,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAK7B,CAAA;AAEF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAA;AAEhE;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAE3E;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,mBAAmB,CA6ChF"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { entityKind } from '../helpers';
|
|
2
|
+
import { baseHelperFunctionOptionsSchema, Section } from './section';
|
|
3
|
+
import * as z from 'zod';
|
|
4
|
+
import { FieldGroupConfigSchema, fieldConfigSchema } from './section';
|
|
5
|
+
const configSchema = z.strictObject({
|
|
6
|
+
title: z.string('Section title is required').describe('The title of the section'),
|
|
7
|
+
db: z
|
|
8
|
+
.strictObject({
|
|
9
|
+
table: z
|
|
10
|
+
.string('Table name is required')
|
|
11
|
+
.describe('The name of the database table to use for this section'),
|
|
12
|
+
}, {
|
|
13
|
+
error: 'DB configuration is required',
|
|
14
|
+
})
|
|
15
|
+
.describe('In simple sections, the identifier field is a number field with the name `id`, and always has the value of 1 in the database table')
|
|
16
|
+
.describe('The database configuration for this section'),
|
|
17
|
+
});
|
|
18
|
+
export class SimpleSection extends Section {
|
|
19
|
+
static [entityKind] = 'SimpleSection';
|
|
20
|
+
type = 'simple';
|
|
21
|
+
title;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
super(config);
|
|
24
|
+
this.title = config.title;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const optionsSchema = z.strictObject({
|
|
28
|
+
...baseHelperFunctionOptionsSchema.shape,
|
|
29
|
+
...configSchema.shape,
|
|
30
|
+
});
|
|
31
|
+
const compiledOptionsSchema = z.strictObject({
|
|
32
|
+
...optionsSchema.shape,
|
|
33
|
+
fieldGroups: z.array(FieldGroupConfigSchema).optional().describe('The field groups of the section if provided'),
|
|
34
|
+
});
|
|
35
|
+
const simpleSectionConfigSchema = z.strictObject({
|
|
36
|
+
...compiledOptionsSchema.shape,
|
|
37
|
+
type: z.literal('simple').describe('The type of the section'),
|
|
38
|
+
fields: z.array(fieldConfigSchema).describe('The fields of the section'),
|
|
39
|
+
build: z.function().output(z.instanceof(SimpleSection)).describe('Build a SimpleSection instance from this config'),
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Helper function to create a simple section configuration
|
|
43
|
+
* Returns a config object with a build() method that can be serialized and used anywhere
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const sectionConfig = simpleSection({ name: 'settings', ... })
|
|
48
|
+
* const sectionInstance = sectionConfig.build()
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @param section
|
|
52
|
+
*/
|
|
53
|
+
export function simpleSection(section) {
|
|
54
|
+
/**
|
|
55
|
+
* Validate the section config
|
|
56
|
+
*/
|
|
57
|
+
const result = optionsSchema.safeParse(section);
|
|
58
|
+
if (!result.success) {
|
|
59
|
+
throw new Error(`[Section: ${section.name}]: ${z.prettifyError(result.error)}`);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolve fields and fieldGroups
|
|
63
|
+
*/
|
|
64
|
+
let fields = [];
|
|
65
|
+
let fieldGroups = [];
|
|
66
|
+
if (section.fields && section.fields.length > 0) {
|
|
67
|
+
const firstInput = section.fields[0];
|
|
68
|
+
/**
|
|
69
|
+
* Check if the first item is a field group.
|
|
70
|
+
* We don't need to check every item,
|
|
71
|
+
* because zod already validated the fields array.
|
|
72
|
+
*/
|
|
73
|
+
if (firstInput && 'fields' in firstInput) {
|
|
74
|
+
fieldGroups = section.fields;
|
|
75
|
+
fields = fieldGroups.flatMap((group) => group.fields);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
fields = section.fields;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const config = {
|
|
82
|
+
...section,
|
|
83
|
+
fields,
|
|
84
|
+
fieldGroups: fieldGroups.length > 0 ? fieldGroups : undefined,
|
|
85
|
+
type: 'simple',
|
|
86
|
+
build() {
|
|
87
|
+
return new SimpleSection({
|
|
88
|
+
...section,
|
|
89
|
+
fields,
|
|
90
|
+
fieldGroups: fieldGroups.length > 0 ? fieldGroups : undefined,
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
return config;
|
|
95
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape a string so it can be safely injected into an HTML text context.
|
|
3
|
+
* Returns `undefined` when the input is `undefined` or `null` to preserve caller intent.
|
|
4
|
+
*/
|
|
5
|
+
export declare function escapeHTML(value: string | undefined | null): string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* Sanitize rich-text/HTML content using DOMPurify with a restrictive allow-list.
|
|
8
|
+
* Always returns a string (empty string when value is undefined or null).
|
|
9
|
+
*/
|
|
10
|
+
export declare function sanitizeRichText(value: string | undefined | null): string;
|
|
11
|
+
//# sourceMappingURL=dom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../../../src/core/security/dom.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAU/E;AA+DD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAMzE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import DOMPurify from 'isomorphic-dompurify';
|
|
2
|
+
const HTML_ESCAPE_LOOKUP = {
|
|
3
|
+
'&': '&',
|
|
4
|
+
'<': '<',
|
|
5
|
+
'>': '>',
|
|
6
|
+
'"': '"',
|
|
7
|
+
"'": ''',
|
|
8
|
+
'/': '/',
|
|
9
|
+
'`': '`',
|
|
10
|
+
'/\/': '/',
|
|
11
|
+
};
|
|
12
|
+
const HTML_ESCAPE_REGEX = /[&<>"'\/`\/\/]/g;
|
|
13
|
+
/**
|
|
14
|
+
* Escape a string so it can be safely injected into an HTML text context.
|
|
15
|
+
* Returns `undefined` when the input is `undefined` or `null` to preserve caller intent.
|
|
16
|
+
*/
|
|
17
|
+
export function escapeHTML(value) {
|
|
18
|
+
if (value === undefined || value === null) {
|
|
19
|
+
return value === null ? '' : undefined;
|
|
20
|
+
}
|
|
21
|
+
if (!HTML_ESCAPE_REGEX.test(value)) {
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
return value.replace(HTML_ESCAPE_REGEX, (char) => HTML_ESCAPE_LOOKUP[char]);
|
|
25
|
+
}
|
|
26
|
+
const RICH_TEXT_ALLOWED_TAGS = [
|
|
27
|
+
'a',
|
|
28
|
+
'abbr',
|
|
29
|
+
'b',
|
|
30
|
+
'blockquote',
|
|
31
|
+
'br',
|
|
32
|
+
'caption',
|
|
33
|
+
'code',
|
|
34
|
+
'div',
|
|
35
|
+
'em',
|
|
36
|
+
'strong',
|
|
37
|
+
'i',
|
|
38
|
+
'img',
|
|
39
|
+
'figure',
|
|
40
|
+
'figcaption',
|
|
41
|
+
'hr',
|
|
42
|
+
'li',
|
|
43
|
+
'ol',
|
|
44
|
+
'p',
|
|
45
|
+
'pre',
|
|
46
|
+
'span',
|
|
47
|
+
'sub',
|
|
48
|
+
'sup',
|
|
49
|
+
'table',
|
|
50
|
+
'tbody',
|
|
51
|
+
'td',
|
|
52
|
+
'tfoot',
|
|
53
|
+
'th',
|
|
54
|
+
'thead',
|
|
55
|
+
'tr',
|
|
56
|
+
'u',
|
|
57
|
+
'ul',
|
|
58
|
+
];
|
|
59
|
+
const RICH_TEXT_ALLOWED_ATTR = [
|
|
60
|
+
'align',
|
|
61
|
+
'alt',
|
|
62
|
+
'class',
|
|
63
|
+
'data*',
|
|
64
|
+
'height',
|
|
65
|
+
'href',
|
|
66
|
+
'id',
|
|
67
|
+
'name',
|
|
68
|
+
'rel',
|
|
69
|
+
'src',
|
|
70
|
+
'style',
|
|
71
|
+
'target',
|
|
72
|
+
'title',
|
|
73
|
+
'width',
|
|
74
|
+
];
|
|
75
|
+
const RICH_TEXT_URI_REGEXP = /^(?:(?:https?|mailto|tel|ftp|file):|data:image\/[a-z0-9.+-]+;base64,)/i;
|
|
76
|
+
const RICH_TEXT_SANITIZE_CONFIG = {
|
|
77
|
+
ALLOWED_TAGS: [...RICH_TEXT_ALLOWED_TAGS],
|
|
78
|
+
ALLOWED_ATTR: [...RICH_TEXT_ALLOWED_ATTR],
|
|
79
|
+
ALLOW_DATA_ATTR: true,
|
|
80
|
+
ALLOWED_URI_REGEXP: RICH_TEXT_URI_REGEXP,
|
|
81
|
+
FORBID_TAGS: ['style', 'script'],
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Sanitize rich-text/HTML content using DOMPurify with a restrictive allow-list.
|
|
85
|
+
* Always returns a string (empty string when value is undefined or null).
|
|
86
|
+
*/
|
|
87
|
+
export function sanitizeRichText(value) {
|
|
88
|
+
if (value === undefined || value === null || value.trim().length === 0) {
|
|
89
|
+
return '';
|
|
90
|
+
}
|
|
91
|
+
return DOMPurify.sanitize(value, RICH_TEXT_SANITIZE_CONFIG);
|
|
92
|
+
}
|