@vexcms/core 0.0.20 → 0.1.0-alpha.3

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 (323) hide show
  1. package/LICENSE +202 -0
  2. package/dist/.build +1 -0
  3. package/dist/access/buildChecks.d.ts +58 -0
  4. package/dist/access/buildChecks.d.ts.map +1 -0
  5. package/dist/access/canAccessAdminPanel.d.ts +53 -0
  6. package/dist/access/canAccessAdminPanel.d.ts.map +1 -0
  7. package/dist/access/compileConstraints.d.ts +155 -0
  8. package/dist/access/compileConstraints.d.ts.map +1 -0
  9. package/dist/access/config.d.ts +62 -0
  10. package/dist/access/config.d.ts.map +1 -0
  11. package/dist/access/constants.d.ts +95 -0
  12. package/dist/access/constants.d.ts.map +1 -0
  13. package/dist/access/constraintTypes.d.ts +337 -0
  14. package/dist/access/constraintTypes.d.ts.map +1 -0
  15. package/dist/access/createAccessQueryBuilder.d.ts +59 -0
  16. package/dist/access/createAccessQueryBuilder.d.ts.map +1 -0
  17. package/dist/access/createFilterConstraintBuilder.d.ts +26 -0
  18. package/dist/access/createFilterConstraintBuilder.d.ts.map +1 -0
  19. package/dist/access/createIndexConstraintBuilder.d.ts +40 -0
  20. package/dist/access/createIndexConstraintBuilder.d.ts.map +1 -0
  21. package/dist/access/hasPermission.d.ts +85 -0
  22. package/dist/access/hasPermission.d.ts.map +1 -0
  23. package/dist/access/index.d.ts +13 -0
  24. package/dist/access/index.d.ts.map +1 -0
  25. package/dist/access/pickQueryIndex.d.ts +42 -0
  26. package/dist/access/pickQueryIndex.d.ts.map +1 -0
  27. package/dist/access/resolveAccessRule.d.ts +85 -0
  28. package/dist/access/resolveAccessRule.d.ts.map +1 -0
  29. package/dist/access/types.d.ts +738 -0
  30. package/dist/access/types.d.ts.map +1 -0
  31. package/dist/access/validateAccessConstraints.d.ts +57 -0
  32. package/dist/access/validateAccessConstraints.d.ts.map +1 -0
  33. package/dist/api/client.d.ts +20 -0
  34. package/dist/api/client.d.ts.map +1 -0
  35. package/dist/api/client.js +117 -0
  36. package/dist/api/client.js.map +1 -0
  37. package/dist/api/convex.d.ts +296 -0
  38. package/dist/api/convex.d.ts.map +1 -0
  39. package/dist/api/create/client.d.ts +50 -0
  40. package/dist/api/create/client.d.ts.map +1 -0
  41. package/dist/api/create/server.d.ts +43 -0
  42. package/dist/api/create/server.d.ts.map +1 -0
  43. package/dist/api/depth.d.ts +39 -0
  44. package/dist/api/depth.d.ts.map +1 -0
  45. package/dist/api/find/client.d.ts +90 -0
  46. package/dist/api/find/client.d.ts.map +1 -0
  47. package/dist/api/find/server.d.ts +73 -0
  48. package/dist/api/find/server.d.ts.map +1 -0
  49. package/dist/api/get/client.d.ts +56 -0
  50. package/dist/api/get/client.d.ts.map +1 -0
  51. package/dist/api/get/server.d.ts +49 -0
  52. package/dist/api/get/server.d.ts.map +1 -0
  53. package/dist/api/globals/find.client.d.ts +20 -0
  54. package/dist/api/globals/find.client.d.ts.map +1 -0
  55. package/dist/api/globals/find.server.d.ts +29 -0
  56. package/dist/api/globals/find.server.d.ts.map +1 -0
  57. package/dist/api/globals/get.client.d.ts +46 -0
  58. package/dist/api/globals/get.client.d.ts.map +1 -0
  59. package/dist/api/globals/get.server.d.ts +55 -0
  60. package/dist/api/globals/get.server.d.ts.map +1 -0
  61. package/dist/api/globals/types.d.ts +65 -0
  62. package/dist/api/globals/types.d.ts.map +1 -0
  63. package/dist/api/globals/upsert.client.d.ts +24 -0
  64. package/dist/api/globals/upsert.client.d.ts.map +1 -0
  65. package/dist/api/globals/upsert.server.d.ts +47 -0
  66. package/dist/api/globals/upsert.server.d.ts.map +1 -0
  67. package/dist/api/populate.d.ts +23 -0
  68. package/dist/api/populate.d.ts.map +1 -0
  69. package/dist/api/remove/client.d.ts +58 -0
  70. package/dist/api/remove/client.d.ts.map +1 -0
  71. package/dist/api/remove/server.d.ts +65 -0
  72. package/dist/api/remove/server.d.ts.map +1 -0
  73. package/dist/api/search/client.d.ts +58 -0
  74. package/dist/api/search/client.d.ts.map +1 -0
  75. package/dist/api/search/server.d.ts +54 -0
  76. package/dist/api/search/server.d.ts.map +1 -0
  77. package/dist/api/server.d.ts +370 -0
  78. package/dist/api/server.d.ts.map +1 -0
  79. package/dist/api/server.js +33 -0
  80. package/dist/api/server.js.map +1 -0
  81. package/dist/api/types.d.ts +525 -0
  82. package/dist/api/types.d.ts.map +1 -0
  83. package/dist/api/update/client.d.ts +53 -0
  84. package/dist/api/update/client.d.ts.map +1 -0
  85. package/dist/api/update/server.d.ts +47 -0
  86. package/dist/api/update/server.d.ts.map +1 -0
  87. package/dist/api/utils.d.ts +57 -0
  88. package/dist/api/utils.d.ts.map +1 -0
  89. package/dist/auth/mergeCollections.d.ts +28 -0
  90. package/dist/auth/mergeCollections.d.ts.map +1 -0
  91. package/dist/auth/types.d.ts +84 -0
  92. package/dist/auth/types.d.ts.map +1 -0
  93. package/dist/chunk-2DU7JDEX.js +1877 -0
  94. package/dist/chunk-2DU7JDEX.js.map +1 -0
  95. package/dist/chunk-2IB2DCN7.js +75 -0
  96. package/dist/chunk-2IB2DCN7.js.map +1 -0
  97. package/dist/collections/config.d.ts +28 -0
  98. package/dist/collections/config.d.ts.map +1 -0
  99. package/dist/collections/constants.d.ts +27 -0
  100. package/dist/collections/constants.d.ts.map +1 -0
  101. package/dist/collections/index.d.ts +7 -0
  102. package/dist/collections/index.d.ts.map +1 -0
  103. package/dist/collections/indexFields.d.ts +20 -0
  104. package/dist/collections/indexFields.d.ts.map +1 -0
  105. package/dist/collections/interfaceGen.d.ts +59 -0
  106. package/dist/collections/interfaceGen.d.ts.map +1 -0
  107. package/dist/collections/internal/index.d.ts +2 -0
  108. package/dist/collections/internal/index.d.ts.map +1 -0
  109. package/dist/collections/types.d.ts +282 -0
  110. package/dist/collections/types.d.ts.map +1 -0
  111. package/dist/collections/utils.d.ts +71 -0
  112. package/dist/collections/utils.d.ts.map +1 -0
  113. package/dist/collections/validator.d.ts +73 -0
  114. package/dist/collections/validator.d.ts.map +1 -0
  115. package/dist/config/config.d.ts +43 -0
  116. package/dist/config/config.d.ts.map +1 -0
  117. package/dist/config/index.d.ts +5 -0
  118. package/dist/config/index.d.ts.map +1 -0
  119. package/dist/config/sanitizeConfig.d.ts +82 -0
  120. package/dist/config/sanitizeConfig.d.ts.map +1 -0
  121. package/dist/config/types.d.ts +316 -0
  122. package/dist/config/types.d.ts.map +1 -0
  123. package/dist/fields/array/config.d.ts +57 -0
  124. package/dist/fields/array/config.d.ts.map +1 -0
  125. package/dist/fields/array/index.d.ts +5 -0
  126. package/dist/fields/array/index.d.ts.map +1 -0
  127. package/dist/fields/array/inputSchema.d.ts +32 -0
  128. package/dist/fields/array/inputSchema.d.ts.map +1 -0
  129. package/dist/fields/array/types.d.ts +127 -0
  130. package/dist/fields/array/types.d.ts.map +1 -0
  131. package/dist/fields/array/validator.d.ts +30 -0
  132. package/dist/fields/array/validator.d.ts.map +1 -0
  133. package/dist/fields/baseTypes.d.ts +251 -0
  134. package/dist/fields/baseTypes.d.ts.map +1 -0
  135. package/dist/fields/blocks/config.d.ts +63 -0
  136. package/dist/fields/blocks/config.d.ts.map +1 -0
  137. package/dist/fields/blocks/index.d.ts +5 -0
  138. package/dist/fields/blocks/index.d.ts.map +1 -0
  139. package/dist/fields/blocks/inputSchema.d.ts +22 -0
  140. package/dist/fields/blocks/inputSchema.d.ts.map +1 -0
  141. package/dist/fields/blocks/types.d.ts +253 -0
  142. package/dist/fields/blocks/types.d.ts.map +1 -0
  143. package/dist/fields/blocks/validator.d.ts +32 -0
  144. package/dist/fields/blocks/validator.d.ts.map +1 -0
  145. package/dist/fields/checkbox/config.d.ts +46 -0
  146. package/dist/fields/checkbox/config.d.ts.map +1 -0
  147. package/dist/fields/checkbox/index.d.ts +5 -0
  148. package/dist/fields/checkbox/index.d.ts.map +1 -0
  149. package/dist/fields/checkbox/inputSchema.d.ts +27 -0
  150. package/dist/fields/checkbox/inputSchema.d.ts.map +1 -0
  151. package/dist/fields/checkbox/types.d.ts +67 -0
  152. package/dist/fields/checkbox/types.d.ts.map +1 -0
  153. package/dist/fields/checkbox/validator.d.ts +34 -0
  154. package/dist/fields/checkbox/validator.d.ts.map +1 -0
  155. package/dist/fields/color/config.d.ts +49 -0
  156. package/dist/fields/color/config.d.ts.map +1 -0
  157. package/dist/fields/color/convert.d.ts +72 -0
  158. package/dist/fields/color/convert.d.ts.map +1 -0
  159. package/dist/fields/color/formats.d.ts +38 -0
  160. package/dist/fields/color/formats.d.ts.map +1 -0
  161. package/dist/fields/color/index.d.ts +8 -0
  162. package/dist/fields/color/index.d.ts.map +1 -0
  163. package/dist/fields/color/inputSchema.d.ts +29 -0
  164. package/dist/fields/color/inputSchema.d.ts.map +1 -0
  165. package/dist/fields/color/types.d.ts +100 -0
  166. package/dist/fields/color/types.d.ts.map +1 -0
  167. package/dist/fields/color/utils.d.ts +100 -0
  168. package/dist/fields/color/utils.d.ts.map +1 -0
  169. package/dist/fields/color/validator.d.ts +27 -0
  170. package/dist/fields/color/validator.d.ts.map +1 -0
  171. package/dist/fields/constants.d.ts +108 -0
  172. package/dist/fields/constants.d.ts.map +1 -0
  173. package/dist/fields/date/config.d.ts +57 -0
  174. package/dist/fields/date/config.d.ts.map +1 -0
  175. package/dist/fields/date/index.d.ts +5 -0
  176. package/dist/fields/date/index.d.ts.map +1 -0
  177. package/dist/fields/date/inputSchema.d.ts +29 -0
  178. package/dist/fields/date/inputSchema.d.ts.map +1 -0
  179. package/dist/fields/date/types.d.ts +147 -0
  180. package/dist/fields/date/types.d.ts.map +1 -0
  181. package/dist/fields/date/validator.d.ts +34 -0
  182. package/dist/fields/date/validator.d.ts.map +1 -0
  183. package/dist/fields/group/config.d.ts +47 -0
  184. package/dist/fields/group/config.d.ts.map +1 -0
  185. package/dist/fields/group/index.d.ts +5 -0
  186. package/dist/fields/group/index.d.ts.map +1 -0
  187. package/dist/fields/group/inputSchema.d.ts +27 -0
  188. package/dist/fields/group/inputSchema.d.ts.map +1 -0
  189. package/dist/fields/group/types.d.ts +105 -0
  190. package/dist/fields/group/types.d.ts.map +1 -0
  191. package/dist/fields/group/validator.d.ts +41 -0
  192. package/dist/fields/group/validator.d.ts.map +1 -0
  193. package/dist/fields/index.d.ts +20 -0
  194. package/dist/fields/index.d.ts.map +1 -0
  195. package/dist/fields/inputSchemas/index.d.ts +23 -0
  196. package/dist/fields/inputSchemas/index.d.ts.map +1 -0
  197. package/dist/fields/inputSchemas/utils.d.ts +25 -0
  198. package/dist/fields/inputSchemas/utils.d.ts.map +1 -0
  199. package/dist/fields/interfaceGen.d.ts +15 -0
  200. package/dist/fields/interfaceGen.d.ts.map +1 -0
  201. package/dist/fields/number/config.d.ts +48 -0
  202. package/dist/fields/number/config.d.ts.map +1 -0
  203. package/dist/fields/number/index.d.ts +5 -0
  204. package/dist/fields/number/index.d.ts.map +1 -0
  205. package/dist/fields/number/inputSchema.d.ts +23 -0
  206. package/dist/fields/number/inputSchema.d.ts.map +1 -0
  207. package/dist/fields/number/types.d.ts +100 -0
  208. package/dist/fields/number/types.d.ts.map +1 -0
  209. package/dist/fields/number/validator.d.ts +39 -0
  210. package/dist/fields/number/validator.d.ts.map +1 -0
  211. package/dist/fields/relationship/config.d.ts +45 -0
  212. package/dist/fields/relationship/config.d.ts.map +1 -0
  213. package/dist/fields/relationship/index.d.ts +5 -0
  214. package/dist/fields/relationship/index.d.ts.map +1 -0
  215. package/dist/fields/relationship/inputSchema.d.ts +31 -0
  216. package/dist/fields/relationship/inputSchema.d.ts.map +1 -0
  217. package/dist/fields/relationship/types.d.ts +126 -0
  218. package/dist/fields/relationship/types.d.ts.map +1 -0
  219. package/dist/fields/relationship/validator.d.ts +27 -0
  220. package/dist/fields/relationship/validator.d.ts.map +1 -0
  221. package/dist/fields/select/config.d.ts +59 -0
  222. package/dist/fields/select/config.d.ts.map +1 -0
  223. package/dist/fields/select/index.d.ts +5 -0
  224. package/dist/fields/select/index.d.ts.map +1 -0
  225. package/dist/fields/select/inputSchema.d.ts +28 -0
  226. package/dist/fields/select/inputSchema.d.ts.map +1 -0
  227. package/dist/fields/select/types.d.ts +89 -0
  228. package/dist/fields/select/types.d.ts.map +1 -0
  229. package/dist/fields/select/validator.d.ts +31 -0
  230. package/dist/fields/select/validator.d.ts.map +1 -0
  231. package/dist/fields/text/config.d.ts +48 -0
  232. package/dist/fields/text/config.d.ts.map +1 -0
  233. package/dist/fields/text/index.d.ts +5 -0
  234. package/dist/fields/text/index.d.ts.map +1 -0
  235. package/dist/fields/text/inputSchema.d.ts +24 -0
  236. package/dist/fields/text/inputSchema.d.ts.map +1 -0
  237. package/dist/fields/text/types.d.ts +132 -0
  238. package/dist/fields/text/types.d.ts.map +1 -0
  239. package/dist/fields/text/validator.d.ts +39 -0
  240. package/dist/fields/text/validator.d.ts.map +1 -0
  241. package/dist/fields/types.d.ts +135 -0
  242. package/dist/fields/types.d.ts.map +1 -0
  243. package/dist/fields/upload/config.d.ts +24 -0
  244. package/dist/fields/upload/config.d.ts.map +1 -0
  245. package/dist/fields/upload/index.d.ts +5 -0
  246. package/dist/fields/upload/index.d.ts.map +1 -0
  247. package/dist/fields/upload/inputSchema.d.ts +17 -0
  248. package/dist/fields/upload/inputSchema.d.ts.map +1 -0
  249. package/dist/fields/upload/types.d.ts +78 -0
  250. package/dist/fields/upload/types.d.ts.map +1 -0
  251. package/dist/fields/upload/validator.d.ts +16 -0
  252. package/dist/fields/upload/validator.d.ts.map +1 -0
  253. package/dist/fields/url/config.d.ts +49 -0
  254. package/dist/fields/url/config.d.ts.map +1 -0
  255. package/dist/fields/url/index.d.ts +5 -0
  256. package/dist/fields/url/index.d.ts.map +1 -0
  257. package/dist/fields/url/inputSchema.d.ts +31 -0
  258. package/dist/fields/url/inputSchema.d.ts.map +1 -0
  259. package/dist/fields/url/types.d.ts +70 -0
  260. package/dist/fields/url/types.d.ts.map +1 -0
  261. package/dist/fields/url/validator.d.ts +37 -0
  262. package/dist/fields/url/validator.d.ts.map +1 -0
  263. package/dist/fields/utils.d.ts +47 -0
  264. package/dist/fields/utils.d.ts.map +1 -0
  265. package/dist/fields/validators/index.d.ts +23 -0
  266. package/dist/fields/validators/index.d.ts.map +1 -0
  267. package/dist/fields/validators/utils.d.ts +24 -0
  268. package/dist/fields/validators/utils.d.ts.map +1 -0
  269. package/dist/framework.d.ts +227 -0
  270. package/dist/framework.d.ts.map +1 -0
  271. package/dist/globals/config/config.d.ts +1 -0
  272. package/dist/globals/config/config.d.ts.map +1 -0
  273. package/dist/globals/config/types.d.ts +1 -0
  274. package/dist/globals/config/types.d.ts.map +1 -0
  275. package/dist/globals/config.d.ts +47 -0
  276. package/dist/globals/config.d.ts.map +1 -0
  277. package/dist/globals/index.d.ts +5 -0
  278. package/dist/globals/index.d.ts.map +1 -0
  279. package/dist/globals/interfaceGen.d.ts +33 -0
  280. package/dist/globals/interfaceGen.d.ts.map +1 -0
  281. package/dist/globals/types.d.ts +175 -0
  282. package/dist/globals/types.d.ts.map +1 -0
  283. package/dist/globals/utils.d.ts +37 -0
  284. package/dist/globals/utils.d.ts.map +1 -0
  285. package/dist/index.d.ts +15 -3165
  286. package/dist/index.d.ts.map +1 -0
  287. package/dist/index.js +2101 -3475
  288. package/dist/index.js.map +1 -1
  289. package/dist/media/api/index.d.ts +5 -0
  290. package/dist/media/api/index.d.ts.map +1 -0
  291. package/dist/media/api/mutations.d.ts +76 -0
  292. package/dist/media/api/mutations.d.ts.map +1 -0
  293. package/dist/media/api/queries.d.ts +28 -0
  294. package/dist/media/api/queries.d.ts.map +1 -0
  295. package/dist/media/api/server.d.ts +68 -0
  296. package/dist/media/api/server.d.ts.map +1 -0
  297. package/dist/media/api/types.d.ts +171 -0
  298. package/dist/media/api/types.d.ts.map +1 -0
  299. package/dist/media/config.d.ts +25 -0
  300. package/dist/media/config.d.ts.map +1 -0
  301. package/dist/media/index.d.ts +6 -0
  302. package/dist/media/index.d.ts.map +1 -0
  303. package/dist/media/types.d.ts +332 -0
  304. package/dist/media/types.d.ts.map +1 -0
  305. package/dist/media/utils.d.ts +35 -0
  306. package/dist/media/utils.d.ts.map +1 -0
  307. package/dist/media/validators.d.ts +61 -0
  308. package/dist/media/validators.d.ts.map +1 -0
  309. package/dist/schema/generateVexSchema.d.ts +36 -0
  310. package/dist/schema/generateVexSchema.d.ts.map +1 -0
  311. package/dist/schema/index.d.ts +3 -0
  312. package/dist/schema/index.d.ts.map +1 -0
  313. package/dist/schema/migrate.d.ts +73 -0
  314. package/dist/schema/migrate.d.ts.map +1 -0
  315. package/dist/types/generateVexTypes.d.ts +41 -0
  316. package/dist/types/generateVexTypes.d.ts.map +1 -0
  317. package/dist/types/generated.d.ts +382 -0
  318. package/dist/types/generated.d.ts.map +1 -0
  319. package/dist/types/index.d.ts +5 -0
  320. package/dist/types/index.d.ts.map +1 -0
  321. package/dist/utils.d.ts +57 -0
  322. package/dist/utils.d.ts.map +1 -0
  323. package/package.json +67 -24
