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.
Files changed (268) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +122 -0
  3. package/dist/_dts-chunks/collections-handler.d-DjgO74Wt.d.ts +20540 -0
  4. package/dist/_dts-chunks/config.d-DNwsDnjs.d.ts +2589 -0
  5. package/dist/_dts-chunks/define-component.d-BUgTHmt3.d.ts +1149 -0
  6. package/dist/_dts-chunks/image-processor.d-OO1PmMrv.d.ts +335 -0
  7. package/dist/_dts-chunks/index.d-axCAzZ7m.d.ts +17842 -0
  8. package/dist/_dts-chunks/media.d-DjDOZo4B.d.ts +117 -0
  9. package/dist/_dts-chunks/on-error.d-CHIKWNxd.d.ts +38 -0
  10. package/dist/_dts-chunks/storage.d-BUhQ2we_.d.ts +404 -0
  11. package/dist/actions/index.d.ts +239 -0
  12. package/dist/actions/index.mjs +281 -0
  13. package/dist/api/auth-state.d.ts +5 -0
  14. package/dist/api/auth-state.mjs +131 -0
  15. package/dist/api/collections-schema-detail.d.ts +56 -0
  16. package/dist/api/collections-schema-detail.mjs +244 -0
  17. package/dist/api/collections-schema-export.d.ts +56 -0
  18. package/dist/api/collections-schema-export.mjs +129 -0
  19. package/dist/api/collections-schema.d.ts +59 -0
  20. package/dist/api/collections-schema.mjs +207 -0
  21. package/dist/api/components-detail.d.ts +50 -0
  22. package/dist/api/components-detail.mjs +132 -0
  23. package/dist/api/components.d.ts +69 -0
  24. package/dist/api/components.mjs +144 -0
  25. package/dist/api/email-providers-default.d.ts +40 -0
  26. package/dist/api/email-providers-default.mjs +75 -0
  27. package/dist/api/email-providers-detail.d.ts +81 -0
  28. package/dist/api/email-providers-detail.mjs +109 -0
  29. package/dist/api/email-providers-test.d.ts +43 -0
  30. package/dist/api/email-providers-test.mjs +114 -0
  31. package/dist/api/email-providers.d.ts +69 -0
  32. package/dist/api/email-providers.mjs +110 -0
  33. package/dist/api/email-send-template.d.ts +41 -0
  34. package/dist/api/email-send-template.mjs +58 -0
  35. package/dist/api/email-send.d.ts +42 -0
  36. package/dist/api/email-send.mjs +58 -0
  37. package/dist/api/email-templates-detail.d.ts +74 -0
  38. package/dist/api/email-templates-detail.mjs +112 -0
  39. package/dist/api/email-templates-layout.d.ts +55 -0
  40. package/dist/api/email-templates-layout.mjs +92 -0
  41. package/dist/api/email-templates-preview.d.ts +48 -0
  42. package/dist/api/email-templates-preview.mjs +93 -0
  43. package/dist/api/email-templates.d.ts +61 -0
  44. package/dist/api/email-templates.mjs +118 -0
  45. package/dist/api/health.d.ts +68 -0
  46. package/dist/api/health.mjs +67 -0
  47. package/dist/api/index.d.ts +54 -0
  48. package/dist/api/index.mjs +16 -0
  49. package/dist/api/media-bulk.d.ts +74 -0
  50. package/dist/api/media-bulk.mjs +196 -0
  51. package/dist/api/media-folders.d.ts +112 -0
  52. package/dist/api/media-folders.mjs +187 -0
  53. package/dist/api/media-handlers.d.ts +102 -0
  54. package/dist/api/media-handlers.mjs +437 -0
  55. package/dist/api/media.d.ts +117 -0
  56. package/dist/api/media.mjs +242 -0
  57. package/dist/api/singles-detail.d.ts +87 -0
  58. package/dist/api/singles-detail.mjs +170 -0
  59. package/dist/api/singles-schema-detail.d.ts +54 -0
  60. package/dist/api/singles-schema-detail.mjs +182 -0
  61. package/dist/api/singles.d.ts +34 -0
  62. package/dist/api/singles.mjs +94 -0
  63. package/dist/api/storage-upload-url.d.ts +48 -0
  64. package/dist/api/storage-upload-url.mjs +202 -0
  65. package/dist/api/uploads.d.ts +109 -0
  66. package/dist/api/uploads.mjs +359 -0
  67. package/dist/auth/index.d.ts +425 -0
  68. package/dist/auth/index.mjs +199 -0
  69. package/dist/boot-apply-PQSYLDIN.mjs +7 -0
  70. package/dist/chunk-2OALJTK6.mjs +489 -0
  71. package/dist/chunk-2Q2SX2CS.mjs +365 -0
  72. package/dist/chunk-2TFX4ND3.mjs +13 -0
  73. package/dist/chunk-2TWPDSYD.mjs +87 -0
  74. package/dist/chunk-2W3DVD7S.mjs +647 -0
  75. package/dist/chunk-2ZFKXPQM.mjs +88 -0
  76. package/dist/chunk-3FA7FKAV.mjs +832 -0
  77. package/dist/chunk-3NZ2KMBL.mjs +58 -0
  78. package/dist/chunk-4MJLT6PZ.mjs +0 -0
  79. package/dist/chunk-56WO4WX7.mjs +0 -0
  80. package/dist/chunk-5APFUGAD.mjs +89 -0
  81. package/dist/chunk-5HMZ644B.mjs +108 -0
  82. package/dist/chunk-67GXH6PR.mjs +32 -0
  83. package/dist/chunk-6JNEPWRW.mjs +14368 -0
  84. package/dist/chunk-6NFHQIJD.mjs +45 -0
  85. package/dist/chunk-7P6ASYW6.mjs +9 -0
  86. package/dist/chunk-A3WPLSDT.mjs +1364 -0
  87. package/dist/chunk-AGJ6F2T3.mjs +144 -0
  88. package/dist/chunk-AK6Z23OX.mjs +1464 -0
  89. package/dist/chunk-APKKRD2G.mjs +102 -0
  90. package/dist/chunk-B2GV2BWH.mjs +73 -0
  91. package/dist/chunk-D5HQBNUB.mjs +74 -0
  92. package/dist/chunk-DNNG377Z.mjs +204 -0
  93. package/dist/chunk-DP3G27G5.mjs +135 -0
  94. package/dist/chunk-DV6WVX2Q.mjs +0 -0
  95. package/dist/chunk-DXGGXIUZ.mjs +57 -0
  96. package/dist/chunk-EGXBZCGC.mjs +943 -0
  97. package/dist/chunk-ERCNLX3V.mjs +176 -0
  98. package/dist/chunk-FQULBZ53.mjs +850 -0
  99. package/dist/chunk-G2AA4QLC.mjs +262 -0
  100. package/dist/chunk-GDBJ5JCU.mjs +488 -0
  101. package/dist/chunk-GJNSJU4S.mjs +19 -0
  102. package/dist/chunk-GZ6DCQKC.mjs +69 -0
  103. package/dist/chunk-H26B4FYG.mjs +167 -0
  104. package/dist/chunk-I4JMR3UR.mjs +21 -0
  105. package/dist/chunk-INV7QKLG.mjs +508 -0
  106. package/dist/chunk-IUDOC7N7.mjs +46 -0
  107. package/dist/chunk-IZWPRDC3.mjs +206 -0
  108. package/dist/chunk-KIMNCZGV.mjs +15 -0
  109. package/dist/chunk-L6HW2DA7.mjs +15 -0
  110. package/dist/chunk-LAZXX4HR.mjs +100 -0
  111. package/dist/chunk-LDKCUMHK.mjs +95 -0
  112. package/dist/chunk-LRXMECUA.mjs +0 -0
  113. package/dist/chunk-M52VMPGA.mjs +119 -0
  114. package/dist/chunk-MGUWEEI6.mjs +160 -0
  115. package/dist/chunk-NRUWQ5Z7.mjs +419 -0
  116. package/dist/chunk-NSEFNNU4.mjs +25360 -0
  117. package/dist/chunk-NTHVDFGO.mjs +138 -0
  118. package/dist/chunk-O3QHXMOX.mjs +3166 -0
  119. package/dist/chunk-P7NH2OSC.mjs +2605 -0
  120. package/dist/chunk-PKMABBB5.mjs +184 -0
  121. package/dist/chunk-PWS6XGJK.mjs +76 -0
  122. package/dist/chunk-R6JJQHFC.mjs +20 -0
  123. package/dist/chunk-RJLLGGPG.mjs +0 -0
  124. package/dist/chunk-SBACDPNX.mjs +689 -0
  125. package/dist/chunk-TO5AFLVQ.mjs +124 -0
  126. package/dist/chunk-TS7GHTG2.mjs +5436 -0
  127. package/dist/chunk-UJ2IMJ4W.mjs +133 -0
  128. package/dist/chunk-UOP63Q54.mjs +102 -0
  129. package/dist/chunk-UUOFWCM6.mjs +78 -0
  130. package/dist/chunk-V4EQTOA4.mjs +893 -0
  131. package/dist/chunk-VJ66NCL4.mjs +193 -0
  132. package/dist/chunk-VQJQHVEV.mjs +29 -0
  133. package/dist/chunk-VTJADRO3.mjs +141 -0
  134. package/dist/chunk-VWF3JO32.mjs +0 -0
  135. package/dist/chunk-W4MGXIRR.mjs +27 -0
  136. package/dist/chunk-W5KKPZT5.mjs +1204 -0
  137. package/dist/chunk-WD34YQ6T.mjs +381 -0
  138. package/dist/chunk-WZBYMYVW.mjs +14 -0
  139. package/dist/chunk-X23WKS3Z.mjs +50 -0
  140. package/dist/chunk-X7TXCYYN.mjs +6496 -0
  141. package/dist/chunk-XGI4EMS3.mjs +140 -0
  142. package/dist/chunk-XZKLBMN6.mjs +1153 -0
  143. package/dist/chunk-YB7INWPY.mjs +0 -0
  144. package/dist/chunk-YV4Y7SDL.mjs +83 -0
  145. package/dist/chunk-YZNBLFIW.mjs +1688 -0
  146. package/dist/chunk-YZZCTONM.mjs +263 -0
  147. package/dist/chunk-ZE6A3FYH.mjs +289 -0
  148. package/dist/cli/nextly.mjs +68 -0
  149. package/dist/cli/utils/index.d.ts +449 -0
  150. package/dist/cli/utils/index.mjs +49 -0
  151. package/dist/component-schema-service-5577KVW6.mjs +11 -0
  152. package/dist/config-loader-23YEMC3Z.mjs +23 -0
  153. package/dist/config.d.ts +44 -0
  154. package/dist/config.mjs +109 -0
  155. package/dist/container-ORGFGYSZ.mjs +9 -0
  156. package/dist/database/index.d.ts +12 -0
  157. package/dist/database/index.mjs +40 -0
  158. package/dist/database/seeders/index.d.ts +93 -0
  159. package/dist/database/seeders/index.mjs +47 -0
  160. package/dist/db-sync-demote-LJGKLB3S.mjs +117 -0
  161. package/dist/db-sync-promote-B26VSYQF.mjs +113 -0
  162. package/dist/dev-reload-broadcaster-B73IQ53V.mjs +25 -0
  163. package/dist/dist-M2NOU37V.mjs +19 -0
  164. package/dist/drizzle-kit-lazy-D2M2PXR2.mjs +13 -0
  165. package/dist/dynamic-collection-schema-service-IEXTPIZ7.mjs +8 -0
  166. package/dist/errors/index.d.ts +159 -0
  167. package/dist/errors/index.mjs +10 -0
  168. package/dist/factory-IWMBKUJM.mjs +15 -0
  169. package/dist/first-run-QIVKWJIF.mjs +63 -0
  170. package/dist/fresh-push-NR67DC3R.mjs +8 -0
  171. package/dist/index.d.ts +4175 -0
  172. package/dist/index.mjs +1336 -0
  173. package/dist/local-plugin-PTET4NAT.mjs +7 -0
  174. package/dist/logger-NU46DXNY.mjs +15 -0
  175. package/dist/logger-YE4TC7ZN.mjs +9 -0
  176. package/dist/migration-journal-EP532Y4L.mjs +139 -0
  177. package/dist/migrations/mysql/0000_eager_sentry.sql +174 -0
  178. package/dist/migrations/mysql/0001_soft_giant_girl.sql +27 -0
  179. package/dist/migrations/mysql/0002_media_table.sql +24 -0
  180. package/dist/migrations/mysql/0003_dynamic_singles.sql +37 -0
  181. package/dist/migrations/mysql/0004_dynamic_components.sql +35 -0
  182. package/dist/migrations/mysql/0005_user_management_tables.sql +92 -0
  183. package/dist/migrations/mysql/0006_api_keys.sql +36 -0
  184. package/dist/migrations/mysql/0007_general_settings.sql +20 -0
  185. package/dist/migrations/mysql/0008_site_settings_logo_url.sql +9 -0
  186. package/dist/migrations/mysql/0009_activity_log.sql +30 -0
  187. package/dist/migrations/mysql/0010_site_settings_sidebar.sql +13 -0
  188. package/dist/migrations/mysql/0011_missing_tables_and_columns.sql +54 -0
  189. package/dist/migrations/mysql/0012_image_sizes_and_focal_point.sql +30 -0
  190. package/dist/migrations/mysql/0012_media_folders.sql +43 -0
  191. package/dist/migrations/mysql/0013_user_brute_force_protection.sql +31 -0
  192. package/dist/migrations/mysql/0014_email_template_attachments.sql +12 -0
  193. package/dist/migrations/mysql/0015_media_uploaded_by_nullable.sql +15 -0
  194. package/dist/migrations/mysql/20260429_000000_000_initial_journal.sql +22 -0
  195. package/dist/migrations/mysql/20260501_000000_journal_batch.sql +17 -0
  196. package/dist/migrations/mysql/20260501_000001_audit_log.sql +24 -0
  197. package/dist/migrations/mysql/20260504_000000_nextly_meta.sql +21 -0
  198. package/dist/migrations/mysql/meta/0000_snapshot.json +1005 -0
  199. package/dist/migrations/mysql/meta/0001_snapshot.json +1099 -0
  200. package/dist/migrations/mysql/meta/_journal.json +41 -0
  201. package/dist/migrations/postgresql/0000_misty_king_bedlam.sql +169 -0
  202. package/dist/migrations/postgresql/0001_perpetual_captain_marvel.sql +8 -0
  203. package/dist/migrations/postgresql/0002_sad_spectrum.sql +16 -0
  204. package/dist/migrations/postgresql/0003_hesitant_ultron.sql +17 -0
  205. package/dist/migrations/postgresql/0004_media_table.sql +24 -0
  206. package/dist/migrations/postgresql/0005_media_folders.sql +36 -0
  207. package/dist/migrations/postgresql/0006_dynamic_collections_update.sql +50 -0
  208. package/dist/migrations/postgresql/0007_dynamic_singles.sql +38 -0
  209. package/dist/migrations/postgresql/0008_dynamic_components.sql +37 -0
  210. package/dist/migrations/postgresql/0009_user_management_tables.sql +95 -0
  211. package/dist/migrations/postgresql/0010_api_keys.sql +34 -0
  212. package/dist/migrations/postgresql/0011_general_settings.sql +20 -0
  213. package/dist/migrations/postgresql/0012_site_settings_logo_url.sql +9 -0
  214. package/dist/migrations/postgresql/0013_activity_log.sql +29 -0
  215. package/dist/migrations/postgresql/0014_image_sizes_and_focal_point.sql +33 -0
  216. package/dist/migrations/postgresql/0014_site_settings_sidebar.sql +13 -0
  217. package/dist/migrations/postgresql/0015_user_brute_force_protection.sql +29 -0
  218. package/dist/migrations/postgresql/0016_email_template_attachments.sql +12 -0
  219. package/dist/migrations/postgresql/0017_media_uploaded_by_nullable.sql +15 -0
  220. package/dist/migrations/postgresql/20260429_000000_000_initial_journal.sql +24 -0
  221. package/dist/migrations/postgresql/20260501_000000_journal_batch.sql +17 -0
  222. package/dist/migrations/postgresql/20260501_000001_audit_log.sql +24 -0
  223. package/dist/migrations/postgresql/20260504_000000_nextly_meta.sql +22 -0
  224. package/dist/migrations/postgresql/meta/0000_snapshot.json +1286 -0
  225. package/dist/migrations/postgresql/meta/0001_snapshot.json +1407 -0
  226. package/dist/migrations/postgresql/meta/0002_snapshot.json +1552 -0
  227. package/dist/migrations/postgresql/meta/0003_snapshot.json +1695 -0
  228. package/dist/migrations/postgresql/meta/0010_snapshot.json +2345 -0
  229. package/dist/migrations/postgresql/meta/_journal.json +90 -0
  230. package/dist/migrations/sqlite/0000_api_keys.sql +34 -0
  231. package/dist/migrations/sqlite/0001_general_settings.sql +20 -0
  232. package/dist/migrations/sqlite/0002_site_settings_logo_url.sql +9 -0
  233. package/dist/migrations/sqlite/0003_activity_log.sql +29 -0
  234. package/dist/migrations/sqlite/0004_image_sizes_and_focal_point.sql +29 -0
  235. package/dist/migrations/sqlite/0004_site_settings_sidebar.sql +11 -0
  236. package/dist/migrations/sqlite/0005_user_brute_force_protection.sql +29 -0
  237. package/dist/migrations/sqlite/0006_email_template_attachments.sql +12 -0
  238. package/dist/migrations/sqlite/0007_media_uploaded_by_nullable.sql +111 -0
  239. package/dist/migrations/sqlite/20260429_000000_000_initial_journal.sql +24 -0
  240. package/dist/migrations/sqlite/20260501_000000_journal_batch.sql +19 -0
  241. package/dist/migrations/sqlite/20260501_000001_audit_log.sql +24 -0
  242. package/dist/migrations/sqlite/20260504_000000_nextly_meta.sql +21 -0
  243. package/dist/migrations/sqlite/20260505_000000_user_management_tables.sql +77 -0
  244. package/dist/next.d.ts +57 -0
  245. package/dist/next.mjs +55 -0
  246. package/dist/observability/index.d.ts +87 -0
  247. package/dist/observability/index.mjs +57 -0
  248. package/dist/permissions-3DZZQZMI.mjs +39 -0
  249. package/dist/pipeline-YOML7SWF.mjs +29 -0
  250. package/dist/preview-ZZTR3QGS.mjs +9 -0
  251. package/dist/program-PW6UB2ZC.mjs +5934 -0
  252. package/dist/reconcile-single-tables-7ENVXJGB.mjs +7 -0
  253. package/dist/register-SF6E6FVU.mjs +49 -0
  254. package/dist/reload-config-HWQ4G5MM.mjs +23 -0
  255. package/dist/resolve-single-table-name-JSOMUB3R.mjs +7 -0
  256. package/dist/routeHandler-UNMMJIBM.mjs +77 -0
  257. package/dist/runtime-schema-generator-NRA6A6Z6.mjs +8 -0
  258. package/dist/runtime.d.ts +120 -0
  259. package/dist/runtime.mjs +73 -0
  260. package/dist/schema-hash-FMMG6VPJ.mjs +13 -0
  261. package/dist/schema-registry-EQ36FZDP.mjs +7 -0
  262. package/dist/scripts/load-env.mjs +42 -0
  263. package/dist/storage/index.d.ts +566 -0
  264. package/dist/storage/index.mjs +45 -0
  265. package/dist/super-admin-G5ZK5F4T.mjs +39 -0
  266. package/dist/system-table-service-WGSRVEGT.mjs +17 -0
  267. package/dist/users-7KELGRYJ.mjs +38 -0
  268. 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
+ };