nextly 0.0.1 → 0.0.2-alpha.0
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 +22 -0
- package/README.md +122 -0
- package/dist/_dts-chunks/collections-handler.d-DjgO74Wt.d.ts +20540 -0
- package/dist/_dts-chunks/config.d-DNwsDnjs.d.ts +2589 -0
- package/dist/_dts-chunks/define-component.d-BUgTHmt3.d.ts +1149 -0
- package/dist/_dts-chunks/image-processor.d-OO1PmMrv.d.ts +335 -0
- package/dist/_dts-chunks/index.d-axCAzZ7m.d.ts +17842 -0
- package/dist/_dts-chunks/media.d-DjDOZo4B.d.ts +117 -0
- package/dist/_dts-chunks/on-error.d-CHIKWNxd.d.ts +38 -0
- package/dist/_dts-chunks/storage.d-BUhQ2we_.d.ts +404 -0
- package/dist/actions/index.d.ts +239 -0
- package/dist/actions/index.mjs +281 -0
- package/dist/api/auth-state.d.ts +5 -0
- package/dist/api/auth-state.mjs +131 -0
- package/dist/api/collections-schema-detail.d.ts +56 -0
- package/dist/api/collections-schema-detail.mjs +244 -0
- package/dist/api/collections-schema-export.d.ts +56 -0
- package/dist/api/collections-schema-export.mjs +129 -0
- package/dist/api/collections-schema.d.ts +59 -0
- package/dist/api/collections-schema.mjs +207 -0
- package/dist/api/components-detail.d.ts +50 -0
- package/dist/api/components-detail.mjs +132 -0
- package/dist/api/components.d.ts +69 -0
- package/dist/api/components.mjs +144 -0
- package/dist/api/email-providers-default.d.ts +40 -0
- package/dist/api/email-providers-default.mjs +75 -0
- package/dist/api/email-providers-detail.d.ts +81 -0
- package/dist/api/email-providers-detail.mjs +109 -0
- package/dist/api/email-providers-test.d.ts +43 -0
- package/dist/api/email-providers-test.mjs +114 -0
- package/dist/api/email-providers.d.ts +69 -0
- package/dist/api/email-providers.mjs +110 -0
- package/dist/api/email-send-template.d.ts +41 -0
- package/dist/api/email-send-template.mjs +58 -0
- package/dist/api/email-send.d.ts +42 -0
- package/dist/api/email-send.mjs +58 -0
- package/dist/api/email-templates-detail.d.ts +74 -0
- package/dist/api/email-templates-detail.mjs +112 -0
- package/dist/api/email-templates-layout.d.ts +55 -0
- package/dist/api/email-templates-layout.mjs +92 -0
- package/dist/api/email-templates-preview.d.ts +48 -0
- package/dist/api/email-templates-preview.mjs +93 -0
- package/dist/api/email-templates.d.ts +61 -0
- package/dist/api/email-templates.mjs +118 -0
- package/dist/api/health.d.ts +68 -0
- package/dist/api/health.mjs +67 -0
- package/dist/api/index.d.ts +54 -0
- package/dist/api/index.mjs +16 -0
- package/dist/api/media-bulk.d.ts +74 -0
- package/dist/api/media-bulk.mjs +196 -0
- package/dist/api/media-folders.d.ts +112 -0
- package/dist/api/media-folders.mjs +187 -0
- package/dist/api/media-handlers.d.ts +102 -0
- package/dist/api/media-handlers.mjs +437 -0
- package/dist/api/media.d.ts +117 -0
- package/dist/api/media.mjs +242 -0
- package/dist/api/singles-detail.d.ts +87 -0
- package/dist/api/singles-detail.mjs +170 -0
- package/dist/api/singles-schema-detail.d.ts +54 -0
- package/dist/api/singles-schema-detail.mjs +182 -0
- package/dist/api/singles.d.ts +34 -0
- package/dist/api/singles.mjs +94 -0
- package/dist/api/storage-upload-url.d.ts +48 -0
- package/dist/api/storage-upload-url.mjs +202 -0
- package/dist/api/uploads.d.ts +109 -0
- package/dist/api/uploads.mjs +359 -0
- package/dist/auth/index.d.ts +425 -0
- package/dist/auth/index.mjs +199 -0
- package/dist/boot-apply-PQSYLDIN.mjs +7 -0
- package/dist/chunk-2OALJTK6.mjs +489 -0
- package/dist/chunk-2Q2SX2CS.mjs +365 -0
- package/dist/chunk-2TFX4ND3.mjs +13 -0
- package/dist/chunk-2TWPDSYD.mjs +87 -0
- package/dist/chunk-2W3DVD7S.mjs +647 -0
- package/dist/chunk-2ZFKXPQM.mjs +88 -0
- package/dist/chunk-3FA7FKAV.mjs +832 -0
- package/dist/chunk-3NZ2KMBL.mjs +58 -0
- package/dist/chunk-4MJLT6PZ.mjs +0 -0
- package/dist/chunk-56WO4WX7.mjs +0 -0
- package/dist/chunk-5APFUGAD.mjs +89 -0
- package/dist/chunk-5HMZ644B.mjs +108 -0
- package/dist/chunk-67GXH6PR.mjs +32 -0
- package/dist/chunk-6JNEPWRW.mjs +14368 -0
- package/dist/chunk-6NFHQIJD.mjs +45 -0
- package/dist/chunk-7P6ASYW6.mjs +9 -0
- package/dist/chunk-A3WPLSDT.mjs +1364 -0
- package/dist/chunk-AGJ6F2T3.mjs +144 -0
- package/dist/chunk-AK6Z23OX.mjs +1464 -0
- package/dist/chunk-APKKRD2G.mjs +102 -0
- package/dist/chunk-B2GV2BWH.mjs +73 -0
- package/dist/chunk-D5HQBNUB.mjs +74 -0
- package/dist/chunk-DNNG377Z.mjs +204 -0
- package/dist/chunk-DP3G27G5.mjs +135 -0
- package/dist/chunk-DV6WVX2Q.mjs +0 -0
- package/dist/chunk-DXGGXIUZ.mjs +57 -0
- package/dist/chunk-EGXBZCGC.mjs +943 -0
- package/dist/chunk-ERCNLX3V.mjs +176 -0
- package/dist/chunk-FQULBZ53.mjs +850 -0
- package/dist/chunk-G2AA4QLC.mjs +262 -0
- package/dist/chunk-GDBJ5JCU.mjs +488 -0
- package/dist/chunk-GJNSJU4S.mjs +19 -0
- package/dist/chunk-GZ6DCQKC.mjs +69 -0
- package/dist/chunk-H26B4FYG.mjs +167 -0
- package/dist/chunk-I4JMR3UR.mjs +21 -0
- package/dist/chunk-INV7QKLG.mjs +508 -0
- package/dist/chunk-IUDOC7N7.mjs +46 -0
- package/dist/chunk-IZWPRDC3.mjs +206 -0
- package/dist/chunk-KIMNCZGV.mjs +15 -0
- package/dist/chunk-L6HW2DA7.mjs +15 -0
- package/dist/chunk-LAZXX4HR.mjs +100 -0
- package/dist/chunk-LDKCUMHK.mjs +95 -0
- package/dist/chunk-LRXMECUA.mjs +0 -0
- package/dist/chunk-M52VMPGA.mjs +119 -0
- package/dist/chunk-MGUWEEI6.mjs +160 -0
- package/dist/chunk-NRUWQ5Z7.mjs +419 -0
- package/dist/chunk-NSEFNNU4.mjs +25360 -0
- package/dist/chunk-NTHVDFGO.mjs +138 -0
- package/dist/chunk-O3QHXMOX.mjs +3166 -0
- package/dist/chunk-P7NH2OSC.mjs +2605 -0
- package/dist/chunk-PKMABBB5.mjs +184 -0
- package/dist/chunk-PWS6XGJK.mjs +76 -0
- package/dist/chunk-R6JJQHFC.mjs +20 -0
- package/dist/chunk-RJLLGGPG.mjs +0 -0
- package/dist/chunk-SBACDPNX.mjs +689 -0
- package/dist/chunk-TO5AFLVQ.mjs +124 -0
- package/dist/chunk-TS7GHTG2.mjs +5436 -0
- package/dist/chunk-UJ2IMJ4W.mjs +133 -0
- package/dist/chunk-UOP63Q54.mjs +102 -0
- package/dist/chunk-UUOFWCM6.mjs +78 -0
- package/dist/chunk-V4EQTOA4.mjs +893 -0
- package/dist/chunk-VJ66NCL4.mjs +193 -0
- package/dist/chunk-VQJQHVEV.mjs +29 -0
- package/dist/chunk-VTJADRO3.mjs +141 -0
- package/dist/chunk-VWF3JO32.mjs +0 -0
- package/dist/chunk-W4MGXIRR.mjs +27 -0
- package/dist/chunk-W5KKPZT5.mjs +1204 -0
- package/dist/chunk-WD34YQ6T.mjs +381 -0
- package/dist/chunk-WZBYMYVW.mjs +14 -0
- package/dist/chunk-X23WKS3Z.mjs +50 -0
- package/dist/chunk-X7TXCYYN.mjs +6496 -0
- package/dist/chunk-XGI4EMS3.mjs +140 -0
- package/dist/chunk-XZKLBMN6.mjs +1153 -0
- package/dist/chunk-YB7INWPY.mjs +0 -0
- package/dist/chunk-YV4Y7SDL.mjs +83 -0
- package/dist/chunk-YZNBLFIW.mjs +1688 -0
- package/dist/chunk-YZZCTONM.mjs +263 -0
- package/dist/chunk-ZE6A3FYH.mjs +289 -0
- package/dist/cli/nextly.mjs +68 -0
- package/dist/cli/utils/index.d.ts +449 -0
- package/dist/cli/utils/index.mjs +49 -0
- package/dist/component-schema-service-5577KVW6.mjs +11 -0
- package/dist/config-loader-23YEMC3Z.mjs +23 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.mjs +109 -0
- package/dist/container-ORGFGYSZ.mjs +9 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.mjs +40 -0
- package/dist/database/seeders/index.d.ts +93 -0
- package/dist/database/seeders/index.mjs +47 -0
- package/dist/db-sync-demote-LJGKLB3S.mjs +117 -0
- package/dist/db-sync-promote-B26VSYQF.mjs +113 -0
- package/dist/dev-reload-broadcaster-B73IQ53V.mjs +25 -0
- package/dist/dist-M2NOU37V.mjs +19 -0
- package/dist/drizzle-kit-lazy-D2M2PXR2.mjs +13 -0
- package/dist/dynamic-collection-schema-service-IEXTPIZ7.mjs +8 -0
- package/dist/errors/index.d.ts +159 -0
- package/dist/errors/index.mjs +10 -0
- package/dist/factory-IWMBKUJM.mjs +15 -0
- package/dist/first-run-QIVKWJIF.mjs +63 -0
- package/dist/fresh-push-NR67DC3R.mjs +8 -0
- package/dist/index.d.ts +4175 -0
- package/dist/index.mjs +1336 -0
- package/dist/local-plugin-PTET4NAT.mjs +7 -0
- package/dist/logger-NU46DXNY.mjs +15 -0
- package/dist/logger-YE4TC7ZN.mjs +9 -0
- package/dist/migration-journal-EP532Y4L.mjs +139 -0
- package/dist/migrations/mysql/0000_eager_sentry.sql +174 -0
- package/dist/migrations/mysql/0001_soft_giant_girl.sql +27 -0
- package/dist/migrations/mysql/0002_media_table.sql +24 -0
- package/dist/migrations/mysql/0003_dynamic_singles.sql +37 -0
- package/dist/migrations/mysql/0004_dynamic_components.sql +35 -0
- package/dist/migrations/mysql/0005_user_management_tables.sql +92 -0
- package/dist/migrations/mysql/0006_api_keys.sql +36 -0
- package/dist/migrations/mysql/0007_general_settings.sql +20 -0
- package/dist/migrations/mysql/0008_site_settings_logo_url.sql +9 -0
- package/dist/migrations/mysql/0009_activity_log.sql +30 -0
- package/dist/migrations/mysql/0010_site_settings_sidebar.sql +13 -0
- package/dist/migrations/mysql/0011_missing_tables_and_columns.sql +54 -0
- package/dist/migrations/mysql/0012_image_sizes_and_focal_point.sql +30 -0
- package/dist/migrations/mysql/0012_media_folders.sql +43 -0
- package/dist/migrations/mysql/0013_user_brute_force_protection.sql +31 -0
- package/dist/migrations/mysql/0014_email_template_attachments.sql +12 -0
- package/dist/migrations/mysql/0015_media_uploaded_by_nullable.sql +15 -0
- package/dist/migrations/mysql/20260429_000000_000_initial_journal.sql +22 -0
- package/dist/migrations/mysql/20260501_000000_journal_batch.sql +17 -0
- package/dist/migrations/mysql/20260501_000001_audit_log.sql +24 -0
- package/dist/migrations/mysql/20260504_000000_nextly_meta.sql +21 -0
- package/dist/migrations/mysql/meta/0000_snapshot.json +1005 -0
- package/dist/migrations/mysql/meta/0001_snapshot.json +1099 -0
- package/dist/migrations/mysql/meta/_journal.json +41 -0
- package/dist/migrations/postgresql/0000_misty_king_bedlam.sql +169 -0
- package/dist/migrations/postgresql/0001_perpetual_captain_marvel.sql +8 -0
- package/dist/migrations/postgresql/0002_sad_spectrum.sql +16 -0
- package/dist/migrations/postgresql/0003_hesitant_ultron.sql +17 -0
- package/dist/migrations/postgresql/0004_media_table.sql +24 -0
- package/dist/migrations/postgresql/0005_media_folders.sql +36 -0
- package/dist/migrations/postgresql/0006_dynamic_collections_update.sql +50 -0
- package/dist/migrations/postgresql/0007_dynamic_singles.sql +38 -0
- package/dist/migrations/postgresql/0008_dynamic_components.sql +37 -0
- package/dist/migrations/postgresql/0009_user_management_tables.sql +95 -0
- package/dist/migrations/postgresql/0010_api_keys.sql +34 -0
- package/dist/migrations/postgresql/0011_general_settings.sql +20 -0
- package/dist/migrations/postgresql/0012_site_settings_logo_url.sql +9 -0
- package/dist/migrations/postgresql/0013_activity_log.sql +29 -0
- package/dist/migrations/postgresql/0014_image_sizes_and_focal_point.sql +33 -0
- package/dist/migrations/postgresql/0014_site_settings_sidebar.sql +13 -0
- package/dist/migrations/postgresql/0015_user_brute_force_protection.sql +29 -0
- package/dist/migrations/postgresql/0016_email_template_attachments.sql +12 -0
- package/dist/migrations/postgresql/0017_media_uploaded_by_nullable.sql +15 -0
- package/dist/migrations/postgresql/20260429_000000_000_initial_journal.sql +24 -0
- package/dist/migrations/postgresql/20260501_000000_journal_batch.sql +17 -0
- package/dist/migrations/postgresql/20260501_000001_audit_log.sql +24 -0
- package/dist/migrations/postgresql/20260504_000000_nextly_meta.sql +22 -0
- package/dist/migrations/postgresql/meta/0000_snapshot.json +1286 -0
- package/dist/migrations/postgresql/meta/0001_snapshot.json +1407 -0
- package/dist/migrations/postgresql/meta/0002_snapshot.json +1552 -0
- package/dist/migrations/postgresql/meta/0003_snapshot.json +1695 -0
- package/dist/migrations/postgresql/meta/0010_snapshot.json +2345 -0
- package/dist/migrations/postgresql/meta/_journal.json +90 -0
- package/dist/migrations/sqlite/0000_api_keys.sql +34 -0
- package/dist/migrations/sqlite/0001_general_settings.sql +20 -0
- package/dist/migrations/sqlite/0002_site_settings_logo_url.sql +9 -0
- package/dist/migrations/sqlite/0003_activity_log.sql +29 -0
- package/dist/migrations/sqlite/0004_image_sizes_and_focal_point.sql +29 -0
- package/dist/migrations/sqlite/0004_site_settings_sidebar.sql +11 -0
- package/dist/migrations/sqlite/0005_user_brute_force_protection.sql +29 -0
- package/dist/migrations/sqlite/0006_email_template_attachments.sql +12 -0
- package/dist/migrations/sqlite/0007_media_uploaded_by_nullable.sql +111 -0
- package/dist/migrations/sqlite/20260429_000000_000_initial_journal.sql +24 -0
- package/dist/migrations/sqlite/20260501_000000_journal_batch.sql +19 -0
- package/dist/migrations/sqlite/20260501_000001_audit_log.sql +24 -0
- package/dist/migrations/sqlite/20260504_000000_nextly_meta.sql +21 -0
- package/dist/migrations/sqlite/20260505_000000_user_management_tables.sql +77 -0
- package/dist/next.d.ts +57 -0
- package/dist/next.mjs +55 -0
- package/dist/observability/index.d.ts +87 -0
- package/dist/observability/index.mjs +57 -0
- package/dist/permissions-3DZZQZMI.mjs +39 -0
- package/dist/pipeline-YOML7SWF.mjs +29 -0
- package/dist/preview-ZZTR3QGS.mjs +9 -0
- package/dist/program-PW6UB2ZC.mjs +5934 -0
- package/dist/reconcile-single-tables-7ENVXJGB.mjs +7 -0
- package/dist/register-SF6E6FVU.mjs +49 -0
- package/dist/reload-config-HWQ4G5MM.mjs +23 -0
- package/dist/resolve-single-table-name-JSOMUB3R.mjs +7 -0
- package/dist/routeHandler-UNMMJIBM.mjs +77 -0
- package/dist/runtime-schema-generator-NRA6A6Z6.mjs +8 -0
- package/dist/runtime.d.ts +120 -0
- package/dist/runtime.mjs +73 -0
- package/dist/schema-hash-FMMG6VPJ.mjs +13 -0
- package/dist/schema-registry-EQ36FZDP.mjs +7 -0
- package/dist/scripts/load-env.mjs +42 -0
- package/dist/storage/index.d.ts +566 -0
- package/dist/storage/index.mjs +45 -0
- package/dist/super-admin-G5ZK5F4T.mjs +39 -0
- package/dist/system-table-service-WGSRVEGT.mjs +17 -0
- package/dist/users-7KELGRYJ.mjs +38 -0
- package/package.json +308 -9
|
@@ -0,0 +1,850 @@
|
|
|
1
|
+
// src/shared/sql-reserved.ts
|
|
2
|
+
var RESERVED_SLUGS = [
|
|
3
|
+
// API routes
|
|
4
|
+
"api",
|
|
5
|
+
"graphql",
|
|
6
|
+
"rest",
|
|
7
|
+
// Admin routes
|
|
8
|
+
"admin",
|
|
9
|
+
"dashboard",
|
|
10
|
+
// Auth routes
|
|
11
|
+
"auth",
|
|
12
|
+
"login",
|
|
13
|
+
"logout",
|
|
14
|
+
"register",
|
|
15
|
+
"signup",
|
|
16
|
+
"signin",
|
|
17
|
+
"signout",
|
|
18
|
+
"forgot-password",
|
|
19
|
+
"reset-password",
|
|
20
|
+
"verify",
|
|
21
|
+
"verify-email",
|
|
22
|
+
// System routes
|
|
23
|
+
"static",
|
|
24
|
+
"public",
|
|
25
|
+
"assets",
|
|
26
|
+
"_next",
|
|
27
|
+
"health",
|
|
28
|
+
"status",
|
|
29
|
+
"metrics",
|
|
30
|
+
// Common system collections
|
|
31
|
+
"users",
|
|
32
|
+
"roles",
|
|
33
|
+
"permissions",
|
|
34
|
+
"sessions",
|
|
35
|
+
"tokens",
|
|
36
|
+
"media",
|
|
37
|
+
"uploads",
|
|
38
|
+
"files"
|
|
39
|
+
];
|
|
40
|
+
var SQL_RESERVED_KEYWORDS = [
|
|
41
|
+
// Data manipulation
|
|
42
|
+
"select",
|
|
43
|
+
"insert",
|
|
44
|
+
"update",
|
|
45
|
+
"delete",
|
|
46
|
+
"from",
|
|
47
|
+
"where",
|
|
48
|
+
"set",
|
|
49
|
+
"values",
|
|
50
|
+
// Table operations
|
|
51
|
+
"create",
|
|
52
|
+
"drop",
|
|
53
|
+
"alter",
|
|
54
|
+
"table",
|
|
55
|
+
"index",
|
|
56
|
+
"view",
|
|
57
|
+
"trigger",
|
|
58
|
+
"database",
|
|
59
|
+
// Joins and relations
|
|
60
|
+
"join",
|
|
61
|
+
"inner",
|
|
62
|
+
"outer",
|
|
63
|
+
"left",
|
|
64
|
+
"right",
|
|
65
|
+
"cross",
|
|
66
|
+
"full",
|
|
67
|
+
"on",
|
|
68
|
+
"using",
|
|
69
|
+
// Clauses
|
|
70
|
+
"order",
|
|
71
|
+
"group",
|
|
72
|
+
"by",
|
|
73
|
+
"having",
|
|
74
|
+
"limit",
|
|
75
|
+
"offset",
|
|
76
|
+
"distinct",
|
|
77
|
+
"as",
|
|
78
|
+
"case",
|
|
79
|
+
"when",
|
|
80
|
+
"then",
|
|
81
|
+
"else",
|
|
82
|
+
"end",
|
|
83
|
+
// Logical
|
|
84
|
+
"and",
|
|
85
|
+
"or",
|
|
86
|
+
"not",
|
|
87
|
+
"in",
|
|
88
|
+
"is",
|
|
89
|
+
"null",
|
|
90
|
+
"like",
|
|
91
|
+
"between",
|
|
92
|
+
"exists",
|
|
93
|
+
// Constraints
|
|
94
|
+
"primary",
|
|
95
|
+
"foreign",
|
|
96
|
+
"key",
|
|
97
|
+
"references",
|
|
98
|
+
"unique",
|
|
99
|
+
"check",
|
|
100
|
+
"constraint",
|
|
101
|
+
"default",
|
|
102
|
+
// Transactions
|
|
103
|
+
"begin",
|
|
104
|
+
"commit",
|
|
105
|
+
"rollback",
|
|
106
|
+
"transaction",
|
|
107
|
+
// Aggregates (can cause confusion)
|
|
108
|
+
"count",
|
|
109
|
+
"sum",
|
|
110
|
+
"avg",
|
|
111
|
+
"min",
|
|
112
|
+
"max",
|
|
113
|
+
// Other commonly problematic
|
|
114
|
+
"all",
|
|
115
|
+
"any",
|
|
116
|
+
"union",
|
|
117
|
+
"except",
|
|
118
|
+
"intersect",
|
|
119
|
+
"column",
|
|
120
|
+
"row",
|
|
121
|
+
"rows",
|
|
122
|
+
"for",
|
|
123
|
+
"to",
|
|
124
|
+
"into",
|
|
125
|
+
"with",
|
|
126
|
+
// High-risk specific keywords
|
|
127
|
+
"user",
|
|
128
|
+
"password",
|
|
129
|
+
"role",
|
|
130
|
+
"session",
|
|
131
|
+
"grant",
|
|
132
|
+
"revoke",
|
|
133
|
+
"match",
|
|
134
|
+
"natural"
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
// src/shared/base-validator.ts
|
|
138
|
+
var SLUG_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
139
|
+
var FIELD_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
140
|
+
var VALID_FIELD_TYPES = [
|
|
141
|
+
// Text types
|
|
142
|
+
"text",
|
|
143
|
+
"textarea",
|
|
144
|
+
"richText",
|
|
145
|
+
"email",
|
|
146
|
+
"password",
|
|
147
|
+
"code",
|
|
148
|
+
// Numeric types
|
|
149
|
+
"number",
|
|
150
|
+
// Selection types
|
|
151
|
+
"checkbox",
|
|
152
|
+
"date",
|
|
153
|
+
"select",
|
|
154
|
+
"radio",
|
|
155
|
+
// Media types
|
|
156
|
+
"upload",
|
|
157
|
+
// Relational types
|
|
158
|
+
"relationship",
|
|
159
|
+
// Structured types
|
|
160
|
+
"repeater",
|
|
161
|
+
"group",
|
|
162
|
+
"json",
|
|
163
|
+
// Selection / tag types
|
|
164
|
+
"chips",
|
|
165
|
+
// Component type
|
|
166
|
+
"component"
|
|
167
|
+
];
|
|
168
|
+
var VALID_FIELD_TYPES_SET = new Set(VALID_FIELD_TYPES);
|
|
169
|
+
var DEFAULT_SQL_KEYWORDS_SET = new Set(
|
|
170
|
+
SQL_RESERVED_KEYWORDS
|
|
171
|
+
);
|
|
172
|
+
function isSQLKeyword(name, keywordsSet = DEFAULT_SQL_KEYWORDS_SET) {
|
|
173
|
+
return keywordsSet.has(name.toLowerCase());
|
|
174
|
+
}
|
|
175
|
+
function validateSlugShared(slug, errors, ctx) {
|
|
176
|
+
const path = "slug";
|
|
177
|
+
if (!slug) {
|
|
178
|
+
errors.push({
|
|
179
|
+
path,
|
|
180
|
+
message: `${ctx.entityLabel} slug is required`,
|
|
181
|
+
code: "SLUG_REQUIRED"
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (typeof slug !== "string") {
|
|
186
|
+
errors.push({
|
|
187
|
+
path,
|
|
188
|
+
message: `${ctx.entityLabel} slug must be a string`,
|
|
189
|
+
code: "SLUG_INVALID_TYPE"
|
|
190
|
+
});
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (slug.length < 1) {
|
|
194
|
+
errors.push({
|
|
195
|
+
path,
|
|
196
|
+
message: `${ctx.entityLabel} slug must be at least 1 character`,
|
|
197
|
+
code: "SLUG_TOO_SHORT"
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (slug.length > 50) {
|
|
201
|
+
errors.push({
|
|
202
|
+
path,
|
|
203
|
+
message: `${ctx.entityLabel} slug must be at most 50 characters`,
|
|
204
|
+
code: "SLUG_TOO_LONG"
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (!SLUG_PATTERN.test(slug)) {
|
|
208
|
+
errors.push({
|
|
209
|
+
path,
|
|
210
|
+
message: `${ctx.entityLabel} slug must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores`,
|
|
211
|
+
code: "SLUG_INVALID_FORMAT"
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
if (ctx.reservedSlugsSet.has(slug.toLowerCase())) {
|
|
215
|
+
errors.push({
|
|
216
|
+
path,
|
|
217
|
+
message: `${ctx.entityLabel} slug '${slug}' is reserved and cannot be used`,
|
|
218
|
+
code: "SLUG_RESERVED"
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
if (isSQLKeyword(slug, ctx.sqlKeywordsSet)) {
|
|
222
|
+
errors.push({
|
|
223
|
+
path,
|
|
224
|
+
message: `${ctx.entityLabel} slug '${slug}' is a SQL reserved keyword. Use a different name or set 'dbName' to customize the table name`,
|
|
225
|
+
code: "SLUG_SQL_KEYWORD"
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function validateFieldNameShared(name, path, errors, seenNames, sqlKeywordsSet = DEFAULT_SQL_KEYWORDS_SET) {
|
|
230
|
+
if (!name) {
|
|
231
|
+
errors.push({
|
|
232
|
+
path: `${path}.name`,
|
|
233
|
+
message: "Field name is required",
|
|
234
|
+
code: "FIELD_NAME_REQUIRED"
|
|
235
|
+
});
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (typeof name !== "string") {
|
|
239
|
+
errors.push({
|
|
240
|
+
path: `${path}.name`,
|
|
241
|
+
message: "Field name must be a string",
|
|
242
|
+
code: "FIELD_NAME_REQUIRED"
|
|
243
|
+
});
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
if (!FIELD_NAME_PATTERN.test(name)) {
|
|
247
|
+
errors.push({
|
|
248
|
+
path: `${path}.name`,
|
|
249
|
+
message: `Field name '${name}' must start with a letter and contain only letters, numbers, and underscores`,
|
|
250
|
+
code: "FIELD_NAME_INVALID_FORMAT"
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
if (isSQLKeyword(name, sqlKeywordsSet)) {
|
|
254
|
+
errors.push({
|
|
255
|
+
path: `${path}.name`,
|
|
256
|
+
message: `Field name '${name}' is a SQL reserved keyword. Consider using a different name like '${name}Field' or '${name}Value'`,
|
|
257
|
+
code: "FIELD_NAME_SQL_KEYWORD"
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const nameLower = name.toLowerCase();
|
|
261
|
+
if (seenNames.has(nameLower)) {
|
|
262
|
+
errors.push({
|
|
263
|
+
path: `${path}.name`,
|
|
264
|
+
message: `Duplicate field name '${name}' at this level`,
|
|
265
|
+
code: "FIELD_NAME_DUPLICATE"
|
|
266
|
+
});
|
|
267
|
+
} else {
|
|
268
|
+
seenNames.add(nameLower);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function validateFieldTypeShared(fieldType, path, errors) {
|
|
272
|
+
if (!fieldType) {
|
|
273
|
+
errors.push({
|
|
274
|
+
path: `${path}.type`,
|
|
275
|
+
message: "Field type is required",
|
|
276
|
+
code: "FIELD_TYPE_REQUIRED"
|
|
277
|
+
});
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
if (typeof fieldType !== "string" || !VALID_FIELD_TYPES_SET.has(fieldType)) {
|
|
281
|
+
errors.push({
|
|
282
|
+
path: `${path}.type`,
|
|
283
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
284
|
+
message: `Invalid field type '${String(fieldType)}'. Valid types: ${VALID_FIELD_TYPES.join(", ")}`,
|
|
285
|
+
code: "FIELD_TYPE_INVALID"
|
|
286
|
+
});
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
function validateSelectOptionsShared(field, path, errors, fieldType) {
|
|
292
|
+
const options = field.options;
|
|
293
|
+
const requiredCode = fieldType === "select" ? "SELECT_OPTIONS_REQUIRED" : "RADIO_OPTIONS_REQUIRED";
|
|
294
|
+
const emptyCode = fieldType === "select" ? "SELECT_OPTIONS_EMPTY" : "RADIO_OPTIONS_EMPTY";
|
|
295
|
+
if (!options) {
|
|
296
|
+
errors.push({
|
|
297
|
+
path: `${path}.options`,
|
|
298
|
+
message: `${fieldType} field must have an 'options' array`,
|
|
299
|
+
code: requiredCode
|
|
300
|
+
});
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (!Array.isArray(options)) {
|
|
304
|
+
errors.push({
|
|
305
|
+
path: `${path}.options`,
|
|
306
|
+
message: `${fieldType} field 'options' must be an array`,
|
|
307
|
+
code: requiredCode
|
|
308
|
+
});
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (options.length === 0) {
|
|
312
|
+
errors.push({
|
|
313
|
+
path: `${path}.options`,
|
|
314
|
+
message: `${fieldType} field must have at least one option`,
|
|
315
|
+
code: emptyCode
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function validateRelationshipTargetShared(field, path, errors, validCollectionSlugs) {
|
|
320
|
+
const relationTo = field.relationTo;
|
|
321
|
+
if (!relationTo) {
|
|
322
|
+
errors.push({
|
|
323
|
+
path: `${path}.relationTo`,
|
|
324
|
+
message: "Relationship field must specify 'relationTo'",
|
|
325
|
+
code: "RELATIONSHIP_TARGET_REQUIRED"
|
|
326
|
+
});
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const knownSet = validCollectionSlugs ? new Set(validCollectionSlugs) : void 0;
|
|
330
|
+
const knownList = validCollectionSlugs?.join(", ") ?? "";
|
|
331
|
+
if (typeof relationTo === "string") {
|
|
332
|
+
if (!SLUG_PATTERN.test(relationTo)) {
|
|
333
|
+
errors.push({
|
|
334
|
+
path: `${path}.relationTo`,
|
|
335
|
+
message: `Invalid relationTo value '${relationTo}'. Must be a valid collection slug`,
|
|
336
|
+
code: "RELATIONSHIP_TARGET_INVALID"
|
|
337
|
+
});
|
|
338
|
+
} else if (knownSet && !knownSet.has(relationTo)) {
|
|
339
|
+
errors.push({
|
|
340
|
+
path: `${path}.relationTo`,
|
|
341
|
+
message: `Unknown collection '${relationTo}' in relationTo. Available collections: ${knownList}`,
|
|
342
|
+
code: "RELATIONSHIP_TARGET_UNKNOWN"
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (Array.isArray(relationTo)) {
|
|
348
|
+
if (relationTo.length === 0) {
|
|
349
|
+
errors.push({
|
|
350
|
+
path: `${path}.relationTo`,
|
|
351
|
+
message: "Relationship field 'relationTo' array cannot be empty",
|
|
352
|
+
code: "RELATIONSHIP_TARGET_REQUIRED"
|
|
353
|
+
});
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
relationTo.forEach((target, index) => {
|
|
357
|
+
if (typeof target !== "string") {
|
|
358
|
+
errors.push({
|
|
359
|
+
path: `${path}.relationTo[${index}]`,
|
|
360
|
+
message: "Each relationTo value must be a string",
|
|
361
|
+
code: "RELATIONSHIP_TARGET_INVALID"
|
|
362
|
+
});
|
|
363
|
+
} else if (!SLUG_PATTERN.test(target)) {
|
|
364
|
+
errors.push({
|
|
365
|
+
path: `${path}.relationTo[${index}]`,
|
|
366
|
+
message: `Invalid relationTo value '${target}'. Must be a valid collection slug`,
|
|
367
|
+
code: "RELATIONSHIP_TARGET_INVALID"
|
|
368
|
+
});
|
|
369
|
+
} else if (knownSet && !knownSet.has(target)) {
|
|
370
|
+
errors.push({
|
|
371
|
+
path: `${path}.relationTo[${index}]`,
|
|
372
|
+
message: `Unknown collection '${target}' in relationTo. Available collections: ${knownList}`,
|
|
373
|
+
code: "RELATIONSHIP_TARGET_UNKNOWN"
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
errors.push({
|
|
380
|
+
path: `${path}.relationTo`,
|
|
381
|
+
message: "'relationTo' must be a string or array of strings",
|
|
382
|
+
code: "RELATIONSHIP_TARGET_INVALID"
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
function validateComponentFieldRefShared(field, path, errors) {
|
|
386
|
+
const singleComp = field.component;
|
|
387
|
+
const multiComp = field.components;
|
|
388
|
+
if (!singleComp && !multiComp) {
|
|
389
|
+
errors.push({
|
|
390
|
+
path,
|
|
391
|
+
message: "Component field must specify either 'component' (single) or 'components' (multi/dynamic zone)",
|
|
392
|
+
code: "COMPONENT_REF_REQUIRED"
|
|
393
|
+
});
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (singleComp && multiComp) {
|
|
397
|
+
errors.push({
|
|
398
|
+
path,
|
|
399
|
+
message: "Component field cannot specify both 'component' and 'components'. Use 'component' for single component or 'components' for dynamic zone",
|
|
400
|
+
code: "COMPONENT_REF_CONFLICT"
|
|
401
|
+
});
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (singleComp !== void 0 && singleComp !== null) {
|
|
405
|
+
if (typeof singleComp !== "string") {
|
|
406
|
+
errors.push({
|
|
407
|
+
path: `${path}.component`,
|
|
408
|
+
message: "'component' must be a string (component slug)",
|
|
409
|
+
code: "COMPONENT_REF_INVALID"
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (!Array.isArray(multiComp)) {
|
|
415
|
+
errors.push({
|
|
416
|
+
path: `${path}.components`,
|
|
417
|
+
message: "'components' must be an array of component slugs",
|
|
418
|
+
code: "COMPONENT_REF_INVALID"
|
|
419
|
+
});
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
if (multiComp.length === 0) {
|
|
423
|
+
errors.push({
|
|
424
|
+
path: `${path}.components`,
|
|
425
|
+
message: "'components' array must have at least one component slug",
|
|
426
|
+
code: "COMPONENT_REF_EMPTY"
|
|
427
|
+
});
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
multiComp.forEach((slug, index) => {
|
|
431
|
+
if (typeof slug !== "string") {
|
|
432
|
+
errors.push({
|
|
433
|
+
path: `${path}.components[${index}]`,
|
|
434
|
+
message: "Each component slug must be a string",
|
|
435
|
+
code: "COMPONENT_REF_INVALID"
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// src/collections/config/validate-config.ts
|
|
442
|
+
var RESERVED_SLUGS_SET = new Set(RESERVED_SLUGS);
|
|
443
|
+
var INDEX_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
444
|
+
function extractFieldNames(fields, prefix = "") {
|
|
445
|
+
const names = /* @__PURE__ */ new Set();
|
|
446
|
+
for (const field of fields) {
|
|
447
|
+
if (!field || typeof field !== "object") continue;
|
|
448
|
+
const f = field;
|
|
449
|
+
const name = f.name;
|
|
450
|
+
if (typeof name === "string") {
|
|
451
|
+
const fullName = prefix ? `${prefix}.${name}` : name;
|
|
452
|
+
names.add(name);
|
|
453
|
+
names.add(fullName);
|
|
454
|
+
}
|
|
455
|
+
if (Array.isArray(f.fields)) {
|
|
456
|
+
const nestedPrefix = typeof name === "string" ? prefix ? `${prefix}.${name}` : name : prefix;
|
|
457
|
+
const nestedNames = extractFieldNames(
|
|
458
|
+
f.fields,
|
|
459
|
+
nestedPrefix
|
|
460
|
+
);
|
|
461
|
+
nestedNames.forEach((n) => names.add(n));
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return names;
|
|
465
|
+
}
|
|
466
|
+
function validateIndexes(indexes, fields, errors) {
|
|
467
|
+
if (!indexes) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (!Array.isArray(indexes)) {
|
|
471
|
+
errors.push({
|
|
472
|
+
path: "indexes",
|
|
473
|
+
message: "Indexes must be an array",
|
|
474
|
+
code: "INDEX_INVALID_TYPE"
|
|
475
|
+
});
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const validFieldNames = extractFieldNames(fields);
|
|
479
|
+
validFieldNames.add("id");
|
|
480
|
+
validFieldNames.add("createdAt");
|
|
481
|
+
validFieldNames.add("updatedAt");
|
|
482
|
+
indexes.forEach((index, idx) => {
|
|
483
|
+
const indexPath = `indexes[${idx}]`;
|
|
484
|
+
if (!index || typeof index !== "object") {
|
|
485
|
+
errors.push({
|
|
486
|
+
path: indexPath,
|
|
487
|
+
message: "Each index must be an object",
|
|
488
|
+
code: "INDEX_INVALID_TYPE"
|
|
489
|
+
});
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const i = index;
|
|
493
|
+
if (!i.fields) {
|
|
494
|
+
errors.push({
|
|
495
|
+
path: `${indexPath}.fields`,
|
|
496
|
+
message: "Index must specify 'fields' array",
|
|
497
|
+
code: "INDEX_FIELDS_REQUIRED"
|
|
498
|
+
});
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
if (!Array.isArray(i.fields)) {
|
|
502
|
+
errors.push({
|
|
503
|
+
path: `${indexPath}.fields`,
|
|
504
|
+
message: "Index 'fields' must be an array",
|
|
505
|
+
code: "INDEX_FIELDS_REQUIRED"
|
|
506
|
+
});
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (i.fields.length === 0) {
|
|
510
|
+
errors.push({
|
|
511
|
+
path: `${indexPath}.fields`,
|
|
512
|
+
message: "Index must specify at least one field",
|
|
513
|
+
code: "INDEX_FIELDS_EMPTY"
|
|
514
|
+
});
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
i.fields.forEach((fieldName, fieldIdx) => {
|
|
518
|
+
if (typeof fieldName !== "string") {
|
|
519
|
+
errors.push({
|
|
520
|
+
path: `${indexPath}.fields[${fieldIdx}]`,
|
|
521
|
+
message: "Index field must be a string",
|
|
522
|
+
code: "INDEX_FIELD_UNKNOWN"
|
|
523
|
+
});
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
if (!validFieldNames.has(fieldName)) {
|
|
527
|
+
errors.push({
|
|
528
|
+
path: `${indexPath}.fields[${fieldIdx}]`,
|
|
529
|
+
message: `Unknown field '${fieldName}' in index. Available fields: ${Array.from(validFieldNames).sort().join(", ")}`,
|
|
530
|
+
code: "INDEX_FIELD_UNKNOWN"
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
if (i.name !== void 0) {
|
|
535
|
+
if (typeof i.name !== "string") {
|
|
536
|
+
errors.push({
|
|
537
|
+
path: `${indexPath}.name`,
|
|
538
|
+
message: "Index name must be a string",
|
|
539
|
+
code: "INDEX_NAME_INVALID"
|
|
540
|
+
});
|
|
541
|
+
} else if (!INDEX_NAME_PATTERN.test(i.name)) {
|
|
542
|
+
errors.push({
|
|
543
|
+
path: `${indexPath}.name`,
|
|
544
|
+
message: `Invalid index name '${i.name}'. Must start with a letter and contain only letters, numbers, and underscores`,
|
|
545
|
+
code: "INDEX_NAME_INVALID"
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
if (i.unique !== void 0 && typeof i.unique !== "boolean") {
|
|
550
|
+
errors.push({
|
|
551
|
+
path: `${indexPath}.unique`,
|
|
552
|
+
message: "Index 'unique' must be a boolean",
|
|
553
|
+
code: "INDEX_INVALID_TYPE"
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
function validateField(field, path, errors, seenNames, allCollectionSlugs) {
|
|
559
|
+
if (!field || typeof field !== "object") {
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const f = field;
|
|
563
|
+
const errsBase = errors;
|
|
564
|
+
if (!validateFieldTypeShared(f.type, path, errsBase)) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const fieldType = f.type;
|
|
568
|
+
validateFieldNameShared(
|
|
569
|
+
f.name,
|
|
570
|
+
path,
|
|
571
|
+
errsBase,
|
|
572
|
+
seenNames,
|
|
573
|
+
DEFAULT_SQL_KEYWORDS_SET
|
|
574
|
+
);
|
|
575
|
+
switch (fieldType) {
|
|
576
|
+
case "select":
|
|
577
|
+
validateSelectOptionsShared(f, path, errsBase, "select");
|
|
578
|
+
break;
|
|
579
|
+
case "radio":
|
|
580
|
+
validateSelectOptionsShared(f, path, errsBase, "radio");
|
|
581
|
+
break;
|
|
582
|
+
case "relationship":
|
|
583
|
+
validateRelationshipTargetShared(f, path, errsBase, allCollectionSlugs);
|
|
584
|
+
break;
|
|
585
|
+
case "repeater": {
|
|
586
|
+
const arrayFields = f.fields;
|
|
587
|
+
if (!arrayFields) {
|
|
588
|
+
errors.push({
|
|
589
|
+
path: `${path}.fields`,
|
|
590
|
+
message: "Array field must have a 'fields' array",
|
|
591
|
+
code: "ARRAY_FIELDS_REQUIRED"
|
|
592
|
+
});
|
|
593
|
+
} else if (Array.isArray(arrayFields)) {
|
|
594
|
+
validateFieldsArray(
|
|
595
|
+
arrayFields,
|
|
596
|
+
`${path}.fields`,
|
|
597
|
+
errors,
|
|
598
|
+
allCollectionSlugs
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
case "group": {
|
|
604
|
+
const groupFields = f.fields;
|
|
605
|
+
if (!groupFields) {
|
|
606
|
+
errors.push({
|
|
607
|
+
path: `${path}.fields`,
|
|
608
|
+
message: "Group field must have a 'fields' array",
|
|
609
|
+
code: "GROUP_FIELDS_REQUIRED"
|
|
610
|
+
});
|
|
611
|
+
} else if (Array.isArray(groupFields)) {
|
|
612
|
+
validateFieldsArray(
|
|
613
|
+
groupFields,
|
|
614
|
+
`${path}.fields`,
|
|
615
|
+
errors,
|
|
616
|
+
allCollectionSlugs
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
case "component":
|
|
622
|
+
validateComponentFieldRefShared(f, path, errsBase);
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
function validateFieldsArray(fields, basePath, errors, allCollectionSlugs) {
|
|
627
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
628
|
+
fields.forEach((field, index) => {
|
|
629
|
+
const fieldPath = `${basePath}[${index}]`;
|
|
630
|
+
validateField(field, fieldPath, errors, seenNames, allCollectionSlugs);
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
function validateFields(fields, errors, allCollectionSlugs) {
|
|
634
|
+
const path = "fields";
|
|
635
|
+
if (!fields) {
|
|
636
|
+
errors.push({
|
|
637
|
+
path,
|
|
638
|
+
message: "Collection fields are required",
|
|
639
|
+
code: "FIELDS_REQUIRED"
|
|
640
|
+
});
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (!Array.isArray(fields)) {
|
|
644
|
+
errors.push({
|
|
645
|
+
path,
|
|
646
|
+
message: "Collection fields must be an array",
|
|
647
|
+
code: "FIELDS_INVALID_TYPE"
|
|
648
|
+
});
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
if (fields.length === 0) {
|
|
652
|
+
errors.push({
|
|
653
|
+
path,
|
|
654
|
+
message: "Collection must have at least one field",
|
|
655
|
+
code: "FIELDS_EMPTY"
|
|
656
|
+
});
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
validateFieldsArray(fields, path, errors, allCollectionSlugs);
|
|
660
|
+
}
|
|
661
|
+
function validateAccess(access, errors) {
|
|
662
|
+
if (!access) {
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
if (typeof access !== "object" || Array.isArray(access)) {
|
|
666
|
+
errors.push({
|
|
667
|
+
path: "access",
|
|
668
|
+
message: "Access control must be an object",
|
|
669
|
+
code: "ACCESS_INVALID_TYPE"
|
|
670
|
+
});
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
const accessObj = access;
|
|
674
|
+
const validAccessKeys = ["create", "read", "update", "delete"];
|
|
675
|
+
for (const key of validAccessKeys) {
|
|
676
|
+
const fn = accessObj[key];
|
|
677
|
+
if (fn !== void 0 && typeof fn !== "function") {
|
|
678
|
+
errors.push({
|
|
679
|
+
path: `access.${key}`,
|
|
680
|
+
message: `Access control '${key}' must be a function`,
|
|
681
|
+
code: "ACCESS_FUNCTION_INVALID"
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
function validateCollectionConfig(config, allCollectionSlugs) {
|
|
687
|
+
const errors = [];
|
|
688
|
+
const errsBase = errors;
|
|
689
|
+
validateSlugShared(config.slug, errsBase, {
|
|
690
|
+
entityLabel: "Collection",
|
|
691
|
+
reservedSlugsSet: RESERVED_SLUGS_SET,
|
|
692
|
+
sqlKeywordsSet: DEFAULT_SQL_KEYWORDS_SET
|
|
693
|
+
});
|
|
694
|
+
validateFields(config.fields, errors, allCollectionSlugs);
|
|
695
|
+
validateIndexes(config.indexes, config.fields, errors);
|
|
696
|
+
validateAccess(config.access, errors);
|
|
697
|
+
return {
|
|
698
|
+
valid: errors.length === 0,
|
|
699
|
+
errors
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function assertValidCollectionConfig(config, allCollectionSlugs) {
|
|
703
|
+
const result = validateCollectionConfig(config, allCollectionSlugs);
|
|
704
|
+
if (!result.valid) {
|
|
705
|
+
const errorMessages = result.errors.map((err) => ` - [${err.code}] ${err.path}: ${err.message}`).join("\n");
|
|
706
|
+
throw new Error(
|
|
707
|
+
`Invalid collection config for '${config.slug || "unknown"}':
|
|
708
|
+
${errorMessages}`
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// src/components/config/validate-component.ts
|
|
714
|
+
var MAX_COMPONENT_NESTING_DEPTH = 3;
|
|
715
|
+
var RESERVED_COMPONENT_SLUGS = [
|
|
716
|
+
...RESERVED_SLUGS,
|
|
717
|
+
// API namespace for Components
|
|
718
|
+
"components",
|
|
719
|
+
"component"
|
|
720
|
+
];
|
|
721
|
+
var RESERVED_COMPONENT_SLUGS_SET = new Set(
|
|
722
|
+
RESERVED_COMPONENT_SLUGS
|
|
723
|
+
);
|
|
724
|
+
function validateField2(field, path, errors, seenNames) {
|
|
725
|
+
if (!field || typeof field !== "object") {
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const f = field;
|
|
729
|
+
const errsBase = errors;
|
|
730
|
+
if (!validateFieldTypeShared(f.type, path, errsBase)) {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
const fieldType = f.type;
|
|
734
|
+
validateFieldNameShared(
|
|
735
|
+
f.name,
|
|
736
|
+
path,
|
|
737
|
+
errsBase,
|
|
738
|
+
seenNames,
|
|
739
|
+
DEFAULT_SQL_KEYWORDS_SET
|
|
740
|
+
);
|
|
741
|
+
switch (fieldType) {
|
|
742
|
+
case "select":
|
|
743
|
+
validateSelectOptionsShared(f, path, errsBase, "select");
|
|
744
|
+
break;
|
|
745
|
+
case "radio":
|
|
746
|
+
validateSelectOptionsShared(f, path, errsBase, "radio");
|
|
747
|
+
break;
|
|
748
|
+
case "relationship":
|
|
749
|
+
validateRelationshipTargetShared(f, path, errsBase);
|
|
750
|
+
break;
|
|
751
|
+
case "repeater": {
|
|
752
|
+
const arrayFields = f.fields;
|
|
753
|
+
if (!arrayFields) {
|
|
754
|
+
errors.push({
|
|
755
|
+
path: `${path}.fields`,
|
|
756
|
+
message: "Array field must have a 'fields' array",
|
|
757
|
+
code: "ARRAY_FIELDS_REQUIRED"
|
|
758
|
+
});
|
|
759
|
+
} else if (Array.isArray(arrayFields)) {
|
|
760
|
+
validateFieldsArray2(arrayFields, `${path}.fields`, errors);
|
|
761
|
+
}
|
|
762
|
+
break;
|
|
763
|
+
}
|
|
764
|
+
case "group": {
|
|
765
|
+
const groupFields = f.fields;
|
|
766
|
+
if (!groupFields) {
|
|
767
|
+
errors.push({
|
|
768
|
+
path: `${path}.fields`,
|
|
769
|
+
message: "Group field must have a 'fields' array",
|
|
770
|
+
code: "GROUP_FIELDS_REQUIRED"
|
|
771
|
+
});
|
|
772
|
+
} else if (Array.isArray(groupFields)) {
|
|
773
|
+
validateFieldsArray2(groupFields, `${path}.fields`, errors);
|
|
774
|
+
}
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
case "component":
|
|
778
|
+
validateComponentFieldRefShared(f, path, errsBase);
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
function validateFieldsArray2(fields, basePath, errors) {
|
|
783
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
784
|
+
fields.forEach((field, index) => {
|
|
785
|
+
const fieldPath = `${basePath}[${index}]`;
|
|
786
|
+
validateField2(field, fieldPath, errors, seenNames);
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
function validateFields2(fields, errors) {
|
|
790
|
+
const path = "fields";
|
|
791
|
+
if (!fields) {
|
|
792
|
+
errors.push({
|
|
793
|
+
path,
|
|
794
|
+
message: "Component fields are required",
|
|
795
|
+
code: "FIELDS_REQUIRED"
|
|
796
|
+
});
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
if (!Array.isArray(fields)) {
|
|
800
|
+
errors.push({
|
|
801
|
+
path,
|
|
802
|
+
message: "Component fields must be an array",
|
|
803
|
+
code: "FIELDS_INVALID_TYPE"
|
|
804
|
+
});
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
validateFieldsArray2(fields, path, errors);
|
|
808
|
+
}
|
|
809
|
+
function validateComponentConfig(config) {
|
|
810
|
+
const errors = [];
|
|
811
|
+
const errsBase = errors;
|
|
812
|
+
validateSlugShared(config.slug, errsBase, {
|
|
813
|
+
entityLabel: "Component",
|
|
814
|
+
reservedSlugsSet: RESERVED_COMPONENT_SLUGS_SET,
|
|
815
|
+
sqlKeywordsSet: DEFAULT_SQL_KEYWORDS_SET
|
|
816
|
+
});
|
|
817
|
+
validateFields2(config.fields, errors);
|
|
818
|
+
return {
|
|
819
|
+
valid: errors.length === 0,
|
|
820
|
+
errors
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
function assertValidComponentConfig(config) {
|
|
824
|
+
const result = validateComponentConfig(config);
|
|
825
|
+
if (!result.valid) {
|
|
826
|
+
const errorMessages = result.errors.map((err) => ` - [${err.code}] ${err.path}: ${err.message}`).join("\n");
|
|
827
|
+
throw new Error(
|
|
828
|
+
`Invalid Component config for '${config.slug || "unknown"}':
|
|
829
|
+
${errorMessages}`
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
export {
|
|
835
|
+
RESERVED_SLUGS,
|
|
836
|
+
SQL_RESERVED_KEYWORDS,
|
|
837
|
+
DEFAULT_SQL_KEYWORDS_SET,
|
|
838
|
+
validateSlugShared,
|
|
839
|
+
validateFieldNameShared,
|
|
840
|
+
validateFieldTypeShared,
|
|
841
|
+
validateSelectOptionsShared,
|
|
842
|
+
validateRelationshipTargetShared,
|
|
843
|
+
validateComponentFieldRefShared,
|
|
844
|
+
validateCollectionConfig,
|
|
845
|
+
assertValidCollectionConfig,
|
|
846
|
+
MAX_COMPONENT_NESTING_DEPTH,
|
|
847
|
+
RESERVED_COMPONENT_SLUGS,
|
|
848
|
+
validateComponentConfig,
|
|
849
|
+
assertValidComponentConfig
|
|
850
|
+
};
|