package/dist/index.d.ts CHANGED
@@ -1,3165 +1,15 @@
1
- import { ComponentType } from 'react';
2
- import * as convex_server from 'convex/server';
3
- import { TableDefinition, GenericDataModel, GenericMutationCtx, GenericQueryCtx, QueryBuilder, RegisteredQuery, TableNamesInDataModel, PaginationOptions } from 'convex/server';
4
- import { VObject, GenericValidator, ObjectType, PropertyValidators, v } from 'convex/values';
5
- import { ColumnDef } from '@tanstack/react-table';
6
- import { ZodTypeAny, z } from 'zod';
7
-
8
- /**
9
- * The style tier identifiers that a block can declare in admin.blockStyles.
10
- * Each tier corresponds to a section in the style popover and a set of CSS properties.
11
- */
12
- type StyleTier = "container" | "text" | "layout" | "media";
13
- /**
14
- * Container styles universal layout/visual properties applied to the block's outer wrapper.
15
- * All values are Tailwind scale keys (e.g., "4" → 1rem/16px) or color strings.
16
- */
17
- interface ContainerStyleConfig {
18
- /** Tailwind margin scale value. Shorthand: applies to all sides. */
19
- margin?: string;
20
- marginTop?: string;
21
- marginRight?: string;
22
- marginBottom?: string;
23
- marginLeft?: string;
24
- /** Tailwind padding scale value. */
25
- padding?: string;
26
- paddingTop?: string;
27
- paddingRight?: string;
28
- paddingBottom?: string;
29
- paddingLeft?: string;
30
- /** Tailwind width class key (e.g., "full", "1/2", "screen"). */
31
- width?: string;
32
- /** Tailwind max-width class key. */
33
- maxWidth?: string;
34
- /** Background color — CSS color string or CSS variable reference. */
35
- backgroundColor?: string;
36
- /** Border width — Tailwind border width key (e.g., "", "2", "4"). */
37
- borderWidth?: string;
38
- /** Border color — CSS color string or CSS variable reference. */
39
- borderColor?: string;
40
- /** Border style. */
41
- borderStyle?: "solid" | "dashed" | "dotted" | "none";
42
- /** Tailwind border radius key (e.g., "sm", "md", "lg", "full"). */
43
- borderRadius?: string;
44
- /** Tailwind box shadow key (e.g., "sm", "md", "lg", "xl", "2xl", "none"). */
45
- boxShadow?: string;
46
- /** Tailwind opacity value (e.g., "0", "25", "50", "75", "100"). */
47
- opacity?: string;
48
- /** Display value. */
49
- display?: "block" | "flex" | "grid" | "none" | "inline-flex";
50
- /** Overflow value. */
51
- overflow?: "hidden" | "scroll" | "auto" | "visible";
52
- }
53
- /**
54
- * Text styles — typography properties for text-heavy blocks.
55
- */
56
- interface TextStyleConfig {
57
- /** Text alignment. */
58
- textAlign?: "left" | "center" | "right" | "justify";
59
- /** Tailwind font size key (e.g., "sm", "base", "lg", "xl", "2xl"). */
60
- fontSize?: string;
61
- /** Tailwind font weight key (e.g., "normal", "medium", "semibold", "bold"). */
62
- fontWeight?: string;
63
- /** Text color — CSS color string or CSS variable reference. */
64
- color?: string;
65
- /** Tailwind line height key (e.g., "tight", "snug", "normal", "relaxed"). */
66
- lineHeight?: string;
67
- /** Tailwind letter spacing key (e.g., "tighter", "tight", "normal", "wide"). */
68
- letterSpacing?: string;
69
- }
70
- /**
71
- * Layout styles — for blocks that contain child elements in flex/grid layouts.
72
- */
73
- interface LayoutStyleConfig {
74
- /** Tailwind gap scale value. */
75
- gap?: string;
76
- /** Flex direction. */
77
- flexDirection?: "row" | "column" | "row-reverse" | "column-reverse";
78
- /** Align items. */
79
- alignItems?: "start" | "center" | "end" | "stretch" | "baseline";
80
- /** Justify content. */
81
- justifyContent?: "start" | "center" | "end" | "between" | "around" | "evenly";
82
- /** Flex wrap. */
83
- flexWrap?: "wrap" | "nowrap" | "wrap-reverse";
84
- }
85
- /**
86
- * Media styles — for blocks with background images or media elements.
87
- */
88
- interface MediaStyleConfig {
89
- /** Object fit for background/media elements. */
90
- objectFit?: "cover" | "contain" | "fill" | "none" | "scale-down";
91
- /** Aspect ratio (e.g., "video", "square", "auto", or custom like "4/3"). */
92
- aspectRatio?: string;
93
- /** Object position (e.g., "center", "top", "bottom"). */
94
- objectPosition?: string;
95
- }
96
- /**
97
- * Combined style config for a single breakpoint.
98
- * Which properties are present depends on the block's declared style tiers.
99
- */
100
- interface BlockStyleValues extends ContainerStyleConfig, TextStyleConfig, LayoutStyleConfig, MediaStyleConfig {
101
- }
102
- /**
103
- * The full blockStyles structure stored as JSON string.
104
- * Keys are breakpoint names ("base" is always present, others come from config).
105
- *
106
- * @example
107
- * ```json
108
- * {
109
- * "base": { "margin": "4", "padding": "2", "backgroundColor": "#fff" },
110
- * "sm": { "margin": "6" },
111
- * "lg": { "margin": "8", "padding": "4" }
112
- * }
113
- * ```
114
- */
115
- type BlockStylesData = Record<string, BlockStyleValues>;
116
-
117
- /**
118
- * A rich text document is an array of Slate/Plate element nodes.
119
- * This is the canonical type for all rich text content stored in VEX.
120
- *
121
- * Each element has a `type` (e.g., "p", "h1", "img") and `children`
122
- * (nested elements or text nodes with formatting marks).
123
- *
124
- * @example
125
- * ```ts
126
- * const doc: RichTextDocument = [
127
- * { type: "h1", children: [{ text: "Hello" }] },
128
- * { type: "p", children: [{ text: "World", bold: true }] },
129
- * ];
130
- * ```
131
- */
132
- type RichTextDocument = RichTextElement[];
133
- /** A single element node in a rich text document. */
134
- interface RichTextElement {
135
- /** Node type identifier (e.g., "p", "h1", "img", "table") */
136
- type: string;
137
- /** Child nodes — nested elements or text leaves */
138
- children: (RichTextElement | RichTextText)[];
139
- /** URL for link/image elements */
140
- url?: string;
141
- /** Alt text for image elements */
142
- alt?: string;
143
- /** Media collection document ID for image elements (ID-based resolution) */
144
- mediaId?: string;
145
- /** Additional node-specific properties (alignment, colspan, etc.) */
146
- [key: string]: unknown;
147
- }
148
- /** A text leaf node with optional formatting marks. */
149
- interface RichTextText {
150
- /** The text content */
151
- text: string;
152
- /** Bold formatting */
153
- bold?: boolean;
154
- /** Italic formatting */
155
- italic?: boolean;
156
- /** Underline formatting */
157
- underline?: boolean;
158
- /** Strikethrough formatting */
159
- strikethrough?: boolean;
160
- /** Inline code formatting */
161
- code?: boolean;
162
- }
163
- /**
164
- * Adapter interface for rich text editors.
165
- * Allows swapping Plate for Tiptap, Lexical, or any custom editor.
166
- *
167
- * Implement this interface and pass it to `defineConfig({ editor: ... })`
168
- * or to individual `richtext({ editor: ... })` fields.
169
- *
170
- * @example
171
- * ```ts
172
- * // Using the built-in Plate adapter
173
- * import { plateEditor } from "@vexcms/richtext/editor"
174
- * defineConfig({ editor: plateEditor() })
175
- *
176
- * // Building a custom adapter
177
- * const myAdapter: VexEditorAdapter = {
178
- * type: "custom",
179
- * editorComponent: MyEditorComponent,
180
- * renderComponent: MyRenderComponent,
181
- * }
182
- * ```
183
- */
184
- interface VexEditorAdapter {
185
- /**
186
- * Unique identifier for this editor adapter (e.g., "plate", "tiptap", "lexical").
187
- * Used for debugging and distinguishing between different editor implementations.
188
- */
189
- type: string;
190
- /**
191
- * React component that renders the editor in the admin form.
192
- *
193
- * Receives the current `RichTextDocument` value and an `onChange` callback.
194
- * Must support `readOnly` mode for document preview.
195
- * The component manages its own internal editor state and calls `onChange`
196
- * when the user makes edits.
197
- */
198
- editorComponent: ComponentType<VexEditorComponentProps>;
199
- /**
200
- * React component that renders rich text content on the frontend.
201
- *
202
- * Receives the stored `RichTextDocument` and renders it as React elements.
203
- * Must be safe for server-side rendering (no browser APIs).
204
- * Optionally supports `resolveMedia` for image URL resolution.
205
- */
206
- renderComponent: ComponentType<VexRenderComponentProps>;
207
- }
208
- /**
209
- * Props passed to the editor component in the admin form.
210
- */
211
- interface VexEditorComponentProps {
212
- /**
213
- * Current editor value as a `RichTextDocument` (Slate/Plate JSON array).
214
- * May be `null` or empty array `[]` for new documents.
215
- */
216
- value: RichTextDocument | null;
217
- /**
218
- * Callback to update the editor value.
219
- * Called whenever the user makes edits. The new value replaces the old one entirely.
220
- */
221
- onChange: (value: RichTextDocument) => void;
222
- /**
223
- * Whether the field is read-only (e.g., user lacks update permission).
224
- * When true, hide the toolbar and disable editing.
225
- * @default false
226
- */
227
- readOnly?: boolean;
228
- /** Placeholder text shown when the editor is empty. */
229
- placeholder?: string;
230
- /** Field label rendered above the editor. */
231
- label?: string;
232
- /** Helper text rendered below the editor. */
233
- description?: string;
234
- /** Field name (key) — used for form binding and accessibility. */
235
- name: string;
236
- /**
237
- * Media collection slug for image uploads.
238
- * When set, the editor can pick images from the specified media collection.
239
- * When not set, only URL input and direct file upload are available.
240
- */
241
- mediaCollection?: string;
242
- }
243
- /**
244
- * Props passed to the render component on the frontend.
245
- *
246
- * @example
247
- * ```tsx
248
- * import { RichText } from "@vexcms/richtext/render"
249
- *
250
- * <RichText content={document.content} className="prose" />
251
- *
252
- * // With media ID resolution
253
- * <RichText
254
- * content={document.content}
255
- * resolveMedia={async (id) => getMediaUrl(id)}
256
- * />
257
- * ```
258
- */
259
- interface VexRenderComponentProps {
260
- /**
261
- * The stored rich text content as a `RichTextDocument` (Slate/Plate JSON array).
262
- * Pass `null` or `[]` for empty content — the component will render nothing.
263
- */
264
- content: RichTextDocument | null;
265
- /**
266
- * Optional async resolver for media IDs → URLs.
267
- * When provided, image nodes with a `mediaId` property will have their
268
- * `url` resolved by calling this function before rendering.
269
- *
270
- * @param mediaId - The media collection document ID from the image node
271
- * @returns The resolved URL, or `null` if the media could not be found
272
- */
273
- resolveMedia?: (mediaId: string) => Promise<string | null>;
274
- /**
275
- * CSS class name applied to the root element.
276
- * Useful for applying prose styles (e.g., Tailwind's `prose` class).
277
- */
278
- className?: string;
279
- }
280
-
281
- /** Content alignment for data table cells. */
282
- type Alignment = "left" | "right" | "center";
283
- type Labels = {
284
- singular: string;
285
- plural: string;
286
- };
287
- /**
288
- * Props passed to custom field components.
289
- * Custom components receive these props and use useVexField() for state.
290
- *
291
- * Use the generic parameter to narrow the field type for type-safe access
292
- * to field-specific properties like `options` on select fields.
293
- *
294
- * @example
295
- * ```tsx
296
- * // Generic — fieldDef has label, admin, description, required
297
- * function MyField({ name, fieldDef, readOnly }: FieldComponentProps) { ... }
298
- *
299
- * // Narrowed — fieldDef is TextFieldDef with maxLength, minLength, etc.
300
- * function MyTextField({ name, fieldDef }: FieldComponentProps<TextFieldDef>) { ... }
301
- * ```
302
- */
303
- interface FieldComponentProps<TField extends VexField = VexField> {
304
- /** The field key name (e.g., "primaryColor") */
305
- name: string;
306
- /** The VexField definition for this field */
307
- fieldDef: TField;
308
- /** Whether the field is read-only (from permissions or config) */
309
- readOnly: boolean;
310
- }
311
- /**
312
- * Props passed to custom cell components in the data table.
313
- */
314
- interface CellComponentProps<TField extends VexField = VexField> {
315
- /** The raw cell value from the document */
316
- value: unknown;
317
- /** The full row data (document) */
318
- row: Record<string, unknown>;
319
- /** The VexField definition for this column's field */
320
- fieldDef: TField;
321
- }
322
- /**
323
- * Admin panel configuration for individual fields.
324
- * Controls visibility, layout, and input behavior in the admin UI.
325
- */
326
- interface FieldAdminConfig {
327
- /**
328
- * Hide this field from the admin form.
329
- * Hidden fields are still stored in the database.
330
- *
331
- * Default: `false`
332
- */
333
- hidden?: boolean;
334
- /**
335
- * Make this field read-only in the admin form.
336
- * The value is displayed but cannot be edited.
337
- *
338
- * Default: `false`
339
- */
340
- readOnly?: boolean;
341
- /**
342
- * Position of the field in the form layout.
343
- *
344
- * - `"main"` — placed in the main content area
345
- * - `"sidebar"` — placed in the sidebar panel
346
- *
347
- * Default: `"main"`
348
- */
349
- position?: "main" | "sidebar";
350
- /**
351
- * Width of the field within its row.
352
- *
353
- * - `"full"` — spans the full width
354
- * - `"half"` — spans half the width (two fields per row)
355
- *
356
- * Default: `"full"`
357
- */
358
- width?: "full" | "half";
359
- /**
360
- * Placeholder text shown in the input when empty.
361
- */
362
- placeholder?: string;
363
- /**
364
- * Helper text displayed below the field input.
365
- * Use for additional context or formatting hints.
366
- */
367
- description?: string;
368
- /**
369
- * Content alignment in data table cells. 'left' | 'right' | 'center'
370
- */
371
- cellAlignment?: Alignment;
372
- /**
373
- * Custom components for this field.
374
- *
375
- * - `Field` replaces the entire field input in the edit form.
376
- * Only allowed on text, number, checkbox, and select fields.
377
- * The component receives FieldComponentProps and uses useVexField() for state.
378
- *
379
- * - `Cell` replaces the cell renderer in the data table list view.
380
- * Allowed on any field type.
381
- */
382
- components?: {
383
- Field?: ComponentType<FieldComponentProps>;
384
- Cell?: ComponentType<CellComponentProps>;
385
- };
386
- }
387
- /**
388
- * Properties shared by all field types.
389
- * Each concrete field type extends this with its `type` discriminant
390
- * and type-specific options.
391
- */
392
- interface BaseField {
393
- /** Display label for the field in the admin form. */
394
- label?: string;
395
- /** Description text shown below the field. */
396
- description?: string;
397
- /**
398
- * Whether this field is required.
399
- *
400
- * Default: `false`
401
- */
402
- required?: boolean;
403
- /** Admin UI configuration for this field. */
404
- admin?: FieldAdminConfig;
405
- /**
406
- * Create a database index on this field.
407
- * The string value becomes the index name in Convex.
408
- *
409
- * @example
410
- * ```ts
411
- * slug: { type: "text", index: "by_slug", required: true }
412
- * // Generates: .index("by_slug", ["slug"])
413
- * ```
414
- */
415
- index?: string;
416
- /**
417
- * Create a full-text search index on this field.
418
- * The field this is defined on becomes the `searchField`.
419
- *
420
- * @example
421
- * ```ts
422
- * title: {
423
- * type: "text",
424
- * searchIndex: { name: "search_title", filterFields: ["status", "author"] },
425
- * }
426
- * // Generates: .searchIndex("search_title", { searchField: "title", filterFields: ["status", "author"] })
427
- * ```
428
- */
429
- searchIndex?: {
430
- /** Search index name (must be unique within the collection). */
431
- name: string;
432
- /**
433
- * Fields to filter search results by.
434
- * String array — validated at runtime against collection field names.
435
- */
436
- filterFields: string[];
437
- };
438
- }
439
- /** Text field definition. */
440
- interface TextFieldDef extends BaseField {
441
- readonly type: "text";
442
- /** Default value for new documents. */
443
- defaultValue?: string;
444
- /** Minimum character length. */
445
- minLength?: number;
446
- /** Maximum character length. */
447
- maxLength?: number;
448
- }
449
- /** Number field definition. */
450
- interface NumberFieldDef extends BaseField {
451
- readonly type: "number";
452
- /** Default value for new documents. */
453
- defaultValue?: number;
454
- /** Minimum allowed value. */
455
- min?: number;
456
- /** Maximum allowed value. */
457
- max?: number;
458
- /** Step increment for the input. */
459
- step?: number;
460
- }
461
- /** Checkbox field definition. */
462
- interface CheckboxFieldDef extends BaseField {
463
- readonly type: "checkbox";
464
- /** Default value for new documents. */
465
- defaultValue?: boolean;
466
- }
467
- /**
468
- * A single option in a select field.
469
- */
470
- interface SelectOption<T extends string = string> {
471
- /** The stored value. */
472
- readonly value: T;
473
- /** The display label shown in the dropdown. */
474
- readonly label: string;
475
- /** Optional badge color for the data table. Accepts a hex string (e.g. "#3b82f6"). */
476
- readonly badgeColor?: string;
477
- }
478
- /** Select field — single value variant. */
479
- interface SelectFieldSingle<T extends string = string> extends BaseField {
480
- readonly type: "select";
481
- /** The available options for this select field. */
482
- options: readonly SelectOption<T>[];
483
- /** Default value for new documents. */
484
- defaultValue?: T;
485
- hasMany?: false;
486
- }
487
- /** Select field — multi-value variant. */
488
- interface SelectFieldMany<T extends string = string> extends BaseField {
489
- readonly type: "select";
490
- /** The available options for this select field. */
491
- options: readonly SelectOption<T>[];
492
- /** Default value for new documents. */
493
- defaultValue?: T;
494
- /** Display labels for the field (singular/plural). */
495
- labels?: Labels;
496
- hasMany: true;
497
- }
498
- /** Select field definition with typed options. Discriminated on `hasMany`. */
499
- type SelectFieldDef<T extends string = string> = SelectFieldSingle<T> | SelectFieldMany<T>;
500
- /** Date field definition. Stores epoch milliseconds. */
501
- interface DateFieldDef extends BaseField {
502
- readonly type: "date";
503
- /** Default value for new documents (epoch ms). */
504
- defaultValue?: number;
505
- }
506
- /** Image URL field definition. Stores a URL string, renders as thumbnail. */
507
- interface ImageUrlFieldDef extends BaseField {
508
- readonly type: "imageUrl";
509
- /** Default value for new documents. */
510
- defaultValue?: string;
511
- /** Width (px) of the image */
512
- width?: number;
513
- /** Height (px) of the image */
514
- height?: number;
515
- }
516
- /** Relationship field — single reference variant. */
517
- interface RelationshipFieldSingle extends BaseField {
518
- readonly type: "relationship";
519
- /** Target table name. */
520
- to: string;
521
- hasMany?: false;
522
- }
523
- /** Relationship field — multi-reference variant. */
524
- interface RelationshipFieldMany extends BaseField {
525
- readonly type: "relationship";
526
- /** Target table name. */
527
- to: string;
528
- /** Display labels for the field (singular/plural). */
529
- labels?: Labels;
530
- hasMany: true;
531
- }
532
- /** Relationship field definition. Discriminated on `hasMany`. */
533
- type RelationshipFieldDef = RelationshipFieldSingle | RelationshipFieldMany;
534
- /** Shared upload field properties. */
535
- interface UploadFieldBase extends BaseField {
536
- readonly type: "upload";
537
- /** Target media collection slug. */
538
- to: string;
539
- /**
540
- * Accepted MIME types for file uploads.
541
- * Supports exact types ("image/png") and wildcards ("image/*").
542
- * When not set, all file types are accepted.
543
- */
544
- accept?: string[];
545
- /**
546
- * Maximum file size in bytes for uploads.
547
- * When not set, no size limit is enforced (beyond storage provider limits).
548
- */
549
- maxSize?: number;
550
- }
551
- /** Upload field — single reference variant. */
552
- interface UploadFieldSingle extends UploadFieldBase {
553
- hasMany?: false;
554
- }
555
- /** Upload field — multi-reference variant. */
556
- interface UploadFieldMany extends UploadFieldBase {
557
- /** Display labels for the field (singular/plural). */
558
- labels?: Labels;
559
- hasMany: true;
560
- }
561
- /**
562
- * Upload field definition. References a media collection document via `v.id()`.
563
- * Discriminated on `hasMany`.
564
- */
565
- type UploadFieldDef = UploadFieldSingle | UploadFieldMany;
566
- /** JSON field definition. Stores arbitrary data via `v.any()`. */
567
- interface JsonFieldDef extends BaseField {
568
- readonly type: "json";
569
- }
570
- /** Object field definition. Stores a named group of sub-fields as `v.object()`. */
571
- interface ObjectFieldDef extends BaseField {
572
- readonly type: "object";
573
- /** Named sub-fields that make up this object. */
574
- fields: Record<string, VexField>;
575
- }
576
- /** Array field definition. Wraps an inner field in `v.array()`. */
577
- interface ArrayFieldDef extends BaseField {
578
- readonly type: "array";
579
- /** Display labels for the field (singular/plural). */
580
- labels?: Labels;
581
- /** The field type for each item in the array. */
582
- items: VexField;
583
- /** Default value for the array (used when creating new documents/blocks). */
584
- defaultValue?: unknown[];
585
- /** Minimum number of items. */
586
- min?: number;
587
- /** Maximum number of items. */
588
- max?: number;
589
- }
590
-
591
- /** Rich text field definition. Stores Plate/Slate JSON via `v.any()`. */
592
- interface RichTextFieldDef extends BaseField {
593
- readonly type: "richtext";
594
- /**
595
- * Editor adapter override for this specific field.
596
- * If not set, uses the global editor from `VexConfig.editor`.
597
- */
598
- editor?: VexEditorAdapter;
599
- /**
600
- * Media collection slug for image uploads.
601
- * When set, the editor can pick images from the specified media collection,
602
- * and paste/drop image uploads are auto-saved to this collection.
603
- * When not set, images can only be inserted by URL.
604
- */
605
- mediaCollection?: string;
606
- }
607
- /**
608
- * Color picker field. Stores a color string in the configured format.
609
- *
610
- * @example
611
- * ```ts
612
- * accentColor: color({ label: "Accent Color", format: "hex" })
613
- * primaryColor: color({ label: "Primary", format: "oklch", themeColors: true })
614
- * ```
615
- */
616
- interface ColorFieldDef extends BaseField {
617
- readonly type: "color";
618
- /**
619
- * Default color value.
620
- * Should be in the configured format (hex, hsl, or oklch).
621
- */
622
- defaultValue?: string;
623
- /**
624
- * Output format for the color value.
625
- * - "hex" — e.g., "#3b82f6" (default)
626
- * - "hsl" — e.g., "hsl(217, 91%, 60%)"
627
- * - "oklch" — e.g., "oklch(0.623 0.214 259.1)"
628
- */
629
- format?: "hex" | "hsl" | "oklch";
630
- /**
631
- * When true, shows a "Theme Colors" tab in the color picker
632
- * that displays CSS variables from the current page's computed styles.
633
- * Users can select a theme color variable instead of picking a custom color.
634
- *
635
- * Default: false
636
- */
637
- themeColors?: boolean;
638
- }
639
- /**
640
- * A single tab definition within a tabs field.
641
- */
642
- interface TabDef<TSlug extends string = string, TFields extends Record<string, VexField> = Record<string, VexField>> {
643
- /** Display label for the tab in the admin panel. */
644
- label: string;
645
- /**
646
- * All fields in this tab are nested under this key as an object.
647
- * e.g., `slug: "light"` → `{ light: { background: "#fff", ... } }`
648
- */
649
- slug: TSlug;
650
- /** Fields within this tab. */
651
- fields: TFields;
652
- }
653
- /**
654
- * Tabs field definition. Groups fields into tabbed UI sections in the admin panel.
655
- *
656
- * - Tabs with `slug` create nested objects in the document
657
- * - Tabs without `slug` flatten their fields onto the parent
658
- *
659
- * @example
660
- * ```ts
661
- * themeColors: tabs({
662
- * label: "Theme Colors",
663
- * tabs: [
664
- * {
665
- * label: "Light",
666
- * slug: "light",
667
- * fields: {
668
- * background: color({ label: "Background" }),
669
- * foreground: color({ label: "Foreground" }),
670
- * },
671
- * },
672
- * {
673
- * label: "Dark",
674
- * slug: "dark",
675
- * fields: {
676
- * background: color({ label: "Background" }),
677
- * foreground: color({ label: "Foreground" }),
678
- * },
679
- * },
680
- * ],
681
- * })
682
- * ```
683
- */
684
- interface TabsFieldDef<TTabs extends TabDef[] = TabDef[]> extends BaseField {
685
- readonly type: "tabs";
686
- /** The tab definitions. */
687
- tabs: TTabs;
688
- }
689
- /**
690
- * UI field definition. Non-persisted — renders a custom component only.
691
- * Skipped during schema generation, form validation, and column generation.
692
- * Requires admin.components.Field to be set.
693
- */
694
- interface UIFieldDef extends BaseField {
695
- readonly type: "ui";
696
- /**
697
- * Admin config — components.Field is required for ui fields.
698
- */
699
- admin: FieldAdminConfig & {
700
- components: {
701
- Field: ComponentType<FieldComponentProps>;
702
- };
703
- };
704
- }
705
- /**
706
- * Admin configuration specific to block definitions.
707
- */
708
- interface BlockAdminConfig {
709
- /** Icon identifier for the block picker UI (e.g., "layout-template"). */
710
- icon?: string;
711
- /** Custom admin components for this block (future — Spec 09b). */
712
- components?: {
713
- Editor?: ComponentType<any>;
714
- };
715
- /**
716
- * Enable block style controls in the admin panel.
717
- *
718
- * - `true` — enables container styles only (equivalent to `["container"]`)
719
- * - `StyleTier[]` — enables specific style tiers (e.g., `["container", "text", "media"]`)
720
- * - `undefined` / omitted — no style controls shown
721
- *
722
- * Available tiers: "container", "text", "layout", "media"
723
- */
724
- blockStyles?: true | StyleTier[];
725
- }
726
- /**
727
- * A block definition created by `defineBlock()`.
728
- * Blocks are reusable field groups composed into ordered lists via the `blocks()` field type.
729
- *
730
- * @example
731
- * ```ts
732
- * const heroBlock = defineBlock({
733
- * slug: "hero",
734
- * label: "Hero Section",
735
- * fields: { heading: text({ required: true }), subheading: text() },
736
- * })
737
- * ```
738
- */
739
- interface BlockDef<TFields extends Record<string, VexField> = Record<string, VexField>> {
740
- /** Unique identifier for this block type. Used as the `blockType` discriminant in stored data. */
741
- readonly slug: string;
742
- /** Display label for the block in the admin picker. */
743
- label: string;
744
- /** Field definitions for this block's data shape. */
745
- fields: TFields;
746
- /** Admin UI configuration. */
747
- admin?: BlockAdminConfig;
748
- /**
749
- * TypeScript interface name used in generated `vex.types.ts`.
750
- * If not set, auto-generated from slug via PascalCase conversion.
751
- * @example "HeroBlock"
752
- */
753
- interfaceName?: string;
754
- }
755
- /** Blocks field definition. Stores an ordered array of block instances. */
756
- interface BlocksFieldDef extends BaseField {
757
- readonly type: "blocks";
758
- /** The block definitions allowed in this field. */
759
- blocks: BlockDef[];
760
- /** Display labels for the field (singular/plural). */
761
- labels?: Labels;
762
- /** Minimum number of blocks. */
763
- min?: number;
764
- /** Maximum number of blocks. */
765
- max?: number;
766
- }
767
- /**
768
- * Distributive version of `Omit` that preserves union branches.
769
- * Standard `Omit` collapses unions; this applies `Omit` to each branch individually.
770
- */
771
- type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
772
- /**
773
- * Discriminated union of all field types. Switch on `field.type` to narrow.
774
- *
775
- * @example
776
- * ```ts
777
- * function handle(field: VexField) {
778
- * switch (field.type) {
779
- * case "text":
780
- * field.maxLength; // TextFieldDef ✓
781
- * break;
782
- * case "select":
783
- * field.options; // SelectFieldDef ✓
784
- * break;
785
- * }
786
- * }
787
- * ```
788
- */
789
- type VexField = TextFieldDef | NumberFieldDef | CheckboxFieldDef | SelectFieldDef<string> | DateFieldDef | ImageUrlFieldDef | RelationshipFieldDef | UploadFieldDef | JsonFieldDef | ObjectFieldDef | ArrayFieldDef | RichTextFieldDef | UIFieldDef | BlocksFieldDef | ColorFieldDef | TabsFieldDef;
790
- /**
791
- * Infer the TypeScript value type from a VexField.
792
- * Uses the `type` discriminant and field options to determine the type.
793
- */
794
- type InferFieldType<F extends VexField> = F extends {
795
- type: "text";
796
- } ? string : F extends {
797
- type: "number";
798
- } ? number : F extends {
799
- type: "checkbox";
800
- } ? boolean : F extends {
801
- type: "select";
802
- hasMany: true;
803
- } ? string[] : F extends {
804
- type: "select";
805
- } ? string : F extends {
806
- type: "date";
807
- } ? number : F extends {
808
- type: "imageUrl";
809
- } ? string : F extends {
810
- type: "relationship";
811
- hasMany: true;
812
- } ? string[] : F extends {
813
- type: "relationship";
814
- } ? string : F extends {
815
- type: "upload";
816
- hasMany: true;
817
- } ? string[] : F extends {
818
- type: "upload";
819
- } ? string : F extends {
820
- type: "json";
821
- } ? unknown : F extends {
822
- type: "object";
823
- } ? Record<string, unknown> : F extends {
824
- type: "richtext";
825
- } ? RichTextDocument : F extends {
826
- type: "blocks";
827
- } ? Array<InferBlockUnion<F>> : F extends {
828
- type: "array";
829
- } ? unknown[] : F extends {
830
- type: "ui";
831
- } ? never : F extends {
832
- type: "color";
833
- } ? string : F extends {
834
- type: "tabs";
835
- } ? Record<string, unknown> : never;
836
- /**
837
- * Infer the discriminated union type for a blocks field.
838
- * Each block becomes an object type with `blockType` literal + `_key` + its field types.
839
- */
840
- type InferBlockUnion<F extends VexField> = F extends BlocksFieldDef ? F["blocks"][number] extends infer B ? B extends BlockDef<infer TFields> ? {
841
- blockType: B["slug"];
842
- blockName?: string;
843
- _key: string;
844
- } & {
845
- [K in keyof TFields]: InferFieldType<TFields[K] & VexField>;
846
- } : never : never : never;
847
- /**
848
- * Infer the expanded tab fields from a tabs field definition.
849
- * Each tab becomes a property keyed by its slug with an object of its inferred fields.
850
- */
851
- type InferTabsExpansion<F extends TabsFieldDef> = F["tabs"][number] extends infer T ? T extends TabDef<infer TSlug, infer TFields> ? {
852
- [K in TSlug]?: {
853
- [FK in keyof TFields]: InferFieldType<TFields[FK] & VexField>;
854
- };
855
- } : {} : {};
856
- /**
857
- * Extract keys of fields that are tabs fields.
858
- */
859
- type TabsFieldKeys<F extends Record<string, any>> = {
860
- [K in keyof F]: (F[K] & VexField) extends {
861
- type: "tabs";
862
- } ? K : never;
863
- }[keyof F];
864
- /**
865
- * Extract keys of fields that are NOT tabs fields.
866
- */
867
- type NonTabsFieldKeys<F extends Record<string, any>> = {
868
- [K in keyof F]: (F[K] & VexField) extends {
869
- type: "tabs";
870
- } ? never : K;
871
- }[keyof F];
872
- /**
873
- * Expand all tabs fields in a record into their tab slug entries.
874
- * Produces a union of objects (one per tabs field) which gets intersected.
875
- */
876
- type ExpandTabsFields<F extends Record<string, any>> = TabsFieldKeys<F> extends infer TK ? TK extends keyof F ? InferTabsExpansion<F[TK] & TabsFieldDef> : {} : {};
877
- /**
878
- * Infer the document type from a record of fields.
879
- * Tabs fields are expanded: instead of `colors: Record<string, unknown>`,
880
- * produces `light?: { ... }; dark?: { ... }` based on the tab definitions.
881
- *
882
- * @example
883
- * ```ts
884
- * type Doc = InferFieldsType<{
885
- * title: { type: "text"; required: true };
886
- * count: { type: "number" };
887
- * }>;
888
- * // { title: string; count: number }
889
- * ```
890
- */
891
- type InferFieldsType<F extends Record<string, VexField>> = 0 extends (1 & F) ? {
892
- [x: string]: unknown;
893
- } : {
894
- [K in NonTabsFieldKeys<F> & keyof F]: InferFieldType<F[K] & VexField>;
895
- } & UnionToIntersection<ExpandTabsFields<F>>;
896
- /**
897
- * Convert a union to an intersection.
898
- * Used to merge expanded tab objects into a single type.
899
- */
900
- type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
901
-
902
- /**
903
- * Responsive breakpoint for the live preview iframe.
904
- */
905
- interface LivePreviewBreakpoint {
906
- /** Display label (e.g., "Mobile", "Desktop") */
907
- label: string;
908
- /** Viewport width in pixels */
909
- width: number;
910
- /** Viewport height in pixels */
911
- height: number;
912
- /** Lucide icon name for the breakpoint button */
913
- icon?: "smartphone" | "tablet" | "laptop" | "monitor";
914
- }
915
- /**
916
- * Live preview configuration for a collection.
917
- * Works with or without `versions.drafts` — the admin panel writes
918
- * a transient preview snapshot on form changes regardless.
919
- *
920
- * @typeParam TFields - The collection's field definitions, used to type the `doc`
921
- * parameter in the `url` function. Defaults to `Record<string, VexField>`.
922
- */
923
- interface LivePreviewConfig<TFields extends Record<string, VexField> = Record<string, VexField>> {
924
- /**
925
- * URL for the preview iframe.
926
- * - String: static URL (e.g., "/preview/pages")
927
- * - Function: receives the document data (typed from the collection's fields), returns URL
928
- *
929
- * @param doc - The current document data including `_id` and all collection fields
930
- */
931
- url: string | ((doc: {
932
- _id: string;
933
- } & InferFieldsType<TFields>) => string);
934
- /**
935
- * Breakpoints for responsive preview.
936
- * Overrides `admin.livePreview.breakpoints` if set.
937
- */
938
- breakpoints?: LivePreviewBreakpoint[];
939
- /**
940
- * Fields that trigger URL recomputation when changed.
941
- * - Not set: URL recomputes on every save
942
- * - Set: URL only recomputes when these fields change
943
- * - Empty array: URL never recomputes (only content refreshes)
944
- */
945
- reloadOnFields?: string[];
946
- }
947
- /**
948
- * Global live preview configuration on the admin config.
949
- * Provides defaults that individual collections can override.
950
- */
951
- interface AdminLivePreviewConfig {
952
- /** Default breakpoints for all collections with live preview */
953
- breakpoints?: LivePreviewBreakpoint[];
954
- }
955
-
956
- /**
957
- * System field keys injected into all user collection schemas.
958
- * `vex_status` is always present; `vex_version` and `vex_publishedAt`
959
- * are added when `versions.drafts` is enabled.
960
- */
961
- type VersioningFieldKeys = "vex_status" | "vex_version" | "vex_publishedAt";
962
- /**
963
- * Admin UI configuration for a collection.
964
- * Controls how the collection appears and behaves in the admin panel.
965
- */
966
- interface CollectionAdminConfig<TFields extends Record<string, VexField> = Record<string, VexField>, TExtraKeys extends string = never> {
967
- /**
968
- * Group this collection under a heading in the sidebar.
969
- * Collections with the same group string are grouped together.
970
- */
971
- group?: string;
972
- /**
973
- * Icon name for the collection in the sidebar.
974
- * Uses Lucide icon names (e.g. `"file-text"`, `"users"`, `"settings"`).
975
- */
976
- icon?: string;
977
- /**
978
- * Field key to use as the document title in list views.
979
- * Should reference a text-like field from the collection's fields,
980
- * or an auth field key when `auth` is provided.
981
- */
982
- useAsTitle?: keyof TFields | TExtraKeys;
983
- /**
984
- * Field keys to show as default columns in the list view.
985
- * If not set, all fields are shown (with `_id` first).
986
- * When set, only the specified fields are shown — `_id` is only
987
- * included if explicitly listed.
988
- * When `auth` is provided, auth field keys (e.g. `"email"`) are also accepted.
989
- */
990
- defaultColumns?: ("_id" | keyof TFields | TExtraKeys)[];
991
- /**
992
- * Disable the "Create New" button in the admin list view.
993
- *
994
- * Default: `false`
995
- */
996
- disableCreate?: boolean;
997
- /**
998
- * Disable the delete action in the admin panel.
999
- *
1000
- * Default: `false`
1001
- */
1002
- disableDelete?: boolean;
1003
- /**
1004
- * Live preview configuration.
1005
- * When set, the admin edit view shows a toggleable side-by-side preview panel
1006
- * with an iframe loading the configured URL.
1007
- *
1008
- * Works with or without `versions.drafts` — the admin panel writes a transient
1009
- * preview snapshot to `vex_versions` on form changes. The preview iframe fetches
1010
- * this snapshot via `vexQuery` with Convex's real-time subscriptions.
1011
- */
1012
- livePreview?: LivePreviewConfig<TFields>;
1013
- }
1014
- /**
1015
- * Index definition for a collection.
1016
- * Compound indexes include multiple fields — order matters.
1017
- */
1018
- interface IndexConfig<TFields extends Record<string, VexField> = Record<string, VexField>, TExtraKeys extends string = never> {
1019
- /**
1020
- * Index name (must be unique within the collection).
1021
- * Convention: `"by_<field>"` for single-field, `"by_<field1>_<field2>"` for compound.
1022
- */
1023
- name: string;
1024
- /**
1025
- * Field names to include in the index. Order matters for compound indexes.
1026
- */
1027
- fields: (keyof TFields & string | TExtraKeys)[];
1028
- }
1029
- /**
1030
- * Search index definition for a collection.
1031
- * Enables full-text search on a field with optional filter fields.
1032
- */
1033
- interface SearchIndexConfig<TFields extends Record<string, VexField> = Record<string, VexField>, TExtraKeys extends string = never> {
1034
- /**
1035
- * Search index name (must be unique within the collection).
1036
- * Convention: `"search_<field>"`.
1037
- */
1038
- name: string;
1039
- /**
1040
- * The field to perform full-text search on.
1041
- */
1042
- searchField: keyof TFields & string | TExtraKeys;
1043
- /**
1044
- * Optional fields to filter search results by.
1045
- */
1046
- filterFields?: (keyof TFields & string | TExtraKeys)[];
1047
- }
1048
- /**
1049
- * Configuration for versioning and draft/publish workflow on a collection.
1050
- * When `drafts` is enabled, the admin panel shows Save Draft + Publish
1051
- * instead of a simple Save button.
1052
- */
1053
- interface VersionsConfig {
1054
- /**
1055
- * Enable the draft/publish workflow.
1056
- * When true, new documents start as drafts and must be explicitly published.
1057
- * The schema gets `_status`, `_version`, and `_publishedAt` fields injected.
1058
- *
1059
- * Default: `false`
1060
- */
1061
- drafts?: boolean;
1062
- /**
1063
- * Enable autosave. Requires `drafts: true`.
1064
- * When `true`, uses a default 2000ms interval.
1065
- * When an object, specify a custom interval.
1066
- *
1067
- * Default: `false`
1068
- */
1069
- autosave?: boolean | {
1070
- /** Interval in milliseconds between autosaves. Default: 2000 */
1071
- interval: number;
1072
- };
1073
- /**
1074
- * Maximum number of versions to keep per document.
1075
- * Oldest non-published versions are deleted when exceeded.
1076
- * `0` means unlimited.
1077
- *
1078
- * Default: `100`
1079
- */
1080
- maxPerDoc?: number;
1081
- }
1082
- /**
1083
- * A collection definition. Users create these as plain objects
1084
- * with `as const satisfies VexCollection`.
1085
- *
1086
- * @example
1087
- * ```ts
1088
- * const posts = {
1089
- * slug: "posts",
1090
- * fields: {
1091
- * title: { type: "text", label: "Title", required: true },
1092
- * },
1093
- * } as const satisfies VexCollection;
1094
- * ```
1095
- */
1096
- interface VexCollection<TFields extends Record<string, any> = any, TExtraKeys extends string = string, TSlug extends string = string> {
1097
- /** The collection identifier, used in URLs and the database. */
1098
- readonly slug: TSlug;
1099
- /**
1100
- * The fields that make up documents in this collection.
1101
- * Each key becomes a field name, and the value defines
1102
- * its type (e.g. `{ type: "text" }`, `{ type: "number" }`, `{ type: "select", ... }`).
1103
- */
1104
- fields: TFields;
1105
- /**
1106
- * The name of the table generated for this collection in the
1107
- * generated vex schema file. Defaults to the collection slug.
1108
- */
1109
- tableName?: string;
1110
- /**
1111
- * Display labels for the collection in the admin UI.
1112
- * If not provided, labels are derived from the collection slug.
1113
- */
1114
- labels?: {
1115
- /** Singular label (e.g. `"Post"`). */
1116
- singular?: string;
1117
- /** Plural label (e.g. `"Posts"`). */
1118
- plural?: string;
1119
- };
1120
- /**
1121
- * Admin UI configuration for this collection.
1122
- * Controls sidebar grouping, icons, list columns, and permissions.
1123
- */
1124
- admin?: CollectionAdminConfig<TFields, TExtraKeys>;
1125
- /**
1126
- * Database indexes for this collection.
1127
- */
1128
- indexes?: IndexConfig<TFields, TExtraKeys>[];
1129
- /**
1130
- * Search indexes for full-text search on this collection.
1131
- */
1132
- searchIndexes?: SearchIndexConfig<TFields, TExtraKeys>[];
1133
- /**
1134
- * Versioning and draft/publish workflow configuration.
1135
- * When `versions.drafts` is `true`, the collection gets draft/publish
1136
- * workflow in the admin panel and version history tracking.
1137
- *
1138
- * Only available on user collections (including auth-merged collections).
1139
- * NOT available on media collections — use VexMediaCollection for those.
1140
- */
1141
- versions?: VersionsConfig;
1142
- /**
1143
- * When true, the CLI generates typed per-collection query/mutation files
1144
- * for this collection (`convex/vex/api/{slug}.ts` + `convex/vex/model/api/{slug}.ts`).
1145
- *
1146
- * Defaults to `true` for user-defined collections and media collections.
1147
- * Auth adapter collections default to `false` — set explicitly to opt in.
1148
- */
1149
- generateApi?: boolean;
1150
- /**
1151
- * Internal marker set by resolveMediaCollection().
1152
- * When true, ExtractFieldKeys includes DefaultMediaFieldKeys.
1153
- * @internal — do not set manually.
1154
- */
1155
- _isMedia?: true;
1156
- /**
1157
- * TypeScript interface name used in generated `vex.types.ts`.
1158
- * If not set, auto-generated from slug via PascalCase conversion.
1159
- * @example "BlogPost"
1160
- */
1161
- interfaceName?: string;
1162
- /**
1163
- * Type helper — use `typeof collection._docType` to get the
1164
- * inferred document shape for this collection.
1165
- */
1166
- readonly _docType?: InferFieldsType<TFields>;
1167
- }
1168
- /**
1169
- * A VexCollection with erased generics, suitable for heterogeneous arrays.
1170
- *
1171
- * @deprecated Use `VexCollection` instead — its default generics accept the
1172
- * same values. This alias is kept only for backwards compatibility.
1173
- */
1174
- type AnyVexCollection = VexCollection<any, any>;
1175
-
1176
- /**
1177
- * Admin UI configuration for a global.
1178
- * Controls how the global appears and behaves in the admin panel.
1179
- */
1180
- interface GlobalAdminConfig<TFields extends Record<string, VexField> = Record<string, VexField>> {
1181
- /**
1182
- * Group this global under a heading in the sidebar.
1183
- * Globals with the same group string are grouped together.
1184
- */
1185
- group?: string;
1186
- /**
1187
- * Icon name for the global in the sidebar.
1188
- * Uses Lucide icon names (e.g. `"settings"`, `"globe"`, `"layout"`).
1189
- */
1190
- icon?: string;
1191
- /**
1192
- * Field key to use as the document title in the admin panel.
1193
- * Should reference a text-like field from the global's fields.
1194
- */
1195
- useAsTitle?: keyof TFields;
1196
- /**
1197
- * Live preview configuration.
1198
- * When set, the admin edit view shows a toggleable side-by-side preview panel
1199
- * with an iframe loading the configured URL.
1200
- */
1201
- livePreview?: LivePreviewConfig<TFields>;
1202
- }
1203
- /**
1204
- * A global definition. Globals are singleton documents — only one
1205
- * document exists per global. The admin panel shows them as a single
1206
- * editable form, not a list view.
1207
- *
1208
- * @example
1209
- * ```ts
1210
- * const siteSettings = defineGlobal({
1211
- * slug: "site_settings",
1212
- * label: "Site Settings",
1213
- * fields: {
1214
- * siteName: text({ label: "Site Name", required: true }),
1215
- * description: text({ label: "Description" }),
1216
- * },
1217
- * admin: { useAsTitle: "siteName" },
1218
- * });
1219
- * ```
1220
- */
1221
- interface VexGlobal<TFields extends Record<string, VexField> = Record<string, VexField>, TSlug extends string = string> {
1222
- /** The global identifier, used in URLs and the database. */
1223
- readonly slug: TSlug;
1224
- /**
1225
- * The fields that make up this global document.
1226
- */
1227
- fields: TFields;
1228
- /**
1229
- * Display label for the global in the admin UI.
1230
- * If not provided, the label is derived from the global slug.
1231
- */
1232
- label?: string;
1233
- /**
1234
- * The name of the table generated for this global in the
1235
- * generated vex schema file. Defaults to the global slug.
1236
- */
1237
- tableName?: string;
1238
- /**
1239
- * Admin UI configuration for this global.
1240
- * Controls sidebar grouping, icons, and live preview.
1241
- */
1242
- admin?: GlobalAdminConfig<TFields>;
1243
- /**
1244
- * Versioning and draft/publish workflow configuration.
1245
- * When `drafts` is enabled, the admin panel shows Save Draft + Publish
1246
- * and version history, same as versioned collections.
1247
- */
1248
- versions?: VersionsConfig;
1249
- /**
1250
- * TypeScript interface name used in generated `vex.types.ts`.
1251
- * If not set, auto-generated from slug via PascalCase conversion.
1252
- * @example "SiteSettings"
1253
- */
1254
- interfaceName?: string;
1255
- /**
1256
- * Type helper — use `typeof global._docType` to get the
1257
- * inferred document shape for this global.
1258
- */
1259
- readonly _docType?: InferFieldsType<TFields>;
1260
- }
1261
-
1262
- /**
1263
- * A resolved index ready for code generation.
1264
- */
1265
- interface ResolvedIndex {
1266
- /** Index name (e.g., "by_slug") */
1267
- name: string;
1268
- /** Field names included in the index */
1269
- fields: string[];
1270
- }
1271
- /**
1272
- * A resolved search index ready for code generation.
1273
- */
1274
- interface ResolvedSearchIndex {
1275
- /** Search index name (e.g., "search_title") */
1276
- name: string;
1277
- /** The field to perform full-text search on (must be a string field) */
1278
- searchField: string;
1279
- /** Fields that can be used to filter search results */
1280
- filterFields: string[];
1281
- }
1282
- /**
1283
- * A type-level map from collection slug to field key union.
1284
- * Used by auth adapters to provide LSP autocomplete for auth field keys
1285
- * in `defaultColumns`, `useAsTitle`, etc.
1286
- *
1287
- * @example
1288
- * ```ts
1289
- * type MyMap = {
1290
- * user: "email" | "createdAt" | "updatedAt";
1291
- * session: "userId" | "token" | "expiresAt";
1292
- * };
1293
- * ```
1294
- */
1295
- type AuthFieldKeyMap = Record<string, string>;
1296
- /**
1297
- * The auth adapter object stored in `VexConfig.auth`.
1298
- * Returned by `vexBetterAuth()`.
1299
- *
1300
- * Uses VexCollection[] — auth tables are defined with the same
1301
- * VexField system as user collections, enabling uniform schema
1302
- * generation, column rendering, and admin UI support.
1303
- *
1304
- * @typeParam TFieldKeyMap - Type-level map from collection slug to field key union.
1305
- * Enables LSP autocomplete for auth field keys in `defaultColumns`, `useAsTitle`, etc.
1306
- */
1307
- interface VexAuthAdapter<TFieldKeyMap extends AuthFieldKeyMap = AuthFieldKeyMap> {
1308
- /** Auth provider identifier (e.g., "better-auth") */
1309
- readonly name: string;
1310
- /**
1311
- * All auth collections (user, session, account, verification, plugin tables, etc.).
1312
- * Already includes plugin-contributed tables and field extensions.
1313
- * Core's schema generator uses these as the base, then merges any
1314
- * user-defined collection configs on top for admin UI customization.
1315
- */
1316
- collections: VexCollection[];
1317
- /**
1318
- * Phantom property — never set at runtime.
1319
- * Carries the type-level field key map for LSP autocomplete.
1320
- */
1321
- readonly _fieldKeyMap?: TFieldKeyMap;
1322
- }
1323
- /**
1324
- * Extract field keys from an auth adapter's collection that matches a given slug.
1325
- * Looks up from the adapter's `_fieldKeyMap` phantom type.
1326
- *
1327
- * @example
1328
- * type UserAuthFields = AuthCollectionFieldKeys<typeof auth, "user">;
1329
- * // => "email" | "emailVerified" | "image" | "createdAt" | "updatedAt"
1330
- */
1331
- type AuthCollectionFieldKeys<TAuth extends VexAuthAdapter<any>, TSlug extends string> = TAuth extends VexAuthAdapter<infer TMap> ? TSlug extends keyof TMap ? TMap[TSlug] & string : never : never;
1332
- /**
1333
- * @deprecated Use AuthCollectionFieldKeys instead.
1334
- */
1335
- type AuthTableFieldKeys<TAuth extends VexAuthAdapter<any>, TSlug extends string> = AuthCollectionFieldKeys<TAuth, TSlug>;
1336
-
1337
- /** Resolved admin panel configuration */
1338
- interface AdminConfig {
1339
- /** Collection slug used for user authentication */
1340
- user: string;
1341
- /** Page metadata */
1342
- meta: {
1343
- /** Suffix appended to the page title in the browser tab */
1344
- titleSuffix: string;
1345
- /** Path to the favicon for the admin panel */
1346
- favicon: string;
1347
- };
1348
- /** Sidebar navigation configuration */
1349
- sidebar: {
1350
- /** Whether global collections are hidden from the sidebar */
1351
- hideGlobals: boolean;
1352
- };
1353
- /** Global live preview defaults */
1354
- livePreview?: AdminLivePreviewConfig;
1355
- /** Onboarding tour configuration */
1356
- onboarding: {
1357
- /** Whether the onboarding tour is disabled. Default: false (tour enabled) */
1358
- disabled: boolean;
1359
- };
1360
- }
1361
- /**
1362
- * Admin page metadata configuration.
1363
- *
1364
- * Default:
1365
- * ```
1366
- * titleSuffix: "| Admin"
1367
- * favicon: "/favicon.ico"
1368
- * ```
1369
- */
1370
- interface AdminMetaInput {
1371
- /**
1372
- * Suffix appended to the page title in the browser tab.
1373
- *
1374
- * Default: `"| Admin"`
1375
- */
1376
- titleSuffix?: string;
1377
- /**
1378
- * Path to the favicon for the admin panel.
1379
- *
1380
- * Default: `"/favicon.ico"`
1381
- */
1382
- favicon?: string;
1383
- }
1384
- /**
1385
- * Admin sidebar configuration.
1386
- *
1387
- * Default:
1388
- * ```
1389
- * hideGlobals: false
1390
- * ```
1391
- */
1392
- interface AdminSidebarInput {
1393
- /**
1394
- * Hide global collections from the sidebar navigation.
1395
- *
1396
- * Default: `false`
1397
- */
1398
- hideGlobals?: boolean;
1399
- }
1400
- /**
1401
- * Admin panel configuration.
1402
- *
1403
- * Default:
1404
- * ```
1405
- * user: "users"
1406
- * meta:
1407
- * titleSuffix: "| Admin"
1408
- * favicon: "/favicon.ico"
1409
- * sidebar:
1410
- * hideGlobals: false
1411
- * ```
1412
- */
1413
- interface AdminConfigInput {
1414
- /**
1415
- * Collection slug to use for user authentication.
1416
- *
1417
- * Default: `"users"`
1418
- */
1419
- user?: string;
1420
- /**
1421
- * Admin page metadata configuration.
1422
- *
1423
- * Default:
1424
- * ```
1425
- * titleSuffix: "| Admin"
1426
- * favicon: "/favicon.ico"
1427
- * ```
1428
- */
1429
- meta?: AdminMetaInput;
1430
- /**
1431
- * Sidebar navigation configuration.
1432
- *
1433
- * Default:
1434
- * ```
1435
- * hideGlobals: false
1436
- * ```
1437
- */
1438
- sidebar?: AdminSidebarInput;
1439
- /**
1440
- * Global live preview defaults.
1441
- * Individual collections can override these breakpoints.
1442
- */
1443
- livePreview?: AdminLivePreviewConfig;
1444
- /**
1445
- * Onboarding tour configuration.
1446
- *
1447
- * Default:
1448
- * ```
1449
- * disabled: false
1450
- * ```
1451
- */
1452
- onboarding?: {
1453
- /**
1454
- * Disable the onboarding tour for all users.
1455
- *
1456
- * Default: `false`
1457
- */
1458
- disabled?: boolean;
1459
- };
1460
- }
1461
-
1462
- /** Schema generation configuration. */
1463
- interface SchemaConfig {
1464
- /**
1465
- * Output path for the generated schema file, relative to project root.
1466
- *
1467
- * Default: `"convex/vex.schema.ts"`
1468
- */
1469
- outputPath: string;
1470
- /**
1471
- * Output path for the generated TypeScript types file, relative to project root.
1472
- * Contains typed interfaces for all collections, blocks, and globals.
1473
- *
1474
- * Default: `"convex/vex.types.ts"`
1475
- */
1476
- typesOutputPath: string;
1477
- /**
1478
- * Automatically backfill existing documents when a new required field
1479
- * with a `defaultValue` is added.
1480
- *
1481
- * Default: `true`
1482
- */
1483
- autoMigrate: boolean;
1484
- /**
1485
- * Automatically remove table entries from `schema.ts` when collections
1486
- * or globals are removed from `vex.config.ts`.
1487
- *
1488
- * Only removes simple entries (bare `exportName,` lines). Entries using
1489
- * `extendTable()`, constant keys, or other custom patterns are left
1490
- * untouched with a warning.
1491
- *
1492
- * Default: `false`
1493
- */
1494
- autoRemove: boolean;
1495
- }
1496
- /** Schema generation configuration input (all fields optional). */
1497
- interface SchemaConfigInput {
1498
- /**
1499
- * Output path for the generated schema file, relative to project root.
1500
- * Can change the filename (e.g., `"convex/generated-schema.ts"`) or
1501
- * the directory (e.g., `"src/convex/vex.schema.ts"`).
1502
- *
1503
- * Default: `"convex/vex.schema.ts"`
1504
- */
1505
- outputPath?: string;
1506
- /**
1507
- * Output path for the generated TypeScript types file, relative to project root.
1508
- * Contains typed interfaces for all collections, blocks, and globals.
1509
- *
1510
- * Default: `"convex/vex.types.ts"`
1511
- */
1512
- typesOutputPath?: string;
1513
- /**
1514
- * Automatically backfill existing documents when a new required field
1515
- * with a `defaultValue` is added.
1516
- *
1517
- * Default: `true`
1518
- */
1519
- autoMigrate?: boolean;
1520
- /**
1521
- * Automatically remove table entries from `schema.ts` when collections
1522
- * or globals are removed from `vex.config.ts`.
1523
- *
1524
- * Only removes simple entries (bare `exportName,` lines). Entries using
1525
- * `extendTable()`, constant keys, or other custom patterns are left
1526
- * untouched with a warning.
1527
- *
1528
- * Default: `false`
1529
- */
1530
- autoRemove?: boolean;
1531
- }
1532
-
1533
- /**
1534
- * Interface that file storage plugins must implement.
1535
- * Each method operates on the storage provider (e.g., Convex file storage, S3, Cloudinary).
1536
- */
1537
- interface FileStorageAdapter {
1538
- /** Identifier for the storage provider (e.g., "convex", "s3", "cloudinary"). */
1539
- readonly name: string;
1540
- /**
1541
- * The Convex value type string for the storageId field in media collections.
1542
- * Determines the schema type at generation time.
1543
- *
1544
- * - Convex adapter: `'v.id("_storage")'` — typed reference to Convex file storage
1545
- * - Generic adapters: `'v.string()'` — plain string for external storage URLs/IDs
1546
- */
1547
- readonly storageIdValueType: string;
1548
- /**
1549
- * Get a presigned upload URL from the storage provider.
1550
- * Called by the admin panel before uploading a file.
1551
- *
1552
- * @returns A URL string that accepts file uploads via PUT/POST.
1553
- */
1554
- getUploadUrl: () => Promise<string>;
1555
- /**
1556
- * Resolve a storage ID to an accessible URL.
1557
- *
1558
- * @param props.storageId - The storage provider's file identifier.
1559
- * @returns A URL string for accessing the file, or null if the file doesn't exist.
1560
- */
1561
- getUrl: (props: {
1562
- storageId: string;
1563
- }) => Promise<string | null>;
1564
- /**
1565
- * Delete a file from the storage provider.
1566
- *
1567
- * @param props.storageId - The storage provider's file identifier.
1568
- */
1569
- deleteFile: (props: {
1570
- storageId: string;
1571
- }) => Promise<void>;
1572
- }
1573
- /**
1574
- * Fields that are auto-injected into every media collection and cannot be overridden.
1575
- */
1576
- declare const LOCKED_MEDIA_FIELDS: readonly ["storageId", "filename", "mimeType", "size"];
1577
- type LockedMediaField = (typeof LOCKED_MEDIA_FIELDS)[number];
1578
- /**
1579
- * Fields that are auto-injected but CAN be overridden by the user.
1580
- */
1581
- declare const OVERRIDABLE_MEDIA_FIELDS: readonly ["url", "alt", "width", "height"];
1582
- type OverridableMediaField = (typeof OVERRIDABLE_MEDIA_FIELDS)[number];
1583
- /**
1584
- * Keys of all default media fields auto-injected by `defineConfig()`.
1585
- * Used as extra autocomplete keys in `CollectionAdminConfig` so that
1586
- * `useAsTitle`, `defaultColumns`, etc. suggest both user fields and preset fields.
1587
- */
1588
- type DefaultMediaFieldKeys = LockedMediaField | OverridableMediaField;
1589
- /**
1590
- * A media collection definition. Users create these as plain objects.
1591
- * Default media fields (storageId, filename, mimeType, size, url, alt, width, height)
1592
- * are injected automatically by `defineConfig()`.
1593
- *
1594
- * The `fields` record contains ONLY user-defined additional fields or overrides
1595
- * of overridable defaults (url, alt, width, height).
1596
- */
1597
- interface VexMediaCollection<TFields extends Record<string, VexField> = any, TSlug extends string = string> {
1598
- readonly slug: TSlug;
1599
- fields?: TFields;
1600
- tableName?: string;
1601
- labels?: {
1602
- singular?: string;
1603
- plural?: string;
1604
- };
1605
- admin?: CollectionAdminConfig<TFields, DefaultMediaFieldKeys>;
1606
- }
1607
- /**
1608
- * The resolved media configuration on VexConfig.
1609
- */
1610
- interface MediaConfig {
1611
- collections: VexCollection[];
1612
- storageAdapter: FileStorageAdapter;
1613
- }
1614
- /**
1615
- * Client-safe media configuration with non-serializable parts stripped.
1616
- * Used when passing config across RSC serialization boundaries (e.g., to client components).
1617
- */
1618
- interface ClientMediaConfig {
1619
- collections: VexCollection[];
1620
- }
1621
- /**
1622
- * Input shape for the `media` field on VexConfigInput.
1623
- */
1624
- interface MediaConfigInput {
1625
- collections: VexMediaCollection[];
1626
- storageAdapter: FileStorageAdapter;
1627
- }
1628
-
1629
- /**
1630
- * Extract the slug literal type from a VexCollection or VexGlobal.
1631
- *
1632
- * @example
1633
- * type S = ExtractSlug<typeof posts>; // "posts"
1634
- */
1635
- type ExtractSlug<T> = T extends {
1636
- slug: infer S extends string;
1637
- } ? S : never;
1638
- /**
1639
- * Expand field keys from a fields record, replacing tabs field keys with their tab slugs.
1640
- */
1641
- type ExpandedFieldKeys<TFields extends Record<string, any>> = {
1642
- [K in keyof TFields & string]: (TFields[K] & VexField) extends {
1643
- type: "tabs";
1644
- } ? (TFields[K] & TabsFieldDef)["tabs"][number] extends TabDef<infer TSlug> ? TSlug : never : K;
1645
- }[keyof TFields & string];
1646
- /**
1647
- * Extract field keys from a VexCollection (including auth extra keys) or VexGlobal.
1648
- * Tabs fields are expanded: instead of the tabs field name, the tab slugs are returned.
1649
- *
1650
- * @example
1651
- * type K = ExtractFieldKeys<typeof posts>; // "title" | "slug" | "status" | "featured"
1652
- */
1653
- type ExtractFieldKeys<T> = T extends VexCollection<infer TFields, infer TExtraKeys> ? T extends {
1654
- _isMedia: true;
1655
- } ? ExpandedFieldKeys<TFields> | (TExtraKeys & string) | DefaultMediaFieldKeys : ExpandedFieldKeys<TFields> | (TExtraKeys & string) : T extends VexMediaCollection<infer TFields> ? ExpandedFieldKeys<TFields> | DefaultMediaFieldKeys : T extends VexGlobal<infer TFields> ? ExpandedFieldKeys<TFields> : never;
1656
- /**
1657
- * Extract the inferred document type from a VexCollection or VexGlobal.
1658
- * Includes `_id` (system field) and auth extra keys (typed as `string`).
1659
- */
1660
- type ExtractDocType<T> = T extends VexCollection<infer TFields, infer TExtraKeys> ? {
1661
- _id: string;
1662
- } & InferFieldsType<TFields> & {
1663
- [K in TExtraKeys & string]: string;
1664
- } : T extends VexMediaCollection<infer TFields> ? {
1665
- _id: string;
1666
- } & InferFieldsType<TFields> & {
1667
- [K in DefaultMediaFieldKeys]: string | number;
1668
- } : T extends VexGlobal<infer TFields> ? {
1669
- _id: string;
1670
- } & InferFieldsType<TFields> : never;
1671
- /**
1672
- * Lookup a resource (collection or global) by slug from a tuple of resources.
1673
- *
1674
- * @example
1675
- * type P = LookupBySlug<[typeof posts, typeof users], "posts">; // typeof posts
1676
- */
1677
- type LookupBySlug<TResources extends readonly any[], TSlug extends string> = TResources extends readonly [infer Head, ...infer Tail] ? Head extends {
1678
- slug: TSlug;
1679
- } ? Head : LookupBySlug<Tail, TSlug> : never;
1680
- /**
1681
- * Extract all slugs from a tuple of resources.
1682
- */
1683
- type ExtractSlugs<TResources extends readonly any[]> = TResources extends readonly [infer Head, ...infer Tail] ? ExtractSlug<Head> | ExtractSlugs<Tail> : never;
1684
- /**
1685
- * The return type for a permission check on a resource.
1686
- * - `boolean` — applies uniformly to all fields (true = all allowed, false = all denied)
1687
- * - `{ mode: 'allow', fields: FieldKey[] }` — only listed fields are allowed; unlisted are denied
1688
- * - `{ mode: 'deny', fields: FieldKey[] }` — listed fields are denied; unlisted are allowed
1689
- */
1690
- type FieldPermissionResult<TFieldKeys extends string> = boolean | {
1691
- mode: "allow";
1692
- fields: TFieldKeys[];
1693
- } | {
1694
- mode: "deny";
1695
- fields: TFieldKeys[];
1696
- };
1697
- /**
1698
- * A permission check can be a static value or a dynamic function.
1699
- * Dynamic functions receive document data, user, and optional organization for
1700
- * context-aware checks.
1701
- *
1702
- * @typeParam TFieldKeys - Union of field key strings for this resource
1703
- * @typeParam TDocType - The document type for this resource
1704
- * @typeParam TUser - The user type (inferred from user collection or explicit override)
1705
- * @typeParam TOrg - The organization type (inferred from org collection, or never)
1706
- */
1707
- type PermissionCheck<TFieldKeys extends string, TDocType = Record<string, any>, TUser = Record<string, any>, TOrg = never> = FieldPermissionResult<TFieldKeys> | ((props: PermissionCallbackProps<TDocType, TUser, TOrg>) => FieldPermissionResult<TFieldKeys>);
1708
- /**
1709
- * Props passed to dynamic permission check callbacks.
1710
- * When TOrg is `never`, the `organization` field is omitted entirely.
1711
- */
1712
- type PermissionCallbackProps<TDocType = Record<string, any>, TUser = Record<string, any>, TOrg = never> = [TOrg] extends [never] ? {
1713
- data: TDocType;
1714
- user: TUser;
1715
- } : {
1716
- data: TDocType;
1717
- user: TUser;
1718
- organization: TOrg;
1719
- };
1720
- /** The four CRUD actions supported by the permission system. */
1721
- type AccessAction = "create" | "read" | "update" | "delete";
1722
- /**
1723
- * Permission map for a single role on a single resource.
1724
- * Each action is optional — missing actions default to permissive (all-true).
1725
- */
1726
- type ResourcePermissions<TFieldKeys extends string, TDocType = Record<string, any>, TUser = Record<string, any>, TOrg = never> = Partial<{
1727
- [Action in AccessAction]: PermissionCheck<TFieldKeys, TDocType, TUser, TOrg>;
1728
- }>;
1729
- /**
1730
- * Build a permission entry for a single resource element.
1731
- * Produces `{ [slug]?: boolean | ResourcePermissions<...> }`.
1732
- */
1733
- type SingleResourceEntry<R, TUser, TOrg> = R extends {
1734
- readonly slug: infer S extends string;
1735
- } ? {
1736
- [K in S]?: boolean | ResourcePermissions<ExtractFieldKeys<R>, ExtractDocType<R>, TUser, TOrg>;
1737
- } : {};
1738
- /**
1739
- * Recursively intersect per-resource permission entries from a tuple.
1740
- * Builds `{ articles?: ... } & { posts?: ... } & { users?: ... }` etc.
1741
- * TS eagerly resolves intersection members for LSP autocomplete.
1742
- */
1743
- type ResourcePermissionMap<TResources extends readonly any[], TUser, TOrg> = TResources extends readonly [infer Head, ...infer Tail] ? SingleResourceEntry<Head, TUser, TOrg> & ResourcePermissionMap<Tail, TUser, TOrg> : {};
1744
- /**
1745
- * Permission check for admin panel access.
1746
- * - `boolean` — static allow/deny
1747
- * - `(props) => boolean` — dynamic check based on user (and org if configured)
1748
- *
1749
- * Defaults to `false` if not specified for a role.
1750
- */
1751
- type AdminPermissionCheck<TUser = Record<string, any>, TOrg = never> = boolean | (([TOrg] extends [never] ? (props: {
1752
- user: TUser;
1753
- }) => boolean : (props: {
1754
- user: TUser;
1755
- organization: TOrg;
1756
- }) => boolean));
1757
- type RolesWithPermissions<TRoles extends string, TResources extends readonly any[], TUser = Record<string, any>, TOrg = never> = {
1758
- [Role in TRoles]: ResourcePermissionMap<TResources, TUser, TOrg> & {
1759
- /** Whether this role can access the admin panel. Defaults to false if not specified. */
1760
- admin?: AdminPermissionCheck<TUser, TOrg>;
1761
- };
1762
- };
1763
- /**
1764
- * Input shape for `defineAccess()` WITHOUT organization support.
1765
- * Carries generics for full type inference of roles, resources, and field keys.
1766
- */
1767
- interface VexAccessInputBase<TRoles extends readonly string[], TResources extends readonly any[], TUserCollection extends VexCollection<any, any, any>, TUser> {
1768
- /** Array of role name strings. Use `as const` for literal type inference. */
1769
- roles: TRoles;
1770
- /**
1771
- * Roles that can access the admin panel and impersonate other users.
1772
- * Must be a subset of `roles`. Defaults to all roles if not specified.
1773
- */
1774
- adminRoles?: readonly (TRoles[number] & string)[];
1775
- /**
1776
- * The resources (collections, media collections, globals) that become entries
1777
- * in the permission matrix. When omitted, all collections and globals in the
1778
- * config are available — but type inference only works for explicitly listed resources.
1779
- */
1780
- resources?: TResources;
1781
- /**
1782
- * The user collection. Used to infer the user type for dynamic permission
1783
- * callbacks `({ data, user })`. Must be a collection created with `defineCollection`.
1784
- */
1785
- userCollection: TUserCollection;
1786
- /**
1787
- * Optional explicit user type override. When provided, this type is used
1788
- * for the `user` param in permission callbacks instead of inferring from
1789
- * the user collection's fields.
1790
- */
1791
- userType?: TUser;
1792
- /** The role-to-resource-to-action permission matrix. */
1793
- permissions: RolesWithPermissions<TRoles[number] & string, TResources, TUser extends undefined ? ExtractDocType<TUserCollection> : NonNullable<TUser>, never>;
1794
- }
1795
- /**
1796
- * Input shape for `defineAccess()` WITH organization support.
1797
- * Extends the base input with org collection and user org field.
1798
- */
1799
- interface VexAccessInputWithOrg<TRoles extends readonly string[], TResources extends readonly any[], TUserCollection extends VexCollection<any, any, any>, TUser, TOrgCollection extends VexCollection<any, any, any>, TOrg> {
1800
- roles: TRoles;
1801
- /**
1802
- * Roles that can access the admin panel and impersonate other users.
1803
- * Must be a subset of `roles`. Defaults to all roles if not specified.
1804
- */
1805
- adminRoles?: readonly (TRoles[number] & string)[];
1806
- resources?: TResources;
1807
- userCollection: TUserCollection;
1808
- userType?: TUser;
1809
- /**
1810
- * The organization collection. Used to infer the organization type for
1811
- * dynamic permission callbacks `({ data, user, organization })`.
1812
- */
1813
- orgCollection: TOrgCollection;
1814
- /**
1815
- * Optional explicit organization type override. When provided, this type is
1816
- * used for the `organization` param in callbacks instead of inferring from
1817
- * the org collection's fields.
1818
- */
1819
- orgType?: TOrg;
1820
- /**
1821
- * The field key on the user collection that relates to the organization collection.
1822
- * Used at runtime by callers to resolve the organization from the user object.
1823
- * Must be a field key on the user collection.
1824
- */
1825
- userOrgField: ExtractFieldKeys<TUserCollection>;
1826
- permissions: RolesWithPermissions<TRoles[number] & string, TResources, TUser extends undefined ? ExtractDocType<TUserCollection> : NonNullable<TUser>, TOrg extends undefined ? ExtractDocType<TOrgCollection> : NonNullable<TOrg>>;
1827
- }
1828
- /**
1829
- * Union of both input shapes. TypeScript will narrow based on presence of `orgCollection`.
1830
- */
1831
- type VexAccessInput<TRoles extends readonly string[], TResources extends readonly any[], TUserCollection extends VexCollection<any, any, any>, TUser = undefined, TOrgCollection extends VexCollection<any, any, any> = never, TOrg = undefined> = [TOrgCollection] extends [never] ? VexAccessInputBase<TRoles, TResources, TUserCollection, TUser> : VexAccessInputWithOrg<TRoles, TResources, TUserCollection, TUser, TOrgCollection, TOrg>;
1832
- /**
1833
- * Resolved access config stored on `VexConfig.access`.
1834
- * Type-erased version for storage in the config object.
1835
- * Use `hasPermission()` for type-safe runtime access.
1836
- */
1837
- interface VexAccessConfig {
1838
- /** The role name strings. */
1839
- roles: readonly string[];
1840
- /** Roles that can access the admin panel and impersonate. */
1841
- adminRoles: readonly string[];
1842
- /** Slug of the user collection. */
1843
- userCollection: string;
1844
- /**
1845
- * Slug of the organization collection, if org support is enabled.
1846
- */
1847
- orgCollection?: string;
1848
- /**
1849
- * The field key on the user collection that relates to the org collection.
1850
- * Present only when orgCollection is set.
1851
- */
1852
- userOrgField?: string;
1853
- /**
1854
- * The permission matrix.
1855
- * Type-erased to `Record<string, ...>` for runtime consumption.
1856
- * Use `hasPermission()` for type-safe access.
1857
- */
1858
- permissions: Record<string, // role
1859
- Record<string, // resource slug
1860
- boolean | Partial<Record<AccessAction, PermissionCheck<string, any, any, any>>>> | undefined>;
1861
- }
1862
-
1863
- /**
1864
- * Responsive breakpoint configuration for block style controls.
1865
- * Keys are breakpoint names (used as Tailwind prefixes), values are min-width in pixels.
1866
- *
1867
- * @example
1868
- * ```ts
1869
- * breakpoints: {
1870
- * sm: 640,
1871
- * md: 768,
1872
- * lg: 1024,
1873
- * xl: 1280,
1874
- * }
1875
- * ```
1876
- */
1877
- type BreakpointConfig = Record<string, number>;
1878
- /** Resolved Vex CMS configuration */
1879
- interface VexConfig {
1880
- /** Base URL path for the admin panel */
1881
- basePath: string;
1882
- /** Array of collection definitions */
1883
- collections: VexCollection[];
1884
- /** Array of global definitions */
1885
- globals: VexGlobal[];
1886
- /** Admin panel configuration */
1887
- admin: AdminConfig;
1888
- /** Auth adapter — required. Use `vexBetterAuth(authConfig)` to create. */
1889
- auth: VexAuthAdapter;
1890
- /** Schema generation config */
1891
- schema: SchemaConfig;
1892
- /** Media collection configuration */
1893
- media?: MediaConfig;
1894
- /** RBAC access permissions config. Optional — if not set, all actions are allowed. */
1895
- access?: VexAccessConfig;
1896
- /** Global rich text editor adapter. Used by all richtext fields unless overridden. */
1897
- editor?: VexEditorAdapter;
1898
- /** Responsive breakpoints for block style controls. If not set, styles apply to all viewports. */
1899
- breakpoints?: BreakpointConfig;
1900
- }
1901
- /**
1902
- * Client-safe version of VexConfig with all non-serializable values stripped.
1903
- * Use this when passing config across RSC serialization boundaries
1904
- * (e.g., from a server component to a client component).
1905
- *
1906
- * Created via `sanitizeConfigForClient(config)`.
1907
- */
1908
- interface ClientVexConfig {
1909
- basePath: string;
1910
- collections: VexCollection[];
1911
- globals: VexGlobal[];
1912
- admin: AdminConfig;
1913
- auth: VexAuthAdapter;
1914
- schema: SchemaConfig;
1915
- media?: ClientMediaConfig;
1916
- /** Global rich text editor adapter. */
1917
- editor?: VexEditorAdapter;
1918
- /** Responsive breakpoints for block style controls. If not set, styles apply to all viewports. */
1919
- breakpoints?: BreakpointConfig;
1920
- }
1921
- /**
1922
- * Input configuration for `defineConfig`. All fields are optional
1923
- * and merged with defaults at runtime.
1924
- */
1925
- interface VexConfigInput {
1926
- /**
1927
- * Base URL path for the admin panel.
1928
- *
1929
- * Default: `"/admin"`
1930
- */
1931
- basePath?: string;
1932
- /**
1933
- * Array of collection definitions.
1934
- *
1935
- * Default: []
1936
- */
1937
- collections?: VexCollection[];
1938
- /**
1939
- * Array of global definitions.
1940
- *
1941
- * Default: []
1942
- */
1943
- globals?: VexGlobal<any>[];
1944
- /**
1945
- * Admin panel configuration.
1946
- */
1947
- admin?: AdminConfigInput;
1948
- /**
1949
- * Auth adapter — **required**. Pass `vexBetterAuth(authConfig)`.
1950
- * Vex requires auth configuration to generate the schema.
1951
- */
1952
- auth: VexAuthAdapter;
1953
- /**
1954
- * Schema generation configuration.
1955
- */
1956
- schema?: SchemaConfigInput;
1957
- /**
1958
- * Media collection configuration.
1959
- * Requires a storage adapter when collections are provided.
1960
- */
1961
- media?: MediaConfigInput;
1962
- /**
1963
- * RBAC access permissions configuration.
1964
- * Created with `defineAccess()` or defined inline.
1965
- *
1966
- * If not set, the admin panel allows all actions on all fields (permissive default).
1967
- */
1968
- access?: VexAccessConfig;
1969
- /**
1970
- * Global rich text editor adapter.
1971
- * Used as the default editor for all richtext fields.
1972
- * Pass `plateEditor()` from `@vexcms/richtext/editor`.
1973
- */
1974
- editor?: VexEditorAdapter;
1975
- /**
1976
- * Responsive breakpoints for block style controls.
1977
- * Keys are Tailwind prefix names, values are min-width in pixels.
1978
- * If not set, block styles apply to all viewports (no breakpoint tabs).
1979
- */
1980
- breakpoints?: BreakpointConfig;
1981
- /**
1982
- * Plugins to apply to the config. Each plugin is a function that receives
1983
- * the config input and returns a modified config input. Plugins run
1984
- * sequentially before the config is resolved.
1985
- *
1986
- * @example
1987
- * ```ts
1988
- * export default defineConfig({
1989
- * plugins: [
1990
- * seoPlugin({ collections: ["pages"] }),
1991
- * ],
1992
- * collections: [pages],
1993
- * })
1994
- * ```
1995
- */
1996
- plugins?: VexPlugin[];
1997
- }
1998
- /**
1999
- * A Vex plugin is a function that transforms the config input.
2000
- * Plugins receive the config (with all previous plugins applied)
2001
- * and return a modified config. Use a curried function for options:
2002
- *
2003
- * @example
2004
- * ```ts
2005
- * export const myPlugin = (opts: MyOpts): VexPlugin =>
2006
- * (config) => ({
2007
- * ...config,
2008
- * collections: [...(config.collections ?? []), myCollection],
2009
- * })
2010
- * ```
2011
- */
2012
- type VexPlugin = (config: VexConfigInput) => VexConfigInput;
2013
-
2014
- declare function defineConfig(vexConfig: VexConfigInput): VexConfig;
2015
-
2016
- /**
2017
- * Creates a VexCollection with full LSP autocomplete on field names,
2018
- * `admin.useAsTitle`, `admin.defaultColumns`, index fields, etc.
2019
- *
2020
- * When `auth` is provided, auth field keys (e.g. "email", "createdAt") are
2021
- * also included in autocomplete for admin config and indexes.
2022
- *
2023
- * @example
2024
- * ```ts
2025
- * // Without auth — autocomplete for own fields
2026
- * export const posts = defineCollection({
2027
- * slug: "posts",
2028
- * fields: {
2029
- * title: { type: "text", required: true },
2030
- * status: { type: "select", options: [...] },
2031
- * },
2032
- * admin: { useAsTitle: "title" }, // autocomplete: "title" | "status"
2033
- * });
2034
- *
2035
- * // With auth — autocomplete for own fields + auth fields
2036
- * export const users = defineCollection({
2037
- * slug: "users",
2038
- * auth,
2039
- * fields: {
2040
- * name: { type: "text", required: true },
2041
- * role: { type: "select", options: [...] },
2042
- * },
2043
- * admin: {
2044
- * useAsTitle: "name", // autocomplete: "name" | "role" | "email" | "createdAt" | ...
2045
- * defaultColumns: ["name", "email"], // same autocomplete
2046
- * },
2047
- * });
2048
- * ```
2049
- */
2050
- declare function defineCollection<TFields extends Record<string, VexField>, TAuth extends VexAuthAdapter<any> | undefined = undefined, TSlug extends string = string>(props: {
2051
- readonly slug: TSlug;
2052
- fields: TFields;
2053
- auth?: TAuth;
2054
- tableName?: string;
2055
- labels?: {
2056
- singular?: string;
2057
- plural?: string;
2058
- };
2059
- admin?: CollectionAdminConfig<TFields, VersioningFieldKeys | (TAuth extends VexAuthAdapter<any> ? AuthCollectionFieldKeys<TAuth, TSlug> : never)>;
2060
- indexes?: IndexConfig<TFields, VersioningFieldKeys | (TAuth extends VexAuthAdapter<any> ? AuthCollectionFieldKeys<TAuth, TSlug> : never)>[];
2061
- searchIndexes?: SearchIndexConfig<TFields, VersioningFieldKeys | (TAuth extends VexAuthAdapter<any> ? AuthCollectionFieldKeys<TAuth, TSlug> : never)>[];
2062
- versions?: VersionsConfig;
2063
- interfaceName?: string;
2064
- }): VexCollection<TFields, VersioningFieldKeys | (TAuth extends VexAuthAdapter<any> ? AuthCollectionFieldKeys<TAuth, TSlug> : never), TSlug>;
2065
- /**
2066
- * Creates a VexMediaCollection with full LSP autocomplete on field names
2067
- * and `admin.useAsTitle`, `admin.defaultColumns`, etc.
2068
- *
2069
- * Default media fields (storageId, filename, mimeType, size, url, alt, width, height)
2070
- * are injected automatically by `defineConfig()` — only define additional or
2071
- * overridden fields here.
2072
- *
2073
- * @example
2074
- * ```ts
2075
- * export const media = defineMediaCollection({
2076
- * slug: "media",
2077
- * fields: {
2078
- * caption: { type: "text" },
2079
- * },
2080
- * admin: { useAsTitle: "filename" }, // autocomplete: "caption" | default media field keys
2081
- * });
2082
- * ```
2083
- */
2084
- declare function defineMediaCollection<TFields extends Record<string, VexField> = Record<never, VexField>, TSlug extends string = string>(props: {
2085
- readonly slug: TSlug;
2086
- fields?: TFields;
2087
- tableName?: string;
2088
- labels?: {
2089
- singular?: string;
2090
- plural?: string;
2091
- };
2092
- admin?: CollectionAdminConfig<TFields, DefaultMediaFieldKeys>;
2093
- }): VexMediaCollection<TFields, TSlug>;
2094
-
2095
- /**
2096
- * Creates a VexGlobal with full LSP autocomplete on field names
2097
- * and `admin.useAsTitle`, etc.
2098
- *
2099
- * A global is a singleton document — only one document exists per global.
2100
- * The admin panel shows it as a single editable form, not a list.
2101
- *
2102
- * @example
2103
- * ```ts
2104
- * export const siteSettings = defineGlobal({
2105
- * slug: "site_settings",
2106
- * label: "Site Settings",
2107
- * fields: {
2108
- * siteName: text({ label: "Site Name", required: true }),
2109
- * description: text({ label: "Description" }),
2110
- * },
2111
- * admin: { useAsTitle: "siteName" },
2112
- * versions: { drafts: true },
2113
- * });
2114
- * ```
2115
- */
2116
- declare function defineGlobal<TFields extends Record<string, VexField>, TSlug extends string = string>(props: {
2117
- readonly slug: TSlug;
2118
- fields: TFields;
2119
- label?: string;
2120
- tableName?: string;
2121
- admin?: GlobalAdminConfig<TFields>;
2122
- versions?: VersionsConfig;
2123
- interfaceName?: string;
2124
- }): VexGlobal<TFields, TSlug>;
2125
-
2126
- /**
2127
- * Define access permissions for the Vex CMS admin panel.
2128
- *
2129
- * This is a builder function (like `defineCollection`) that provides full
2130
- * TypeScript inference for roles, resource slugs, field keys, user type,
2131
- * and organization type.
2132
- *
2133
- * The function validates configuration in non-production and returns a
2134
- * `VexAccessConfig` for passing to `defineConfig({ access: ... })`.
2135
- *
2136
- * @returns A `VexAccessConfig` for passing to `defineConfig({ access: ... })`.
2137
- */
2138
- declare function defineAccess<const TRoles extends readonly string[], const TResources extends readonly any[], const TUserCollection extends VexCollection<any, any, any>, TUser = undefined, const TOrgCollection extends VexCollection<any, any, any> = never, TOrg = undefined>(props: VexAccessInputWithOrg<TRoles, TResources, TUserCollection, TUser, TOrgCollection, TOrg>): VexAccessConfig;
2139
- declare function defineAccess<const TRoles extends readonly string[], const TResources extends readonly any[], const TUserCollection extends VexCollection<any, any, any>, TUser = undefined>(props: VexAccessInputBase<TRoles, TResources, TUserCollection, TUser> & {
2140
- orgCollection?: never;
2141
- orgType?: never;
2142
- userOrgField?: never;
2143
- }): VexAccessConfig;
2144
-
2145
- /**
2146
- * The result of resolving field permissions for a resource action.
2147
- * Maps each field key to whether the action is allowed on that field.
2148
- */
2149
- type ResolvedFieldPermissions = Record<string, boolean>;
2150
- /**
2151
- * Check permissions for a user on a resource action.
2152
- *
2153
- * Without `fields` param → returns `boolean` (overall action access)
2154
- * With `fields` param → returns `Record<string, boolean>` for those specific fields
2155
- *
2156
- * @param props.access - The VexAccessConfig from defineAccess
2157
- * @param props.user - The user object
2158
- * @param props.userRoles - The user's role(s) as a string array
2159
- * @param props.resource - The resource slug (collection or global slug)
2160
- * @param props.action - The CRUD action to check
2161
- * @param props.data - Document data for dynamic permission checks. Defaults to `{}`.
2162
- * @param props.organization - Optional organization object for org-aware permission checks.
2163
- * @param props.fields - Specific fields to check. When provided, returns Record<string, boolean>.
2164
- * @param props.throwOnDenied - When true, throws VexAccessError instead of returning false. Default: false.
2165
- * @returns `boolean` when fields is omitted, `Record<string, boolean>` when fields is provided
2166
- * @throws {VexAccessError} When `throwOnDenied` is true and access is denied
2167
- */
2168
- declare function hasPermission(props: {
2169
- access: VexAccessConfig | undefined;
2170
- user: Record<string, any>;
2171
- userRoles: string[];
2172
- resource: string;
2173
- action: AccessAction;
2174
- data?: Record<string, any>;
2175
- organization?: Record<string, any>;
2176
- fields?: string[];
2177
- throwOnDenied?: boolean;
2178
- }): ResolvedFieldPermissions | boolean;
2179
-
2180
- /**
2181
- * Check whether a user has access to the admin panel.
2182
- *
2183
- * Evaluates the `admin` permission on each of the user's roles.
2184
- * If any role has `admin: true` (or a callback that returns true),
2185
- * the user is granted access. Returns false if no role grants access
2186
- * or if the access config is not defined.
2187
- *
2188
- * @param props.access - The VexAccessConfig from defineAccess
2189
- * @param props.user - The user object (passed to dynamic permission callbacks)
2190
- * @param props.userRoles - The user's role(s) as a string array
2191
- * @param props.organization - Optional organization object (passed to callbacks if org support is configured)
2192
- * @returns Whether the user can access the admin panel
2193
- */
2194
- declare function checkAdminAccess(props: {
2195
- access: VexAccessConfig | undefined;
2196
- user: Record<string, any>;
2197
- userRoles: string[];
2198
- organization?: Record<string, any>;
2199
- }): boolean;
2200
-
2201
- /**
2202
- * Framework-agnostic site metadata object.
2203
- * Consumers (Next.js, TanStack Start, etc.) map this to their framework's metadata format.
2204
- */
2205
- interface SiteMetadata {
2206
- title: string;
2207
- description: string;
2208
- ogImage?: string;
2209
- twitterHandle?: string;
2210
- }
2211
- /**
2212
- * Build site metadata by merging site-wide defaults with per-page overrides.
2213
- *
2214
- * Resolution order (per-page wins over site-wide):
2215
- * - title: page.metaTitle → page.title → site.metaTitle → site.name → "Untitled"
2216
- * - description: page.metaDescription → site.metaDescription → site.description → ""
2217
- * - ogImage: page.ogImage → site.ogImage → undefined
2218
- * - twitterHandle: site.twitterHandle → undefined
2219
- *
2220
- * @param props.site - Site settings fields (from globals)
2221
- * @param props.page - Optional per-page overrides
2222
- * @param props.titleSuffix - Optional suffix appended to title (e.g. " | My Site")
2223
- * @returns Framework-agnostic metadata object
2224
- */
2225
- declare function buildSiteMetadata(props: {
2226
- site: {
2227
- name?: string;
2228
- metaTitle?: string;
2229
- metaDescription?: string;
2230
- description?: string;
2231
- ogImage?: string;
2232
- twitterHandle?: string;
2233
- };
2234
- page?: {
2235
- title?: string;
2236
- metaTitle?: string;
2237
- metaDescription?: string;
2238
- ogImage?: string;
2239
- };
2240
- titleSuffix?: string;
2241
- }): SiteMetadata;
2242
-
2243
- /**
2244
- * Strip non-serializable values from VexConfig for safe passage across
2245
- * RSC / JSON serialization boundaries (e.g., server layout → client component).
2246
- *
2247
- * Currently strips:
2248
- * - `media.storageAdapter` (contains async functions — only needed at CLI / schema-gen time)
2249
- * - `admin.livePreview.url` function values on collections (replaced with `null`)
2250
- *
2251
- * This function is the single place to extend when new non-serializable
2252
- * properties are added to VexConfig in the future.
2253
- */
2254
- declare function sanitizeConfigForClient(config: VexConfig): ClientVexConfig;
2255
- /**
2256
- * Extracts a map of collection slug → original livePreview URL function for collections
2257
- * that have function-based preview URLs. Pass this to admin components so they
2258
- * can resolve preview URLs at runtime on the client.
2259
- *
2260
- * @returns Map of collection slug → { url } (only entries with function URLs)
2261
- */
2262
- declare function extractLivePreviewConfigs(config: VexConfig): Record<string, {
2263
- url: (doc: {
2264
- _id: string;
2265
- [key: string]: any;
2266
- }) => string;
2267
- }>;
2268
-
2269
- interface HasSlug {
2270
- readonly slug: string;
2271
- }
2272
- interface ConfigWithMedia {
2273
- media?: {
2274
- collections: HasSlug[];
2275
- };
2276
- }
2277
- /**
2278
- * Check whether a collection is a media collection.
2279
- *
2280
- * Compares the collection's slug against the slugs in `config.media.collections`.
2281
- * Works with both `VexConfig` and `ClientVexConfig` (both have the `media?.collections` shape).
2282
- *
2283
- * @param props.collection - The collection to check
2284
- * @param props.config - The Vex config (or client config) containing media configuration
2285
- * @returns true if the collection's slug matches a media collection slug
2286
- */
2287
- declare function isMediaCollection(props: {
2288
- collection: HasSlug;
2289
- config: ConfigWithMedia;
2290
- }): boolean;
2291
-
2292
- interface HasSlugAndFields {
2293
- readonly slug: string;
2294
- fields: Record<string, VexField>;
2295
- }
2296
- interface ConfigShape {
2297
- collections: HasSlugAndFields[];
2298
- globals: HasSlugAndFields[];
2299
- media?: {
2300
- collections: HasSlugAndFields[];
2301
- };
2302
- }
2303
- type CollectionKind = "collection" | "media" | "global";
2304
- interface ResolvedCollectionMatch {
2305
- slug: string;
2306
- fields: Record<string, VexField>;
2307
- kind: CollectionKind;
2308
- }
2309
- /**
2310
- * Get all collections, media collections, and globals as a flat array.
2311
- *
2312
- * Each entry includes the `kind` discriminator so callers can switch on it.
2313
- *
2314
- * @param props.config - The resolved Vex config
2315
- * @param props.excludeGlobals - Skip globals (default: false)
2316
- */
2317
- declare function getAllCollections(props: {
2318
- config: ConfigShape;
2319
- excludeGlobals?: boolean;
2320
- }): ResolvedCollectionMatch[];
2321
- /**
2322
- * Find a collection, media collection, or global by slug across the entire config.
2323
- *
2324
- * Searches in order: collections → media collections → globals.
2325
- * Returns the match with its fields and what kind it is, or null if not found.
2326
- *
2327
- * @param props.slug - The slug to search for
2328
- * @param props.config - The resolved Vex config
2329
- * @param props.excludeGlobals - Skip globals when searching (default: false)
2330
- */
2331
- declare function findCollectionBySlug(props: {
2332
- slug: string;
2333
- config: ConfigShape;
2334
- excludeGlobals?: boolean;
2335
- }): ResolvedCollectionMatch | null;
2336
-
2337
- /**
2338
- * Generates the full TypeScript source content for `convex/vex.schema.ts`.
2339
- *
2340
- * This is the main entry point for schema generation. It:
2341
- * 1. Validates all slugs are unique (via SlugRegistry)
2342
- * 2. For each auth collection, checks if a matching user collection exists (by slug):
2343
- * a. If yes: merges auth collection fields with user collection fields
2344
- * b. If no matching collection: generates the auth collection as-is
2345
- * 3. User collections that don't match any auth collection: generates from collection fields only
2346
- * 4. Collects indexes from per-field `index` properties and collection-level `indexes`
2347
- * 5. Generates defineTable() calls for each table with chained .index() calls
2348
- * 6. Generates defineTable() calls for system tables (vex_globals if globals exist)
2349
- *
2350
- * All fields go through fieldToValueType() uniformly — no dual path for auth vs user.
2351
- */
2352
- declare function generateVexSchema(props: {
2353
- config: VexConfig;
2354
- }): string;
2355
-
2356
- type ExtractFields<T> = T extends TableDefinition<VObject<any, infer F>> ? F : never;
2357
- type ForbidExistingKeys<Existing, New> = {
2358
- [K in keyof New]: K extends keyof Existing ? never : New[K];
2359
- };
2360
- /**
2361
- * Compute the TableDefinition type that preserves field types.
2362
- * Matches the second overload of defineTable:
2363
- * defineTable(fields) → TableDefinition<VObject<ObjectType<Fields>, Fields>>
2364
- */
2365
- type ExtendedTableDef<Fields extends Record<string, GenericValidator>> = TableDefinition<VObject<ObjectType<Fields>, Fields>>;
2366
- /**
2367
- * Extends a vex-generated table definition with additional fields,
2368
- * preserving all indexes from the original table.
2369
- *
2370
- * Use this in your `convex/schema.ts` when you need to add custom
2371
- * fields to a vex-managed table (e.g., adding a `body` field to posts).
2372
- *
2373
- * @param props.table - The table definition from vex.schema.ts
2374
- * @param props.additionalFields - Additional Convex validator fields to add.
2375
- * Keys that already exist on the table will cause a type error.
2376
- * @returns A new TableDefinition with merged fields and original indexes.
2377
- * You can chain additional `.index()` calls on the result.
2378
- *
2379
- * @example
2380
- * ```ts
2381
- * import { posts } from "./vex.schema";
2382
- * import { extendTable } from "@vexcms/core";
2383
- * import { v } from "convex/values";
2384
- *
2385
- * export default defineSchema({
2386
- * posts: extendTable({
2387
- * table: posts,
2388
- * additionalFields: { body: v.optional(v.string()) },
2389
- * }).index("by_status", ["status"]),
2390
- * });
2391
- * ```
2392
- */
2393
- declare function extendTable<T extends TableDefinition<VObject<any, any>>, A extends Record<string, GenericValidator> = {}>(props: {
2394
- table: T;
2395
- additionalFields?: A & ForbidExistingKeys<ExtractFields<T>, A>;
2396
- }): ExtendedTableDef<ExtractFields<T> & A>;
2397
-
2398
- /**
2399
- * Generates an array of ColumnDef objects from a VexCollection's field configs.
2400
- *
2401
- * @param props.collection - The collection to generate columns for
2402
- * @param props.auth - Optional auth adapter. When provided, auth fields (e.g. createdAt)
2403
- * get proper columnDef dispatch instead of falling back to plain text columns.
2404
- * @returns Array of ColumnDef objects for use with @tanstack/react-table
2405
- */
2406
- declare function generateColumns(props: {
2407
- collection: VexCollection;
2408
- auth?: VexAuthAdapter;
2409
- }): ColumnDef<Record<string, unknown>>[];
2410
-
2411
- /**
2412
- * Result of merging an auth collection with a user collection.
2413
- * Contains merged VexFields and metadata about field sources.
2414
- */
2415
- interface MergedCollectionResult {
2416
- /**
2417
- * The final merged field map.
2418
- * Key is field name, value is the VexField object.
2419
- * Auth fields win for schema generation; user admin config is preserved.
2420
- */
2421
- fields: Record<string, VexField>;
2422
- /** Indexes from both auth and user collections (deduplicated by name). */
2423
- indexes: ResolvedIndex[];
2424
- /** Search indexes from the user collection. */
2425
- searchIndexes: ResolvedSearchIndex[];
2426
- /**
2427
- * Fields that exist in both auth collection and user config.
2428
- * The auth VexField wins for schema gen; user admin config wins for UI.
2429
- */
2430
- overlapping: string[];
2431
- /** Fields that only exist in the auth collection (not in user's collection). */
2432
- authOnly: string[];
2433
- /** Fields that only exist in the user's collection (not from auth). */
2434
- userOnly: string[];
2435
- }
2436
- /**
2437
- * Merges an auth collection's fields with a user-defined collection's fields.
2438
- *
2439
- * Both sides are VexField records. For overlapping fields, the auth
2440
- * VexField's schema properties are used (it controls the DB shape), but
2441
- * the user's admin config (label, hidden, etc.) is preserved by copying
2442
- * admin-related metadata from the user's field onto the auth field.
2443
- *
2444
- * @param authCollection - The auth collection with fully resolved fields
2445
- * @param userCollection - The user's collection that matches this auth table by slug
2446
- * @returns Merged collection result with combined fields and source tracking
2447
- */
2448
- declare function mergeAuthCollectionWithUserCollection(props: {
2449
- authCollection: VexCollection;
2450
- userCollection: VexCollection;
2451
- }): MergedCollectionResult;
2452
-
2453
- declare function toTitleCase(input: string): string;
2454
-
2455
- /**
2456
- * Generate a Zod schema from a collection's field definitions.
2457
- * Used by both the client-side form (for validation on submit)
2458
- * and the server-side mutation (for payload validation).
2459
- *
2460
- * @param props.fields - Record of field name → VexField from the collection
2461
- * @returns A z.object() schema matching the collection's editable fields
2462
- */
2463
- declare function generateFormSchema(props: {
2464
- fields: Record<string, VexField>;
2465
- }): z.ZodObject<Record<string, ZodTypeAny>>;
2466
- /**
2467
- * Convert a single field to its Zod validator.
2468
- * Does NOT handle optional wrapping — that's done by the caller.
2469
- *
2470
- * @param props.field - The field definition (discriminated on `type`)
2471
- * @returns The base Zod type for this field (always required)
2472
- */
2473
- declare function fieldMetaToZod(props: {
2474
- field: VexField;
2475
- }): ZodTypeAny;
2476
-
2477
- /**
2478
- * Generate default values for a create form from a collection's field definitions.
2479
- * Skips hidden fields.
2480
- *
2481
- * @param props.fields - Record of field name -> VexField from the collection
2482
- * @returns Record of field name -> default value for the create form
2483
- */
2484
- declare function generateFormDefaultValues(props: {
2485
- fields: Record<string, VexField>;
2486
- }): Record<string, unknown>;
2487
-
2488
- declare function text(options?: Omit<TextFieldDef, "type">): TextFieldDef;
2489
-
2490
- declare function number(options?: Omit<NumberFieldDef, "type">): NumberFieldDef;
2491
-
2492
- declare function checkbox(options?: Omit<CheckboxFieldDef, "type">): CheckboxFieldDef;
2493
-
2494
- declare function select<T extends string = string>(options: Omit<SelectFieldMany<T>, "type">): SelectFieldDef<T>;
2495
- declare function select<T extends string = string>(options: Omit<SelectFieldSingle<T>, "type">): SelectFieldDef<T>;
2496
-
2497
- declare function date(options?: Omit<DateFieldDef, "type">): DateFieldDef;
2498
-
2499
- declare function imageUrl(options?: Omit<ImageUrlFieldDef, "type">): ImageUrlFieldDef;
2500
-
2501
- declare function relationship(options: Omit<RelationshipFieldMany, "type">): RelationshipFieldDef;
2502
- declare function relationship(options: Omit<RelationshipFieldSingle, "type">): RelationshipFieldDef;
2503
-
2504
- declare function upload(options: Omit<UploadFieldMany, "type">): UploadFieldDef;
2505
- declare function upload(options: Omit<UploadFieldSingle, "type">): UploadFieldDef;
2506
-
2507
- declare function json(options?: Omit<JsonFieldDef, "type">): JsonFieldDef;
2508
-
2509
- declare function object(options: Omit<ObjectFieldDef, "type">): ObjectFieldDef;
2510
-
2511
- declare function array(options: Omit<ArrayFieldDef, "type">): ArrayFieldDef;
2512
-
2513
- /**
2514
- * Creates a rich text field definition.
2515
- * Stores Plate/Slate JSON document as `v.any()` in Convex.
2516
- *
2517
- * @param options.label - Display label in admin form
2518
- * @param options.required - Whether this field is required
2519
- * @param options.editor - Per-field editor adapter override
2520
- * @returns A RichTextFieldDef
2521
- *
2522
- * @example
2523
- * ```ts
2524
- * content: richtext({ label: "Content", required: true })
2525
- * ```
2526
- */
2527
- declare function richtext(options?: Omit<RichTextFieldDef, "type">): RichTextFieldDef;
2528
-
2529
- /**
2530
- * Creates a UI field — a non-persisted field that renders a custom component.
2531
- * UI fields are skipped during schema generation, form validation, and column generation.
2532
- * They are useful for computed displays, action buttons, and embedded widgets.
2533
- *
2534
- * @param props.label - Display label for the field
2535
- * @param props.admin - Admin config. components.Field is required.
2536
- * @param props.description - Helper text displayed below the field
2537
- * @returns A UIFieldDef
2538
- *
2539
- * @example
2540
- * ```ts
2541
- * import { ui } from "@vexcms/core";
2542
- * import WordCount from "~/components/admin/WordCount";
2543
- *
2544
- * const collection = defineCollection({
2545
- * slug: "posts",
2546
- * fields: {
2547
- * wordCount: ui({
2548
- * label: "Word Count",
2549
- * admin: {
2550
- * components: { Field: WordCount },
2551
- * position: "sidebar",
2552
- * },
2553
- * }),
2554
- * },
2555
- * });
2556
- * ```
2557
- */
2558
- declare function ui(props: {
2559
- label?: string;
2560
- admin: FieldAdminConfig & {
2561
- components: {
2562
- Field: ComponentType<FieldComponentProps>;
2563
- };
2564
- };
2565
- description?: string;
2566
- }): UIFieldDef;
2567
-
2568
- /**
2569
- * Create a blocks field that stores an ordered array of block instances.
2570
- *
2571
- * @param props.blocks - Array of BlockDef objects allowed in this field
2572
- * @param props.labels - Optional singular/plural display labels
2573
- * @param props.min - Minimum number of blocks
2574
- * @param props.max - Maximum number of blocks
2575
- * @returns A BlocksFieldDef
2576
- *
2577
- * @throws VexBlockValidationError if two blocks share the same slug
2578
- *
2579
- * @example
2580
- * ```ts
2581
- * content: blocks({
2582
- * blocks: [heroBlock, ctaBlock, featureGridBlock],
2583
- * })
2584
- * ```
2585
- */
2586
- declare function blocks(props: {
2587
- blocks: BlockDef[];
2588
- labels?: BlocksFieldDef["labels"];
2589
- min?: number;
2590
- max?: number;
2591
- label?: string;
2592
- description?: string;
2593
- required?: boolean;
2594
- admin?: BlocksFieldDef["admin"];
2595
- }): BlocksFieldDef;
2596
-
2597
- /**
2598
- * Creates a color field definition.
2599
- *
2600
- * @param props - Color field configuration
2601
- * @param props.label - Display label in admin panel
2602
- * @param props.format - Output format: "hex" (default), "hsl", or "oklch"
2603
- * @param props.themeColors - When true, shows theme CSS variable picker tab
2604
- * @returns ColorFieldDef
2605
- */
2606
- declare function color(props?: Omit<ColorFieldDef, "type">): ColorFieldDef;
2607
-
2608
- /**
2609
- * Creates a tabs field definition.
2610
- * Groups fields into tabbed UI sections in the admin panel.
2611
- *
2612
- * Tabs with `slug` create nested objects in the document.
2613
- * Tabs without `slug` flatten their fields onto the parent.
2614
- *
2615
- * @param props - Tabs field configuration
2616
- * @param props.tabs - Array of tab definitions with label, optional slug, and fields
2617
- * @returns TabsFieldDef with preserved tab slug and field type information
2618
- */
2619
- declare function tabs<const TTabs extends TabDef[]>(props: Omit<TabsFieldDef<TTabs>, "type">): TabsFieldDef<TTabs>;
2620
-
2621
- /**
2622
- * Define a block type for use with the `blocks()` field.
2623
- *
2624
- * @param props.slug - Unique identifier for this block type
2625
- * @param props.label - Display label for the admin picker
2626
- * @param props.fields - Field definitions for this block's data shape
2627
- * @param props.admin - Optional admin UI configuration (icon, custom components)
2628
- * @returns A BlockDef object
2629
- *
2630
- * @throws VexBlockValidationError if slug is empty or contains invalid characters
2631
- * @throws VexBlockValidationError if any field name is reserved (blockType, _key)
2632
- *
2633
- * @example
2634
- * ```ts
2635
- * const heroBlock = defineBlock({
2636
- * slug: "hero",
2637
- * label: "Hero Section",
2638
- * fields: {
2639
- * heading: text({ required: true }),
2640
- * subheading: text(),
2641
- * },
2642
- * })
2643
- * ```
2644
- */
2645
- declare function defineBlock<TFields extends Record<string, VexField>>(props: {
2646
- slug: string;
2647
- label: string;
2648
- fields: TFields;
2649
- admin?: BlockDef["admin"];
2650
- interfaceName?: string;
2651
- }): BlockDef<TFields>;
2652
-
2653
- interface StylePreset {
2654
- /** Tailwind scale value (stored in blockStyles JSON). */
2655
- value: string;
2656
- /** Display label in the popover. */
2657
- label: string;
2658
- /** Size hint shown next to the label (e.g., "16px / 1rem"). */
2659
- hint: string;
2660
- }
2661
- declare const SPACING_PRESETS: StylePreset[];
2662
- declare const FONT_SIZE_PRESETS: StylePreset[];
2663
- declare const FONT_WEIGHT_PRESETS: StylePreset[];
2664
- declare const BORDER_RADIUS_PRESETS: StylePreset[];
2665
- declare const BOX_SHADOW_PRESETS: StylePreset[];
2666
- declare const OPACITY_PRESETS: StylePreset[];
2667
- declare const WIDTH_PRESETS: StylePreset[];
2668
- declare const MAX_WIDTH_PRESETS: StylePreset[];
2669
- declare const LINE_HEIGHT_PRESETS: StylePreset[];
2670
- declare const LETTER_SPACING_PRESETS: StylePreset[];
2671
- declare const BORDER_WIDTH_PRESETS: StylePreset[];
2672
- declare const ASPECT_RATIO_PRESETS: StylePreset[];
2673
-
2674
- /**
2675
- * Convert a blockStyles JSON string into a Tailwind class string.
2676
- *
2677
- * Handles responsive breakpoint prefixes. The "base" breakpoint has no prefix,
2678
- * all other breakpoints use their key as prefix (e.g., "sm:", "md:", "lg:").
2679
- *
2680
- * @param props.blockStylesJson - The raw JSON string from the block instance's blockStyles field
2681
- * @returns Tailwind class string ready to use in className, or empty string if input is empty/invalid
2682
- *
2683
- * @example
2684
- * ```ts
2685
- * blockStylesToTailwind({
2686
- * blockStylesJson: '{"base":{"margin":"4","padding":"2"},"sm":{"margin":"6"}}'
2687
- * })
2688
- * // → "m-4 p-2 sm:m-6"
2689
- * ```
2690
- */
2691
- declare function blockStylesToTailwind(props: {
2692
- blockStylesJson: string | undefined;
2693
- }): string;
2694
-
2695
- /**
2696
- * Generate the complete TypeScript source for `vex.types.ts`.
2697
- *
2698
- * @param props.config - The resolved VexConfig
2699
- * @returns TypeScript source code string
2700
- */
2701
- declare function generateVexTypes(props: {
2702
- config: VexConfig;
2703
- }): string;
2704
-
2705
- /**
2706
- * Convert a slug string to a PascalCase interface name.
2707
- *
2708
- * @param props.slug - The slug to convert (e.g., "blog-posts", "new_block", "media")
2709
- * @returns PascalCase string (e.g., "BlogPosts", "NewBlock", "Media")
2710
- */
2711
- declare function slugToInterfaceName(props: {
2712
- slug: string;
2713
- }): string;
2714
-
2715
- /**
2716
- * Extracts user-defined fields from a document, stripping all
2717
- * system fields (_id, _creationTime, _status, _version, _publishedAt).
2718
- *
2719
- * Used to create version snapshots that contain only content fields.
2720
- *
2721
- * @param props.document - The full document including system fields
2722
- * @returns A new object with only user-defined fields
2723
- */
2724
- declare function extractUserFields(props: {
2725
- document: Record<string, unknown>;
2726
- }): Record<string, unknown>;
2727
-
2728
- /**
2729
- * System field names injected into versioned collection schemas.
2730
- * These are excluded from user-editable fields and version snapshots.
2731
- */
2732
- declare const VERSION_SYSTEM_FIELDS: readonly ["vex_status", "vex_version", "vex_publishedAt"];
2733
- /**
2734
- * All system fields (Convex built-in + versioning) to strip when
2735
- * extracting user content from a document.
2736
- */
2737
- declare const ALL_SYSTEM_FIELDS: Set<string>;
2738
- /**
2739
- * Default max versions to keep per document.
2740
- */
2741
- declare const DEFAULT_MAX_VERSIONS_PER_DOC = 100;
2742
- /**
2743
- * Default autosave interval in milliseconds.
2744
- */
2745
- declare const DEFAULT_AUTOSAVE_INTERVAL = 2000;
2746
-
2747
- /**
2748
- * Resolves the preview URL from a collection's live preview config.
2749
- *
2750
- * @param props.config - The collection's livePreview config
2751
- * @param props.doc - The current document data (must include `_id`)
2752
- * @param props.fallbackURL - URL to return if the function throws
2753
- * @returns The resolved preview URL
2754
- * @throws If the resolved URL is empty and no fallbackURL is provided
2755
- */
2756
- declare function resolvePreviewURL(props: {
2757
- config: LivePreviewConfig;
2758
- doc: {
2759
- _id: string;
2760
- [key: string]: any;
2761
- };
2762
- fallbackURL?: string;
2763
- }): string;
2764
-
2765
- /**
2766
- * Determines if the preview iframe URL should be recomputed.
2767
- *
2768
- * @param props.config - The collection's livePreview config
2769
- * @param props.changedFields - Set of field names that changed in the save
2770
- * @returns true if the URL should be recomputed
2771
- */
2772
- declare function shouldReloadURL(props: {
2773
- config: LivePreviewConfig;
2774
- changedFields: string[];
2775
- }): boolean;
2776
-
2777
- declare const DEFAULT_BREAKPOINTS: LivePreviewBreakpoint[];
2778
- /**
2779
- * Debounce interval for writing preview snapshots on form changes.
2780
- */
2781
- declare const PREVIEW_SNAPSHOT_DEBOUNCE_MS = 500;
2782
-
2783
- /**
2784
- * Upserts a preview snapshot for a document.
2785
- * If a snapshot already exists for this collection+document, it is updated in place.
2786
- * If not, a new entry is created.
2787
- *
2788
- * @param props.ctx - Convex mutation context
2789
- * @param props.collection - Collection slug
2790
- * @param props.documentId - Document ID
2791
- * @param props.snapshot - Complete field snapshot from the form
2792
- */
2793
- declare function upsertPreviewSnapshot<DataModel extends GenericDataModel>(props: {
2794
- ctx: GenericMutationCtx<DataModel>;
2795
- collection: string;
2796
- documentId: string;
2797
- snapshot: Record<string, unknown>;
2798
- }): Promise<void>;
2799
- /**
2800
- * Deletes the preview snapshot for a document.
2801
- * Called after a successful save to clean up transient state.
2802
- *
2803
- * @param props.ctx - Convex mutation context
2804
- * @param props.collection - Collection slug
2805
- * @param props.documentId - Document ID
2806
- */
2807
- declare function deletePreviewSnapshot<DataModel extends GenericDataModel>(props: {
2808
- ctx: GenericMutationCtx<DataModel>;
2809
- collection: string;
2810
- documentId: string;
2811
- }): Promise<void>;
2812
- /**
2813
- * Gets the preview data for a document.
2814
- *
2815
- * Lookup order:
2816
- * 1. Preview snapshot (transient, written by admin form on each change)
2817
- * 2. Latest version from vex_versions (draft or published, excluding autosave/previewSnapshot)
2818
- * 3. null (fall back to main document)
2819
- *
2820
- * @param props.ctx - Convex query context
2821
- * @param props.collection - Collection slug
2822
- * @param props.documentId - Document ID
2823
- * @returns The snapshot data, or null if no preview/version exists
2824
- */
2825
- declare function getPreviewSnapshot<DataModel extends GenericDataModel>(props: {
2826
- ctx: GenericQueryCtx<DataModel>;
2827
- collection: string;
2828
- documentId: string;
2829
- }): Promise<Record<string, unknown> | null>;
2830
-
2831
- /**
2832
- * Drafts mode for vexQuery.
2833
- * - "snapshot": Fetch the transient preview snapshot (written by admin on form changes)
2834
- * - true: Fetch the latest draft version (from versioning system)
2835
- * - false: Fetch published content only
2836
- */
2837
- type VexDraftsMode = "snapshot" | boolean;
2838
- /**
2839
- * Context passed to vexQuery handlers.
2840
- * Extends the standard Convex QueryCtx with draft-awareness.
2841
- */
2842
- interface VexQueryCtx<DataModel extends GenericDataModel = GenericDataModel> extends GenericQueryCtx<DataModel> {
2843
- /**
2844
- * The resolved drafts mode.
2845
- * - "snapshot": caller wants preview snapshot data
2846
- * - true: caller wants latest draft version
2847
- * - false: caller wants published content only
2848
- *
2849
- * Defaults to "snapshot" when not explicitly passed by the caller.
2850
- */
2851
- drafts: VexDraftsMode;
2852
- }
2853
- /**
2854
- * Create a typed vexQuery builder from your project's query builder.
2855
- *
2856
- * Call this once in your project to get a `vexQuery` function that
2857
- * preserves full return type inference from your DataModel.
2858
- *
2859
- * @example
2860
- * ```ts
2861
- * // convex/vex/helpers.ts
2862
- * import { createVexQuery } from "@vexcms/core";
2863
- * import { query } from "../_generated/server";
2864
- *
2865
- * export const vexQuery = createVexQuery(query);
2866
- * ```
2867
- *
2868
- * Then use it in your query files:
2869
- * ```ts
2870
- * // convex/pages.ts
2871
- * import { vexQuery } from "./vex/helpers";
2872
- * import { getPreviewSnapshot } from "@vexcms/core";
2873
- *
2874
- * export const getBySlug = vexQuery({
2875
- * args: { slug: v.string() },
2876
- * handler: async (ctx, args) => {
2877
- * const page = await ctx.db
2878
- * .query("pages")
2879
- * .withIndex("by_slug", (q) => q.eq("slug", args.slug))
2880
- * .first();
2881
- * if (!page) return null;
2882
- * if (ctx.drafts === "snapshot") {
2883
- * const snapshot = await getPreviewSnapshot({ ctx, collection: "pages", documentId: page._id });
2884
- * if (snapshot) return { ...page, ...snapshot };
2885
- * }
2886
- * return page;
2887
- * },
2888
- * });
2889
- * ```
2890
- */
2891
- declare function createVexQuery<DataModel extends GenericDataModel>(_queryBuilder: QueryBuilder<DataModel, "public">): <Args extends PropertyValidators, Output>(props: {
2892
- args: Args;
2893
- handler: (ctx: VexQueryCtx<DataModel>, args: ObjectType<Args>) => Output | Promise<Output>;
2894
- }) => RegisteredQuery<"public", ObjectType<Args & {
2895
- _vexDrafts: typeof v.optional<any>;
2896
- }>, Awaited<Output>>;
2897
- /**
2898
- * Generic vexQuery for use without project-specific types.
2899
- * Prefer `createVexQuery(query)` for full type inference.
2900
- *
2901
- * @deprecated Use `createVexQuery(query)` instead for proper return type inference.
2902
- */
2903
- declare function vexQuery<Args extends PropertyValidators, Output>(props: {
2904
- args: Args;
2905
- handler: (ctx: VexQueryCtx, args: ObjectType<Args>) => Output | Promise<Output>;
2906
- }): RegisteredQuery<"public", ObjectType<Args & {
2907
- _vexDrafts: typeof v.optional<any>;
2908
- }>, Awaited<Output>>;
2909
-
2910
- declare function listDocuments<DataModel extends GenericDataModel>(props: {
2911
- args: {
2912
- collectionSlug: TableNamesInDataModel<DataModel>;
2913
- paginationOpts: PaginationOptions;
2914
- order?: "asc" | "desc";
2915
- };
2916
- ctx: GenericQueryCtx<DataModel>;
2917
- }): Promise<{
2918
- page: any[];
2919
- isDone: boolean;
2920
- continueCursor: convex_server.Cursor;
2921
- splitCursor?: convex_server.Cursor | null;
2922
- pageStatus?: "SplitRecommended" | "SplitRequired" | null;
2923
- }>;
2924
- declare function getDocument<DataModel extends GenericDataModel>(props: {
2925
- ctx: GenericQueryCtx<DataModel>;
2926
- args: {
2927
- collectionSlug: TableNamesInDataModel<DataModel>;
2928
- documentId: string;
2929
- /** When true, merges the transient preview snapshot (from admin live preview) */
2930
- preview?: boolean;
2931
- };
2932
- }): Promise<any>;
2933
- declare function updateDocument<DataModel extends GenericDataModel>(props: {
2934
- ctx: GenericMutationCtx<DataModel>;
2935
- args: {
2936
- collectionSlug: TableNamesInDataModel<DataModel>;
2937
- documentId: string;
2938
- fields: Record<string, unknown>;
2939
- collectionFields: Record<string, VexField>;
2940
- };
2941
- }): Promise<string>;
2942
- declare function createDocument<DataModel extends GenericDataModel>(props: {
2943
- ctx: GenericMutationCtx<DataModel>;
2944
- args: {
2945
- collectionSlug: TableNamesInDataModel<DataModel>;
2946
- fields: Record<string, unknown>;
2947
- collectionFields: Record<string, VexField>;
2948
- kind: CollectionKind;
2949
- };
2950
- }): Promise<string>;
2951
- declare function deleteDocument<DataModel extends GenericDataModel>(props: {
2952
- ctx: GenericMutationCtx<DataModel>;
2953
- args: {
2954
- collectionSlug: TableNamesInDataModel<DataModel>;
2955
- documentId: string;
2956
- kind: CollectionKind;
2957
- };
2958
- }): Promise<void>;
2959
- declare function searchDocuments<DataModel extends GenericDataModel>(props: {
2960
- args: {
2961
- collectionSlug: TableNamesInDataModel<DataModel>;
2962
- searchIndexName: string;
2963
- searchField: string;
2964
- query: string;
2965
- };
2966
- ctx: GenericQueryCtx<DataModel>;
2967
- }): Promise<any[]>;
2968
-
2969
- /** Sentinel string placed at the top of every generated file. */
2970
- declare const GENERATED_HEADER = "// \u26A0\uFE0F AUTO-GENERATED BY VEX CMS \u2014 DO NOT EDIT \u26A0\uFE0F";
2971
- /**
2972
- * Relative import paths used inside generated files.
2973
- * Computed by the CLI from cwd + convexDir; passed in so this function
2974
- * stays pure and testable.
2975
- */
2976
- interface CollectionQueryImports {
2977
- /** Import path for vex.config.ts from api/ dir. e.g. `"../../../vex.config"` */
2978
- vexConfigFromApi: string;
2979
- /** Import path for _generated/ from api/ dir. e.g. `"../../_generated"` */
2980
- generatedDirFromApi: string;
2981
- /** Import path for the user's auth helper from api/ dir. e.g. `"../auth"` */
2982
- authFromApi: string;
2983
- /** Import path for _generated/ from model/api/ dir. e.g. `"../../../_generated"` */
2984
- generatedDirFromModel: string;
2985
- }
2986
- /**
2987
- * Result of generating all collection files.
2988
- * Keys are relative paths from the vex/ directory.
2989
- * e.g. `"api/articles.ts"`, `"model/api/articles.ts"`, `"api/index.ts"`
2990
- */
2991
- type GeneratedFiles = Record<string, string>;
2992
- /**
2993
- * Generate typed Convex query/mutation files for all collections in `config`.
2994
- *
2995
- * Produces two files per collection:
2996
- * - `model/api/{slug}.ts` — typed model functions (DB logic)
2997
- * - `api/{slug}.ts` — Convex query/mutation exports (auth + RBAC + calls model)
2998
- * Plus a barrel `api/index.ts`.
2999
- */
3000
- declare function generateCollectionQueries(props: {
3001
- config: VexConfig;
3002
- imports: CollectionQueryImports;
3003
- }): GeneratedFiles;
3004
- /**
3005
- * Generate the barrel `index.ts` that namespace-re-exports all collection files.
3006
- */
3007
- declare function generateIndexFile(props: {
3008
- slugs: string[];
3009
- }): string;
3010
-
3011
- /**
3012
- * Base error class for all Vex CMS errors.
3013
- * Provides consistent error formatting with a [vex] prefix.
3014
- */
3015
- declare class VexError extends Error {
3016
- constructor(message: string);
3017
- }
3018
- /**
3019
- * Thrown when a duplicate table slug is detected during schema generation.
3020
- * Includes both registrations so the user can identify the conflict.
3021
- */
3022
- declare class VexSlugConflictError extends VexError {
3023
- readonly slug: string;
3024
- readonly existingSource: string;
3025
- readonly existingLocation: string;
3026
- readonly newSource: string;
3027
- readonly newLocation: string;
3028
- constructor(slug: string, existingSource: string, existingLocation: string, newSource: string, newLocation: string);
3029
- }
3030
- /**
3031
- * Thrown when a field fails validation during schema generation.
3032
- * For example: required field with no defaultValue, or wrong defaultValue type.
3033
- */
3034
- declare class VexFieldValidationError extends VexError {
3035
- readonly collectionSlug: string;
3036
- readonly fieldName: string;
3037
- readonly detail: string;
3038
- constructor(collectionSlug: string, fieldName: string, detail: string);
3039
- }
3040
- /**
3041
- * Thrown when auth configuration is invalid.
3042
- * For example: userCollection not found in collections.
3043
- */
3044
- declare class VexAuthConfigError extends VexError {
3045
- constructor(detail: string);
3046
- }
3047
- /**
3048
- * Thrown when media configuration is invalid.
3049
- */
3050
- declare class VexMediaConfigError extends VexError {
3051
- constructor(detail: string);
3052
- }
3053
- /**
3054
- * Thrown when access configuration is invalid.
3055
- * For example: orgCollection provided without userOrgField.
3056
- */
3057
- declare class VexAccessConfigError extends VexError {
3058
- constructor(detail: string);
3059
- }
3060
- /**
3061
- * Thrown by `hasPermission` when `throwOnDenied` is true and the user
3062
- * does not have permission for the requested action.
3063
- *
3064
- * Contains structured context about the denied access attempt so callers
3065
- * can log, surface to users, or handle programmatically.
3066
- */
3067
- declare class VexAccessError extends VexError {
3068
- readonly resource: string;
3069
- readonly action: string;
3070
- readonly field?: string | undefined;
3071
- constructor(resource: string, action: string, field?: string | undefined);
3072
- }
3073
- /**
3074
- * Thrown when a block definition is invalid.
3075
- * For example: reserved field name used, duplicate block slug.
3076
- */
3077
- declare class VexBlockValidationError extends VexError {
3078
- readonly blockSlug: string;
3079
- readonly detail: string;
3080
- constructor(blockSlug: string, detail: string);
3081
- }
3082
-
3083
- interface SchemaFieldInfo {
3084
- /** Table name (export const name). */
3085
- table: string;
3086
- /** Field name within the table. */
3087
- field: string;
3088
- /** Full value type string, e.g. "v.string()" or "v.optional(v.string())". */
3089
- valueType: string;
3090
- /** Whether the field is wrapped in v.optional(...). */
3091
- isOptional: boolean;
3092
- }
3093
- interface RemovedFieldInfo {
3094
- /** Table name (export const name). */
3095
- table: string;
3096
- /** Field name that was removed. */
3097
- field: string;
3098
- /** The old value type string, e.g. "v.string()". */
3099
- valueType: string;
3100
- /** Whether the field was optional in the old schema. */
3101
- wasOptional: boolean;
3102
- }
3103
- interface SchemaDiff {
3104
- /** Fields that changed from optional → required (need backfill). */
3105
- newRequired: SchemaFieldInfo[];
3106
- /** Fields that are entirely new and required (need backfill). */
3107
- addedRequired: SchemaFieldInfo[];
3108
- /** Fields that are entirely new and optional (may need backfill if they have a defaultValue). */
3109
- addedOptional: SchemaFieldInfo[];
3110
- /** Fields that existed in old schema but are absent in new schema. */
3111
- removedFields: RemovedFieldInfo[];
3112
- /** All fields that need migration. */
3113
- needsMigration: SchemaFieldInfo[];
3114
- }
3115
- /**
3116
- * Compare two generated schema strings and return fields that need migration.
3117
- *
3118
- * A field needs migration when:
3119
- * 1. It exists in the new schema but not the old, and is NOT optional → `addedRequired`
3120
- * 2. It exists in the new schema but not the old, and IS optional → `addedOptional`
3121
- * (planMigration decides whether to backfill based on defaultValue)
3122
- * 3. It exists in both, was optional in old but is NOT optional in new → `newRequired`
3123
- */
3124
- declare function diffSchema(oldSchema: string, newSchema: string): SchemaDiff;
3125
- /**
3126
- * Rewrite specific fields in a schema string to be `v.optional(...)`.
3127
- *
3128
- * Used to produce an interim schema where new required fields are temporarily
3129
- * optional, allowing Convex to accept the schema before documents are backfilled.
3130
- */
3131
- declare function makeFieldsOptional(schema: string, fields: SchemaFieldInfo[]): string;
3132
- /**
3133
- * Re-insert removed fields into a schema string as `v.optional(...)`.
3134
- *
3135
- * Used to produce an interim schema that still accepts documents with the
3136
- * removed field, so we can strip the field from documents before deploying
3137
- * the final schema without the field.
3138
- */
3139
- declare function addRemovedFieldsAsOptional(schema: string, fields: RemovedFieldInfo[]): string;
3140
-
3141
- interface MigrationOp {
3142
- /** The table name (as exported in the schema). */
3143
- table: string;
3144
- /** The field name to backfill. */
3145
- field: string;
3146
- /** The default value to set on existing documents. */
3147
- defaultValue: unknown;
3148
- }
3149
- /**
3150
- * Given a schema diff and the full Vex config, produce a list of
3151
- * migration operations — one per field that needs backfilling.
3152
- *
3153
- * Fields are matched by looking up the collection whose table name
3154
- * (or slug) matches the diff's table, then finding the field's
3155
- * `defaultValue`.
3156
- *
3157
- * Auth-only fields (fields that come from the auth adapter, not from
3158
- * user-defined collections) are skipped — auth manages its own data.
3159
- */
3160
- declare function planMigration(props: {
3161
- diff: SchemaDiff;
3162
- config: VexConfig;
3163
- }): MigrationOp[];
3164
-
3165
- export { ALL_SYSTEM_FIELDS, ASPECT_RATIO_PRESETS, type AccessAction, type AdminConfig, type AdminConfigInput, type AdminLivePreviewConfig, type AdminMetaInput, type AdminSidebarInput, type AnyVexCollection, type ArrayFieldDef, type AuthCollectionFieldKeys, type AuthTableFieldKeys, BORDER_RADIUS_PRESETS, BORDER_WIDTH_PRESETS, BOX_SHADOW_PRESETS, type BlockAdminConfig, type BlockDef, type BlockStyleValues, type BlockStylesData, type BlocksFieldDef, type BreakpointConfig, type CellComponentProps, type CheckboxFieldDef, type ClientMediaConfig, type ClientVexConfig, type CollectionAdminConfig, type CollectionKind, type CollectionQueryImports, type ColorFieldDef, type ContainerStyleConfig, DEFAULT_AUTOSAVE_INTERVAL, DEFAULT_BREAKPOINTS, DEFAULT_MAX_VERSIONS_PER_DOC, type DateFieldDef, type DefaultMediaFieldKeys, type DistributiveOmit, type ExtractDocType, type ExtractFieldKeys, type ExtractSlug, type ExtractSlugs, FONT_SIZE_PRESETS, FONT_WEIGHT_PRESETS, type FieldAdminConfig, type FieldComponentProps, type FieldPermissionResult, type FileStorageAdapter, GENERATED_HEADER, type GeneratedFiles, type GlobalAdminConfig, type ImageUrlFieldDef, type IndexConfig, type InferBlockUnion, type InferFieldType, type InferFieldsType, type JsonFieldDef, LETTER_SPACING_PRESETS, LINE_HEIGHT_PRESETS, LOCKED_MEDIA_FIELDS, type LayoutStyleConfig, type LivePreviewBreakpoint, type LivePreviewConfig, type LockedMediaField, type LookupBySlug, MAX_WIDTH_PRESETS, type MediaConfig, type MediaConfigInput, type MediaStyleConfig, type MergedCollectionResult, type MigrationOp, type NumberFieldDef, OPACITY_PRESETS, OVERRIDABLE_MEDIA_FIELDS, type ObjectFieldDef, type OverridableMediaField, PREVIEW_SNAPSHOT_DEBOUNCE_MS, type PermissionCallbackProps, type PermissionCheck, type RelationshipFieldDef, type RemovedFieldInfo, type ResolvedCollectionMatch, type ResolvedFieldPermissions, type ResolvedIndex, type ResolvedSearchIndex, type ResourcePermissions, type RichTextDocument, type RichTextElement, type RichTextFieldDef, type RichTextText, type RolesWithPermissions, SPACING_PRESETS, type SchemaDiff, type SchemaFieldInfo, type SearchIndexConfig, type SelectFieldDef, type SelectOption, type SiteMetadata, type StylePreset, type StyleTier, type TabDef, type TabsFieldDef, type TextFieldDef, type TextStyleConfig, type UIFieldDef, type UploadFieldDef, VERSION_SYSTEM_FIELDS, type VersioningFieldKeys, type VersionsConfig, type VexAccessConfig, VexAccessConfigError, VexAccessError, type VexAccessInput, type VexAccessInputBase, type VexAccessInputWithOrg, type VexAuthAdapter, VexAuthConfigError, VexBlockValidationError, type VexCollection, type VexConfig, type VexConfigInput, type VexDraftsMode, type VexEditorAdapter, type VexEditorComponentProps, VexError, type VexField, VexFieldValidationError, type VexGlobal, type VexMediaCollection, VexMediaConfigError, type VexPlugin, type VexQueryCtx, type VexRenderComponentProps, VexSlugConflictError, WIDTH_PRESETS, addRemovedFieldsAsOptional, array, blockStylesToTailwind, blocks, buildSiteMetadata, checkAdminAccess, checkbox, color, createDocument, createVexQuery, date, defineAccess, defineBlock, defineCollection, defineConfig, defineGlobal, defineMediaCollection, deleteDocument, deletePreviewSnapshot, diffSchema, extendTable, extractLivePreviewConfigs, extractUserFields, fieldMetaToZod, findCollectionBySlug, generateCollectionQueries, generateColumns, generateFormDefaultValues, generateFormSchema, generateIndexFile, generateVexSchema, generateVexTypes, getAllCollections, getDocument, getPreviewSnapshot, hasPermission, imageUrl, isMediaCollection, json, listDocuments, makeFieldsOptional, mergeAuthCollectionWithUserCollection, number, object, planMigration, relationship, resolvePreviewURL, richtext, sanitizeConfigForClient, searchDocuments, select, shouldReloadURL, slugToInterfaceName, tabs, text, toTitleCase, ui, updateDocument, upload, upsertPreviewSnapshot, vexQuery };
1
+ export * from "./collections";
2
+ export * from "./globals";
3
+ export * from "./fields";
4
+ export * from "./utils";
5
+ export * from "./framework";
6
+ export * from "./api/convex";
7
+ export * from "./api/types";
8
+ export * from "./config";
9
+ export * from "./access";
10
+ export * from "./schema";
11
+ export * from "./types";
12
+ export { type VexAuthAdapter, type AuthCollectionConfig, type AuthFieldMeta, VexAuthConfigError, } from "./auth/types";
13
+ export { mergeAuthCollections } from "./auth/mergeCollections";
14
+ export { type VexStorageAdapter, type StorageAdapterPresignedUrlInterface, type StorageAdapterBaseInterface, type StorageAdapterProtocol, type MediaCollectionConfig, type MediaCollectionMeta, type MediaCollectionConfigInput, type GetUrlReturn, type GenerateUploadUrlReturn, type UploadFileReturn, STORAGE_ADAPTER_PROTOCOLS, StorageAdapterPresignedUrl, VexStorageConfigError, validateAndMergeStorageConfig, mediaApi, formatBytes, formatMimeType, createMediaDocumentArgs, createMediaDocumentReturn, deleteMediaArgs, deleteMediaReturn, getUrlArgs, getUrlReturn, } from "./media";
15
+ //# sourceMappingURL=index.d.ts.map