@vexcms/core 0.0.20 → 0.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -0,0 +1,1877 @@
1
+ // src/api/server.ts
2
+ import {
3
+ paginationOptsValidator
4
+ } from "convex/server";
5
+ import { v } from "convex/values";
6
+
7
+ // src/api/depth.ts
8
+ function buildDepthPopulate(config, slug, depth, visited = /* @__PURE__ */ new Set()) {
9
+ if (depth <= 0) return {};
10
+ const source = config.collections.find((c) => c.slug === slug) ?? config.globals?.find((g) => g.slug === slug);
11
+ if (!source) return {};
12
+ const nextVisited = new Set(visited).add(slug);
13
+ const result = {};
14
+ for (const [key, field] of Object.entries(source.fields)) {
15
+ if (field.type !== "relationship") continue;
16
+ const targetSlug = field.collection.slug;
17
+ if (depth === 1 || nextVisited.has(targetSlug)) {
18
+ result[key] = true;
19
+ continue;
20
+ }
21
+ const nested = buildDepthPopulate(config, targetSlug, depth - 1, nextVisited);
22
+ result[key] = Object.keys(nested).length > 0 ? { populate: nested } : true;
23
+ }
24
+ return result;
25
+ }
26
+
27
+ // ../../node_modules/.pnpm/convex-helpers@0.1.120_@standard-schema+spec@1.1.0_convex@1.44.0_react@19.2.7__hono@4.1_fda37119e0b134c2babc8f4bd1faf097/node_modules/convex-helpers/index.js
28
+ async function asyncMap(list, asyncTransform) {
29
+ const promises = [];
30
+ let index = 0;
31
+ list = await list;
32
+ for (const item of list) {
33
+ promises.push(asyncTransform(item, index));
34
+ index += 1;
35
+ }
36
+ return Promise.all(promises);
37
+ }
38
+
39
+ // ../../node_modules/.pnpm/convex-helpers@0.1.120_@standard-schema+spec@1.1.0_convex@1.44.0_react@19.2.7__hono@4.1_fda37119e0b134c2babc8f4bd1faf097/node_modules/convex-helpers/server/relationships.js
40
+ async function getAll(db, arg1, arg2) {
41
+ const [table, ids] = arg2 !== void 0 ? [arg1, arg2] : [null, arg1];
42
+ return table ? asyncMap(ids, (id) => db.get(table, id)) : asyncMap(ids, (id) => db.get(id));
43
+ }
44
+
45
+ // src/api/populate.ts
46
+ async function populateDocs(ctx, docs, populate) {
47
+ const result = await asyncMap(docs, async (doc) => {
48
+ const out = { ...doc };
49
+ for (const [fieldKey, opts] of Object.entries(populate)) {
50
+ const ids = doc[fieldKey];
51
+ if (!Array.isArray(ids)) continue;
52
+ const targets = await getAll(ctx.db, ids);
53
+ const filtered = targets.filter((t) => t !== null);
54
+ if (typeof opts === "object" && opts !== null && "populate" in opts && opts.populate && filtered.length > 0) {
55
+ out[fieldKey] = await populateDocs(ctx, filtered, opts.populate);
56
+ } else {
57
+ out[fieldKey] = filtered;
58
+ }
59
+ }
60
+ return out;
61
+ });
62
+ return result;
63
+ }
64
+
65
+ // src/access/constants.ts
66
+ var CRUD_ACTIONS = {
67
+ create: "create",
68
+ read: "read",
69
+ update: "update",
70
+ delete: "delete"
71
+ };
72
+ var DRAFT_ACTIONS = {
73
+ readDrafts: "readDrafts",
74
+ saveDraft: "saveDraft",
75
+ publish: "publish",
76
+ unpublish: "unpublish"
77
+ };
78
+ var QUERY_ACTIONS = {
79
+ read: CRUD_ACTIONS.read,
80
+ readDrafts: DRAFT_ACTIONS.readDrafts
81
+ };
82
+ var PERMISSION_MODES = {
83
+ allow: "allow",
84
+ deny: "deny"
85
+ };
86
+ var PERMISSION_SCOPES = {
87
+ doc: "doc",
88
+ any: "any",
89
+ all: "all"
90
+ };
91
+ var WILDCARD_KEY = "*";
92
+ var ADMIN_CUSTOM_SUBJECTS = {
93
+ adminPanel: {
94
+ key: "adminPanel",
95
+ actions: ["access", "impersonate"]
96
+ }
97
+ };
98
+
99
+ // src/access/types.ts
100
+ import { ConvexError } from "convex/values";
101
+ function dataType() {
102
+ return {};
103
+ }
104
+ var VexAccessError = class extends ConvexError {
105
+ /** The subject on which access was denied. */
106
+ resource;
107
+ /** The denied action. */
108
+ action;
109
+ /**
110
+ * @param options — Structured denial context.
111
+ * @param options.message — Human-readable error message.
112
+ * @param options.resource — Subject name.
113
+ * @param options.action — Action name.
114
+ */
115
+ constructor(options) {
116
+ super({
117
+ code: "ACCESS_DENIED",
118
+ resource: options.resource,
119
+ action: options.action,
120
+ message: options.message ?? `Access Denied: ${options.resource}/${options.action}`
121
+ });
122
+ this.name = "VexAccessError";
123
+ this.resource = options.resource;
124
+ this.action = options.action;
125
+ }
126
+ };
127
+ var VexAccessConfigError = class extends Error {
128
+ /** @param message — Human-readable description of the configuration error. */
129
+ constructor(message) {
130
+ super(message);
131
+ this.name = "VexAccessConfigError";
132
+ }
133
+ };
134
+
135
+ // src/access/createFilterConstraintBuilder.ts
136
+ function createFilterConstraintBuilder() {
137
+ const asFilterConstraint = (result) => result;
138
+ const compare = (op) => (field, value) => ({
139
+ kind: "compare",
140
+ field,
141
+ op,
142
+ value
143
+ });
144
+ const builder = {
145
+ eq: compare("eq"),
146
+ neq: compare("neq"),
147
+ gt: compare("gt"),
148
+ gte: compare("gte"),
149
+ lt: compare("lt"),
150
+ lte: compare("lte"),
151
+ and: (...nodes) => ({ kind: "and", nodes: nodes.map(asFilterConstraint) }),
152
+ or: (...nodes) => ({ kind: "or", nodes: nodes.map(asFilterConstraint) }),
153
+ not: (node) => ({ kind: "not", node: asFilterConstraint(node) })
154
+ };
155
+ return builder;
156
+ }
157
+
158
+ // src/access/createIndexConstraintBuilder.ts
159
+ var CONSTRAINTS = /* @__PURE__ */ Symbol("vex.access.indexConstraints");
160
+ function createIndexConstraintBuilder() {
161
+ function node(constraints) {
162
+ const extend = (op, field, value) => node([
163
+ ...constraints,
164
+ { field, op, value }
165
+ ]);
166
+ return {
167
+ eq: (field, value) => extend("eq", field, value),
168
+ gt: (field, value) => extend("gt", field, value),
169
+ gte: (field, value) => extend("gte", field, value),
170
+ lt: (field, value) => extend("lt", field, value),
171
+ lte: (field, value) => extend("lte", field, value),
172
+ [CONSTRAINTS]: constraints
173
+ };
174
+ }
175
+ return node([]);
176
+ }
177
+ function readIndexConstraints(result) {
178
+ const carried = result[CONSTRAINTS];
179
+ return carried ?? [];
180
+ }
181
+
182
+ // src/access/createAccessQueryBuilder.ts
183
+ var CONDITION = /* @__PURE__ */ Symbol("vex.access.condition");
184
+ function createAccessQueryBuilder() {
185
+ const conditionNode = (condition) => ({
186
+ [CONDITION]: condition
187
+ });
188
+ const runFilter = (predicate) => predicate(createFilterConstraintBuilder());
189
+ const builder = {
190
+ withIndex: (name, range) => {
191
+ const built = range(createIndexConstraintBuilder());
192
+ const constraints = readIndexConstraints(built);
193
+ const base = { index: { name, constraints } };
194
+ return {
195
+ ...conditionNode(base),
196
+ // Continuing with `.filter(…)` produces a NEW condition carrying both
197
+ // halves; the index-only value the rule already holds is unchanged.
198
+ filter: (predicate) => conditionNode({ ...base, filter: runFilter(predicate) })
199
+ };
200
+ },
201
+ filter: (predicate) => conditionNode({ filter: runFilter(predicate) })
202
+ };
203
+ return builder;
204
+ }
205
+ function readAccessCondition(result) {
206
+ if (result === null || typeof result !== "object" && typeof result !== "function") {
207
+ return void 0;
208
+ }
209
+ return result[CONDITION];
210
+ }
211
+
212
+ // src/access/compileConstraints.ts
213
+ function convexValuesEqual(a, b) {
214
+ if (a === b) return true;
215
+ if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
216
+ if (a instanceof ArrayBuffer || b instanceof ArrayBuffer) {
217
+ if (!(a instanceof ArrayBuffer) || !(b instanceof ArrayBuffer)) return false;
218
+ if (a.byteLength !== b.byteLength) return false;
219
+ const left2 = new Uint8Array(a);
220
+ const right2 = new Uint8Array(b);
221
+ return left2.every((byte, i) => byte === right2[i]);
222
+ }
223
+ if (Array.isArray(a) || Array.isArray(b)) {
224
+ if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;
225
+ return a.every((item, i) => convexValuesEqual(item, b[i]));
226
+ }
227
+ const left = a;
228
+ const right = b;
229
+ const leftKeys = Object.keys(left);
230
+ if (leftKeys.length !== Object.keys(right).length) return false;
231
+ return leftKeys.every(
232
+ (key) => key in right && convexValuesEqual(left[key], right[key])
233
+ );
234
+ }
235
+ var CONSTRAINT_COMPARATORS = {
236
+ eq: convexValuesEqual,
237
+ gt: (a, b) => a > b,
238
+ gte: (a, b) => a >= b,
239
+ lt: (a, b) => a < b,
240
+ lte: (a, b) => a <= b
241
+ };
242
+ function accessConstraintsToIndexRange(props) {
243
+ return (q) => props.constraints.reduce(
244
+ (builder, c) => (
245
+ // Convex's `IndexRangeBuilder` narrows its RETURN TYPE per call, exactly as
246
+ // `ConstraintBuilder` does — so a dynamic reduce over an untyped op string
247
+ // needs one cast at this boundary. The static safety already happened when
248
+ // the rule's `constraints` callback was type-checked, and again at
249
+ // `defineAccess` time in `validateAccessConstraints`.
250
+ builder[c.op](
251
+ c.field,
252
+ c.value
253
+ )
254
+ ),
255
+ q
256
+ // Every `IndexRangeBuilder` structurally extends `IndexRange`, including one
257
+ // with zero calls applied — so an empty constraint list returns `q` itself,
258
+ // matching Convex's own "no restriction" semantics.
259
+ );
260
+ }
261
+ function accessConstraintsToFilter(props) {
262
+ const q = props.q;
263
+ const [first, ...rest] = props.constraints.map((c) => q[c.op](q.field(c.field), c.value));
264
+ if (first === void 0) {
265
+ throw new Error(
266
+ "accessConstraintsToFilter: `constraints` must be non-empty \u2014 an unrestricted rule is resolved to a boolean before it reaches a compile target."
267
+ );
268
+ }
269
+ return rest.length === 0 ? first : q.and(first, ...rest);
270
+ }
271
+ function accessConstraintsToPredicate(props) {
272
+ return (doc) => props.constraints.every((c) => CONSTRAINT_COMPARATORS[c.op](doc[c.field], c.value));
273
+ }
274
+ var FILTER_COMPARATORS = {
275
+ ...CONSTRAINT_COMPARATORS,
276
+ neq: (a, b) => !convexValuesEqual(a, b)
277
+ };
278
+ function accessFilterTreeToFilter(props) {
279
+ const q = props.q;
280
+ function compile(node) {
281
+ switch (node.kind) {
282
+ case "compare":
283
+ return q[node.op](q.field(node.field), node.value);
284
+ case "and":
285
+ return q.and(...node.nodes.map(compile));
286
+ case "or":
287
+ return q.or(...node.nodes.map(compile));
288
+ case "not":
289
+ return q.not(compile(node.node));
290
+ }
291
+ }
292
+ return compile(props.node);
293
+ }
294
+ function accessFilterTreeToPredicate(props) {
295
+ function evaluate(node, doc) {
296
+ switch (node.kind) {
297
+ case "compare":
298
+ return FILTER_COMPARATORS[node.op](doc[node.field], node.value);
299
+ case "and":
300
+ return node.nodes.every((child) => evaluate(child, doc));
301
+ case "or":
302
+ return node.nodes.some((child) => evaluate(child, doc));
303
+ case "not":
304
+ return !evaluate(node.node, doc);
305
+ }
306
+ }
307
+ return (doc) => evaluate(props.node, doc);
308
+ }
309
+
310
+ // src/access/hasPermission.ts
311
+ function hasPermission(props) {
312
+ const { access } = props;
313
+ if (!access || access === void 0 || !access.enabled) {
314
+ return true;
315
+ }
316
+ const rawRoles = props.user ? props.user[access.userRolesField] : [];
317
+ const userRoles = typeof rawRoles === "string" ? [rawRoles] : Array.isArray(rawRoles) ? rawRoles.filter((role) => typeof role === "string") : [];
318
+ const effectiveRoles = userRoles.length === 0 && access.anonRole !== void 0 ? [access.anonRole] : userRoles;
319
+ const knownRoles = effectiveRoles.filter((role) => access.roles.includes(role));
320
+ const defaultAllowed = access.defaultPermissionMode === PERMISSION_MODES.allow;
321
+ let allPermissions;
322
+ if (knownRoles.length === 0) {
323
+ allPermissions = false;
324
+ } else {
325
+ const resolved = knownRoles.map((userRole) => {
326
+ const role = access.permissions[userRole];
327
+ const resource = role?.[props.resource];
328
+ let check;
329
+ if (typeof resource === "boolean") {
330
+ check = resource;
331
+ } else if (resource !== null && resource !== void 0 && typeof resource === "object") {
332
+ check = resolveActionCheck({
333
+ resource,
334
+ action: props.action
335
+ }) ?? defaultAllowed;
336
+ } else {
337
+ const roleWildcard = role?.[WILDCARD_KEY];
338
+ check = typeof roleWildcard === "boolean" ? roleWildcard : defaultAllowed;
339
+ }
340
+ return resolvePermissionCheck({
341
+ check,
342
+ user: props.user,
343
+ data: props.data,
344
+ organization: access.orgCollectionSlug !== void 0 ? props.organization : void 0,
345
+ resource: props.resource,
346
+ action: props.action,
347
+ scope: props.scope ?? PERMISSION_SCOPES.all
348
+ }) ?? defaultAllowed;
349
+ });
350
+ allPermissions = resolved.some(Boolean);
351
+ }
352
+ if (allPermissions === false) {
353
+ if (props.throwOnDenied) {
354
+ throw new VexAccessError({
355
+ resource: props.resource,
356
+ action: props.action
357
+ });
358
+ }
359
+ return false;
360
+ }
361
+ return true;
362
+ }
363
+ var CAPABILITY_PROBE = /* @__PURE__ */ Symbol("vex.capabilityProbe");
364
+ function isConstrainedCheck(check) {
365
+ return typeof check === "object" && check !== null && "constraints" in check;
366
+ }
367
+ function resolveConstrainedCheck(props) {
368
+ const outcome = props.check.constraints({
369
+ user: props.user,
370
+ q: createAccessQueryBuilder(),
371
+ ...props.organization !== void 0 ? { organization: props.organization } : {}
372
+ });
373
+ if (typeof outcome === "boolean") return outcome;
374
+ if (props.data === void 0) {
375
+ if (props.scope === PERMISSION_SCOPES.any) return true;
376
+ if (props.scope === PERMISSION_SCOPES.all) return false;
377
+ throw new VexAccessError({
378
+ resource: props.resource,
379
+ action: props.action,
380
+ message: `hasPermission: "${props.resource}.${props.action}" needs a "data" object (its check is a constraint). Pass "data" for an exact check, or use scope: "any" (nav/list gating) or scope: "all" (bulk actions).`
381
+ });
382
+ }
383
+ const condition = readAccessCondition(outcome);
384
+ if (condition === void 0) return false;
385
+ const doc = props.data;
386
+ const indexHolds = condition.index === void 0 || accessConstraintsToPredicate({ constraints: condition.index.constraints })(doc);
387
+ const filterHolds = condition.filter === void 0 || accessFilterTreeToPredicate({ node: condition.filter })(doc);
388
+ if (!indexHolds || !filterHolds) return false;
389
+ if (props.check.filter === void 0) return true;
390
+ return resolvePermissionCheck({
391
+ ...props,
392
+ check: props.check.filter
393
+ });
394
+ }
395
+ function resolvePermissionCheck(props) {
396
+ if (isConstrainedCheck(props.check)) {
397
+ return resolveConstrainedCheck({
398
+ ...props,
399
+ check: props.check
400
+ });
401
+ }
402
+ if (typeof props.check !== "function") {
403
+ return props.check;
404
+ }
405
+ const callbackProps = {
406
+ user: props.user,
407
+ data: props.data !== void 0 ? props.data : void 0,
408
+ organization: props.organization !== void 0 ? props.organization : void 0
409
+ };
410
+ if (props.data !== void 0) {
411
+ const result = props.check(callbackProps);
412
+ return result === void 0 ? false : result;
413
+ }
414
+ try {
415
+ const probe = new Proxy(
416
+ {},
417
+ {
418
+ get() {
419
+ throw { [CAPABILITY_PROBE]: true };
420
+ },
421
+ has() {
422
+ throw { [CAPABILITY_PROBE]: true };
423
+ },
424
+ ownKeys() {
425
+ throw { [CAPABILITY_PROBE]: true };
426
+ }
427
+ }
428
+ );
429
+ const result = props.check({ ...callbackProps, data: probe });
430
+ return result === void 0 ? false : result;
431
+ } catch (e) {
432
+ const touchedData = typeof e === "object" && e !== null && e[CAPABILITY_PROBE] === true;
433
+ if (!touchedData) throw e;
434
+ if (props.scope === PERMISSION_SCOPES.any) {
435
+ return true;
436
+ }
437
+ if (props.scope === PERMISSION_SCOPES.all) {
438
+ return false;
439
+ }
440
+ throw new VexAccessError({
441
+ resource: props.resource,
442
+ action: props.action,
443
+ message: `hasPermission: "${props.resource}.${props.action}" needs a "data" object (its check reads the document). Pass "data" for an exact check, or use scope: "any" (nav/list gating) or scope: "all" (bulk actions).`
444
+ });
445
+ }
446
+ }
447
+ function resolveActionCheck(props) {
448
+ if (props.action in props.resource) {
449
+ return props.resource[props.action];
450
+ }
451
+ if (WILDCARD_KEY in props.resource) {
452
+ return props.resource[WILDCARD_KEY];
453
+ }
454
+ return void 0;
455
+ }
456
+
457
+ // src/access/resolveAccessRule.ts
458
+ function isConstrainedCheck2(check) {
459
+ return typeof check === "object" && check !== null && "constraints" in check;
460
+ }
461
+ function classifyRole(props) {
462
+ const defaultAllowed = props.access.defaultPermissionMode === PERMISSION_MODES.allow;
463
+ const roleRules = props.access.permissions[props.role];
464
+ const subject = roleRules?.[props.resource];
465
+ let check;
466
+ if (typeof subject === "boolean") {
467
+ check = subject;
468
+ } else if (subject !== null && subject !== void 0 && typeof subject === "object") {
469
+ check = resolveActionCheck({
470
+ resource: subject,
471
+ action: props.action
472
+ }) ?? defaultAllowed;
473
+ } else {
474
+ const roleWildcard = roleRules?.[WILDCARD_KEY];
475
+ check = typeof roleWildcard === "boolean" ? roleWildcard : defaultAllowed;
476
+ }
477
+ if (check === false) return { kind: "deny" };
478
+ if (check === true) return { kind: "unrestricted" };
479
+ if (!isConstrainedCheck2(check)) return { kind: "opaque" };
480
+ const outcome = check.constraints({
481
+ user: props.user ?? {},
482
+ q: createAccessQueryBuilder(),
483
+ ...props.access.orgCollectionSlug !== void 0 ? { organization: props.organization } : {}
484
+ });
485
+ if (outcome === true) return { kind: "unrestricted" };
486
+ if (outcome === false) return { kind: "deny" };
487
+ const condition = readAccessCondition(outcome);
488
+ if (condition === void 0) return { kind: "opaque" };
489
+ return { kind: "conditional", condition };
490
+ }
491
+ function selectSingleCondition(props) {
492
+ const { access } = props;
493
+ const rawRoles = props.user ? props.user[access.userRolesField] : [];
494
+ const userRoles = typeof rawRoles === "string" ? [rawRoles] : Array.isArray(rawRoles) ? rawRoles.filter((role) => typeof role === "string") : [];
495
+ const effectiveRoles = userRoles.length === 0 && access.anonRole !== void 0 ? [access.anonRole] : userRoles;
496
+ const knownRoles = effectiveRoles.filter((role) => access.roles.includes(role));
497
+ if (knownRoles.length === 0) return void 0;
498
+ const candidates = [];
499
+ for (const role of knownRoles) {
500
+ const contribution = classifyRole({
501
+ access,
502
+ role,
503
+ resource: props.resource,
504
+ action: props.action,
505
+ user: props.user,
506
+ organization: props.organization
507
+ });
508
+ if (contribution.kind === "deny") continue;
509
+ if (contribution.kind === "unrestricted" || contribution.kind === "opaque") return void 0;
510
+ candidates.push(contribution.condition);
511
+ }
512
+ if (candidates.length !== 1) return void 0;
513
+ return candidates[0];
514
+ }
515
+ function resolveAccessIndex(props) {
516
+ const access = props.access;
517
+ if (!access || !access.enabled) return void 0;
518
+ const condition = selectSingleCondition({
519
+ access,
520
+ user: props.user,
521
+ organization: props.organization,
522
+ resource: props.resource,
523
+ action: props.action
524
+ });
525
+ if (condition?.index === void 0) return void 0;
526
+ return {
527
+ name: condition.index.name,
528
+ range: accessConstraintsToIndexRange({ constraints: condition.index.constraints })
529
+ };
530
+ }
531
+ function resolveAccessConstraint(props) {
532
+ const access = props.access;
533
+ if (!access || !access.enabled) return void 0;
534
+ const condition = selectSingleCondition({
535
+ access,
536
+ user: props.user,
537
+ organization: props.organization,
538
+ resource: props.resource,
539
+ action: props.action
540
+ });
541
+ if (condition === void 0) return void 0;
542
+ const { index, filter } = condition;
543
+ const indexConstrains = props.indexAlreadyApplied !== true && index !== void 0 && index.constraints.length > 0;
544
+ if (indexConstrains && filter !== void 0) {
545
+ return (q) => q.and(
546
+ accessConstraintsToFilter({ constraints: index.constraints, q }),
547
+ accessFilterTreeToFilter({ node: filter, q })
548
+ );
549
+ }
550
+ if (indexConstrains) {
551
+ return (q) => accessConstraintsToFilter({ constraints: index.constraints, q });
552
+ }
553
+ if (filter !== void 0) {
554
+ return (q) => accessFilterTreeToFilter({ node: filter, q });
555
+ }
556
+ return void 0;
557
+ }
558
+
559
+ // src/access/pickQueryIndex.ts
560
+ var warnedDisplacements = /* @__PURE__ */ new Set();
561
+ function warnDisplacedAccessIndex(props) {
562
+ if (process.env.NODE_ENV === "production") return;
563
+ const key = `${props.accessIndexName}\0${props.callerIndexName}`;
564
+ if (warnedDisplacements.has(key)) return;
565
+ warnedDisplacements.add(key);
566
+ console.warn(
567
+ `[vexcms] Access index "${props.accessIndexName}" cannot be applied: this query already uses "${props.callerIndexName}". Convex permits one index per query, so the access rule falls back to a per-document check and reads scale with the caller's range instead. Declare a compound index covering both fields to serve them in one query.`
568
+ );
569
+ }
570
+ function composeRanges(props) {
571
+ const { first, second } = props;
572
+ if (!first) return second;
573
+ if (!second) return first;
574
+ return (q) => second(first(q));
575
+ }
576
+ function pickQueryIndex(props) {
577
+ const { accessIndex, callerIndex } = props;
578
+ if (!accessIndex && !callerIndex) return void 0;
579
+ if (!callerIndex) return accessIndex;
580
+ if (!accessIndex) return callerIndex;
581
+ if (accessIndex.name === callerIndex.name) {
582
+ return {
583
+ name: accessIndex.name,
584
+ range: composeRanges({ first: accessIndex.range, second: callerIndex.range })
585
+ };
586
+ }
587
+ warnDisplacedAccessIndex({
588
+ accessIndexName: accessIndex.name,
589
+ callerIndexName: callerIndex.name
590
+ });
591
+ return callerIndex;
592
+ }
593
+
594
+ // src/fields/constants.ts
595
+ var ADMIN_FIELDS = {
596
+ text: {
597
+ type: "text",
598
+ interfaceType: "string",
599
+ validator: "v.string()",
600
+ defaultValue: ""
601
+ },
602
+ url: {
603
+ type: "url",
604
+ interfaceType: "string",
605
+ validator: "v.string()",
606
+ defaultValue: ""
607
+ },
608
+ color: {
609
+ type: "color",
610
+ interfaceType: "string",
611
+ validator: "v.string()",
612
+ defaultValue: ""
613
+ },
614
+ number: {
615
+ type: "number",
616
+ interfaceType: "number",
617
+ validator: "v.number()",
618
+ defaultValue: 0
619
+ },
620
+ checkbox: {
621
+ type: "checkbox",
622
+ interfaceType: "boolean",
623
+ validator: "v.boolean()",
624
+ defaultValue: false
625
+ },
626
+ date: {
627
+ type: "date",
628
+ interfaceType: "number",
629
+ validator: "v.number()",
630
+ defaultValue: void 0
631
+ },
632
+ select: {
633
+ type: "select",
634
+ interfaceType: "string[]",
635
+ validator: "v.array(\nv.string()\n)",
636
+ defaultValue: []
637
+ },
638
+ relationship: {
639
+ type: "relationship",
640
+ interfaceType: "Id<CollectionSlug>[]",
641
+ validator: "v.array(\nv.string()\n)",
642
+ defaultValue: []
643
+ },
644
+ array: {
645
+ type: "array",
646
+ interfaceType: "unknown[]",
647
+ validator: "v.array(\nv.any()\n)",
648
+ defaultValue: []
649
+ },
650
+ group: {
651
+ type: "group",
652
+ interfaceType: "Record<string, any>",
653
+ validator: "v.object({})",
654
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
655
+ defaultValue: {}
656
+ },
657
+ blocks: {
658
+ type: "blocks",
659
+ interfaceType: "Record<string, any>[]",
660
+ // placeholder - blocks() overridses per instance
661
+ validator: "v.array(v.any())",
662
+ // placeholder - blocksFieldToValidator builds dynamically
663
+ defaultValue: []
664
+ },
665
+ upload: {
666
+ type: "upload",
667
+ interfaceType: "Id<MediaCollectionSlug>[]",
668
+ validator: "v.array(\nv.string()\n)",
669
+ defaultValue: void 0
670
+ }
671
+ // richtext: {
672
+ // type: "richtext",
673
+ // validator: "v.any()",
674
+ // defaultValue: {},
675
+ // },
676
+ };
677
+
678
+ // src/api/utils.ts
679
+ function resolveAccessCall(props) {
680
+ const configured = props.config?.access;
681
+ const bypassed = props.access?.bypass === true;
682
+ const action = props.access?.action ?? props.defaultAction;
683
+ if (process.env.NODE_ENV !== "production") {
684
+ if (bypassed && configured === void 0) {
685
+ console.warn(
686
+ `[vexcms] access.bypass was set for "${props.resource}" but this call has no access config, so RBAC was already off and the flag changed nothing. Either drop it, or check that the call receives the config you expect.`
687
+ );
688
+ }
689
+ if (!bypassed && props.access?.action !== void 0 && configured !== void 0) {
690
+ const declaredForResource = configured.customActions?.[props.resource];
691
+ const declared = /* @__PURE__ */ new Set([
692
+ ...Object.values(CRUD_ACTIONS),
693
+ ...Object.values(DRAFT_ACTIONS),
694
+ ...declaredForResource?.query ?? [],
695
+ ...declaredForResource?.mutation ?? []
696
+ ]);
697
+ if (!declared.has(action)) {
698
+ console.warn(
699
+ `[vexcms] Action "${action}" is not declared for "${props.resource}": it is neither a built-in verb nor listed in customActions["${props.resource}"]. No rule can name it, so this call resolves to a denial. Check for a typo.`
700
+ );
701
+ }
702
+ }
703
+ }
704
+ return { access: bypassed ? void 0 : configured, action, resource: props.resource };
705
+ }
706
+ function resolveCollectionSlug(props) {
707
+ for (const c of props.config?.collections ?? []) {
708
+ if (props.ctx.db.normalizeId(c.slug, props.id) !== null) {
709
+ return c.slug;
710
+ }
711
+ }
712
+ throw new Error("[resolveCollectionSlug]: document id does not match a collection slug");
713
+ }
714
+
715
+ // src/api/find/server.ts
716
+ async function find(args) {
717
+ const { access, action, resource } = resolveAccessCall({
718
+ config: args.config,
719
+ access: args.access,
720
+ defaultAction: CRUD_ACTIONS.read,
721
+ resource: args.collection
722
+ });
723
+ const accessIndex = resolveAccessIndex({
724
+ access,
725
+ user: args.auth?.user ?? null,
726
+ organization: args.auth?.organization,
727
+ resource,
728
+ action
729
+ });
730
+ const callerIndex = args.withIndex;
731
+ const resolvedIndex = pickQueryIndex({ accessIndex, callerIndex });
732
+ const accessFilter = resolveAccessConstraint({
733
+ access,
734
+ user: args.auth?.user ?? null,
735
+ organization: args.auth?.organization,
736
+ resource,
737
+ action,
738
+ indexAlreadyApplied: accessIndex !== void 0 && resolvedIndex?.name === accessIndex.name
739
+ });
740
+ const findQuery = buildQuery({
741
+ ...args,
742
+ resolvedIndex,
743
+ accessFilter
744
+ });
745
+ let docs;
746
+ let convexPaginationResult;
747
+ if (args.paginationOpts) {
748
+ convexPaginationResult = await findQuery.paginate(args.paginationOpts);
749
+ docs = convexPaginationResult.page.filter(
750
+ (d) => hasPermission({
751
+ access,
752
+ resource,
753
+ action,
754
+ data: d,
755
+ user: args.auth?.user ?? {},
756
+ organization: args.auth?.organization
757
+ })
758
+ );
759
+ } else if (args.limit) {
760
+ docs = (await findQuery.take(args.limit)).filter(
761
+ (d) => hasPermission({
762
+ access,
763
+ resource,
764
+ action,
765
+ data: d,
766
+ user: args.auth?.user ?? {},
767
+ organization: args.auth?.organization
768
+ })
769
+ );
770
+ } else {
771
+ docs = (await findQuery.collect()).filter(
772
+ (d) => hasPermission({
773
+ access,
774
+ resource,
775
+ action,
776
+ data: d,
777
+ user: args.auth?.user ?? {},
778
+ organization: args.auth?.organization
779
+ })
780
+ );
781
+ }
782
+ const effectivePopulate = args.populate ?? (args.depth !== void 0 && args.depth > 0 && args.config ? buildDepthPopulate(args.config, args.collection, args.depth) : void 0);
783
+ const finalDocs = !effectivePopulate || Object.keys(effectivePopulate).length === 0 ? docs : await populateDocs(args.ctx, docs, effectivePopulate);
784
+ if (args.paginationOpts && convexPaginationResult) {
785
+ if (args.paginationOpts.totalDocs && !args.paginationOpts.cursor) {
786
+ try {
787
+ if (convexPaginationResult.isDone) {
788
+ return {
789
+ ...convexPaginationResult,
790
+ page: finalDocs,
791
+ totalDocs: finalDocs.length
792
+ };
793
+ } else {
794
+ const countQuery = buildQuery({
795
+ ...args,
796
+ resolvedIndex,
797
+ accessFilter
798
+ });
799
+ const totalDocs = (await countQuery.collect()).filter(
800
+ (d) => hasPermission({
801
+ access,
802
+ resource,
803
+ action,
804
+ data: d,
805
+ user: args.auth?.user ?? {},
806
+ organization: args.auth?.organization
807
+ })
808
+ );
809
+ return {
810
+ ...convexPaginationResult,
811
+ page: finalDocs,
812
+ totalDocs: totalDocs.length
813
+ };
814
+ }
815
+ } catch (error) {
816
+ console.warn("Failed to collect all documents:", error);
817
+ return {
818
+ ...convexPaginationResult,
819
+ page: finalDocs,
820
+ totalDocs: null
821
+ // Signals "too large to count"
822
+ };
823
+ }
824
+ }
825
+ return {
826
+ ...convexPaginationResult,
827
+ page: finalDocs
828
+ };
829
+ }
830
+ return finalDocs;
831
+ }
832
+ function buildQuery(props) {
833
+ const tableName = props.collection;
834
+ let q = props.ctx.db.query(tableName);
835
+ if (props.resolvedIndex) {
836
+ q = props.resolvedIndex.range ? q.withIndex(props.resolvedIndex.name, props.resolvedIndex.range) : q.withIndex(props.resolvedIndex.name);
837
+ }
838
+ if (props.order) q = q.order(props.order);
839
+ if (props.accessFilter && props.filter) {
840
+ q = q.filter((filterQ) => filterQ.and(props.accessFilter(filterQ), props.filter(filterQ)));
841
+ } else if (props.accessFilter) {
842
+ q = q.filter(props.accessFilter);
843
+ } else if (props.filter) {
844
+ q = q.filter(props.filter);
845
+ }
846
+ return q;
847
+ }
848
+
849
+ // src/api/get/server.ts
850
+ async function get(args) {
851
+ const doc = await args.ctx.db.get(args.id);
852
+ if (doc && args.config?.access !== void 0) {
853
+ const { access, action, resource } = resolveAccessCall({
854
+ config: args.config,
855
+ access: args.access,
856
+ defaultAction: CRUD_ACTIONS.read,
857
+ resource: args.collection
858
+ });
859
+ hasPermission({
860
+ throwOnDenied: true,
861
+ access,
862
+ user: args.auth?.user ?? {},
863
+ organization: args.auth?.organization,
864
+ resource,
865
+ action,
866
+ data: doc
867
+ });
868
+ }
869
+ const effectivePopulate = args.populate ?? (args.depth !== void 0 && args.depth > 0 && args.config && args.collection ? buildDepthPopulate(args.config, args.collection, args.depth) : void 0);
870
+ if (!effectivePopulate || !doc) {
871
+ return doc;
872
+ }
873
+ const [populated] = await populateDocs(
874
+ args.ctx,
875
+ [doc],
876
+ effectivePopulate
877
+ );
878
+ return populated ?? null;
879
+ }
880
+
881
+ // src/api/search/server.ts
882
+ async function search(args) {
883
+ const { access, action, resource } = resolveAccessCall({
884
+ config: args.config,
885
+ access: args.access,
886
+ defaultAction: CRUD_ACTIONS.read,
887
+ resource: args.collection
888
+ });
889
+ const accessFilter = resolveAccessConstraint({
890
+ access,
891
+ user: args.auth?.user ?? null,
892
+ organization: args.auth?.organization,
893
+ resource,
894
+ action
895
+ });
896
+ const searchQuery = buildQuery2({
897
+ ...args,
898
+ accessFilter
899
+ });
900
+ let docs;
901
+ let convexPaginationResult;
902
+ if (args.paginationOpts) {
903
+ convexPaginationResult = await searchQuery.paginate(args.paginationOpts);
904
+ docs = convexPaginationResult.page.filter(
905
+ (d) => hasPermission({
906
+ access,
907
+ user: args.auth?.user ?? {},
908
+ organization: args.auth?.organization,
909
+ resource,
910
+ action,
911
+ data: d
912
+ })
913
+ );
914
+ } else if (args.limit) {
915
+ docs = (await searchQuery.take(args.limit)).filter(
916
+ (d) => hasPermission({
917
+ access,
918
+ user: args.auth?.user ?? {},
919
+ organization: args.auth?.organization,
920
+ resource,
921
+ action,
922
+ data: d
923
+ })
924
+ );
925
+ } else {
926
+ docs = (await searchQuery.collect()).filter(
927
+ (d) => hasPermission({
928
+ access,
929
+ user: args.auth?.user ?? {},
930
+ organization: args.auth?.organization,
931
+ resource,
932
+ action,
933
+ data: d
934
+ })
935
+ );
936
+ }
937
+ const effectivePopulate = args.populate ?? (args.depth !== void 0 && args.depth > 0 && args.config ? buildDepthPopulate(args.config, args.collection, args.depth) : void 0);
938
+ const finalDocs = !effectivePopulate || Object.keys(effectivePopulate).length === 0 ? docs : await populateDocs(args.ctx, docs, effectivePopulate);
939
+ if (args.paginationOpts) {
940
+ if (args.paginationOpts.totalDocs && !args.paginationOpts.cursor) {
941
+ try {
942
+ const countQuery = buildQuery2(args);
943
+ const allDocs = (await countQuery.collect()).filter(
944
+ (d) => hasPermission({
945
+ access,
946
+ user: args.auth?.user ?? {},
947
+ organization: args.auth?.organization,
948
+ resource,
949
+ action,
950
+ data: d
951
+ })
952
+ );
953
+ if (convexPaginationResult) {
954
+ return {
955
+ ...convexPaginationResult,
956
+ page: finalDocs,
957
+ totalDocs: allDocs.length
958
+ };
959
+ }
960
+ } catch (error) {
961
+ console.warn("Failed to count search results:", error);
962
+ if (convexPaginationResult) {
963
+ return {
964
+ ...convexPaginationResult,
965
+ page: finalDocs,
966
+ totalDocs: null
967
+ };
968
+ }
969
+ }
970
+ }
971
+ if (convexPaginationResult) {
972
+ return {
973
+ ...convexPaginationResult,
974
+ page: finalDocs
975
+ };
976
+ }
977
+ }
978
+ return finalDocs;
979
+ }
980
+ function buildQuery2(args) {
981
+ let q = args.ctx.db.query(args.collection);
982
+ if (args.query) {
983
+ q = q.withSearchIndex(args.searchIndexName, (q2) => q2.search(args.searchField, args.query));
984
+ }
985
+ if (args.accessFilter && args.filter) {
986
+ q = q.filter((fq) => fq.and(args.accessFilter(fq), args.filter(fq)));
987
+ } else if (args.accessFilter) {
988
+ q = q.filter(args.accessFilter);
989
+ } else if (args.filter) {
990
+ q = q.filter(args.filter);
991
+ }
992
+ return q;
993
+ }
994
+
995
+ // src/api/create/server.ts
996
+ async function create(args) {
997
+ if (args.config.access !== void 0) {
998
+ const { access, action, resource } = resolveAccessCall({
999
+ config: args.config,
1000
+ access: args.access,
1001
+ defaultAction: CRUD_ACTIONS.create,
1002
+ resource: args.collection
1003
+ });
1004
+ hasPermission({
1005
+ access,
1006
+ user: args.auth?.user ?? {},
1007
+ organization: args.auth?.organization,
1008
+ resource,
1009
+ action,
1010
+ data: args.data,
1011
+ throwOnDenied: true
1012
+ });
1013
+ }
1014
+ const id = await args.ctx.db.insert(
1015
+ args.collection,
1016
+ args.data
1017
+ );
1018
+ return id;
1019
+ }
1020
+
1021
+ // src/api/update/server.ts
1022
+ async function update(args) {
1023
+ if (args.config.access !== void 0) {
1024
+ const doc = await args.ctx.db.get(args.id);
1025
+ const { access, action, resource } = resolveAccessCall({
1026
+ config: args.config,
1027
+ access: args.access,
1028
+ defaultAction: CRUD_ACTIONS.update,
1029
+ resource: args.collection
1030
+ });
1031
+ hasPermission({
1032
+ throwOnDenied: true,
1033
+ access,
1034
+ user: args.auth?.user ?? {},
1035
+ organization: args.auth?.organization,
1036
+ resource,
1037
+ action,
1038
+ data: doc ?? void 0
1039
+ });
1040
+ }
1041
+ await args.ctx.db.patch(args.id, args.data);
1042
+ }
1043
+
1044
+ // src/api/remove/server.ts
1045
+ async function remove(args) {
1046
+ async function removeById(id) {
1047
+ if (args.config.access !== void 0) {
1048
+ const doc = await args.ctx.db.get(id);
1049
+ const { access, action, resource } = resolveAccessCall({
1050
+ config: args.config,
1051
+ access: args.access,
1052
+ defaultAction: CRUD_ACTIONS.delete,
1053
+ resource: args.collection
1054
+ });
1055
+ hasPermission({
1056
+ throwOnDenied: true,
1057
+ access,
1058
+ user: args.auth?.user ?? {},
1059
+ organization: args.auth?.organization,
1060
+ resource,
1061
+ action,
1062
+ data: doc ?? void 0
1063
+ });
1064
+ }
1065
+ if (args.softDelete) {
1066
+ return await args.ctx.db.patch(args.collection, id, { [args.softDelete]: true });
1067
+ }
1068
+ return await args.ctx.db.delete(args.collection, id);
1069
+ }
1070
+ await Promise.all(args.ids.map((id) => removeById(id)));
1071
+ }
1072
+
1073
+ // src/api/globals/get.server.ts
1074
+ function flattenGlobalRow(row) {
1075
+ const { slug, data, _id, _creationTime } = row;
1076
+ return { _id, _creationTime, _slug: slug, ...data ?? {} };
1077
+ }
1078
+ async function getGlobal(args) {
1079
+ const { ctx, slug, populate, depth, config } = args;
1080
+ const row = await ctx.db.query("vex_globals").withIndex("by_slug", (q) => q.eq("slug", slug)).first();
1081
+ if (!row) return null;
1082
+ let flat = flattenGlobalRow(row);
1083
+ if (args.config?.access !== void 0) {
1084
+ const { access, action, resource } = resolveAccessCall({
1085
+ config: args.config,
1086
+ access: args.access,
1087
+ defaultAction: CRUD_ACTIONS.read,
1088
+ resource: args.slug
1089
+ });
1090
+ hasPermission({
1091
+ throwOnDenied: true,
1092
+ user: args.auth?.user ?? {},
1093
+ organization: args.auth?.organization,
1094
+ access,
1095
+ resource,
1096
+ action,
1097
+ data: flat
1098
+ });
1099
+ }
1100
+ if (depth && depth > 0 && config) {
1101
+ const globalConfig = config.globals.find((g) => g.slug === slug);
1102
+ if (globalConfig) {
1103
+ const depthPopulate = buildDepthPopulate(config, slug, depth);
1104
+ if (depthPopulate && Object.keys(depthPopulate).length > 0) {
1105
+ const [populated] = await populateDocs(ctx, [flat], depthPopulate);
1106
+ flat = populated;
1107
+ }
1108
+ }
1109
+ }
1110
+ if (populate && Object.keys(populate).length > 0) {
1111
+ const [populated] = await populateDocs(ctx, [flat], populate);
1112
+ flat = populated;
1113
+ }
1114
+ return flat;
1115
+ }
1116
+
1117
+ // src/api/globals/find.server.ts
1118
+ async function findGlobals(args) {
1119
+ const { ctx } = args;
1120
+ let rows = await ctx.db.query("vex_globals").collect();
1121
+ if (args.config.access !== void 0) {
1122
+ rows = rows.filter((r) => {
1123
+ const { access, action, resource } = resolveAccessCall({
1124
+ config: args.config,
1125
+ access: args.access,
1126
+ defaultAction: CRUD_ACTIONS.read,
1127
+ resource: r.slug
1128
+ });
1129
+ return hasPermission({
1130
+ access,
1131
+ user: args.auth?.user ?? {},
1132
+ organization: args.auth?.organization,
1133
+ resource,
1134
+ action,
1135
+ data: r.data
1136
+ });
1137
+ });
1138
+ }
1139
+ return rows.map((row) => {
1140
+ const { slug, data, _id, _creationTime } = row;
1141
+ return { _id, _creationTime, _slug: slug, ...data ?? {} };
1142
+ });
1143
+ }
1144
+
1145
+ // src/api/globals/upsert.server.ts
1146
+ import { ConvexError as ConvexError2 } from "convex/values";
1147
+
1148
+ // src/globals/utils.ts
1149
+ import { z as z13 } from "zod";
1150
+
1151
+ // src/fields/text/inputSchema.ts
1152
+ import { z } from "zod";
1153
+
1154
+ // src/fields/inputSchemas/utils.ts
1155
+ function applyBaseInputSchemaMeta(props) {
1156
+ const { field, inputSchema } = props;
1157
+ const fieldMeta = {
1158
+ fieldType: field.type,
1159
+ label: field.label,
1160
+ description: field.description ?? ""
1161
+ };
1162
+ if (!field.required) {
1163
+ return inputSchema.meta(fieldMeta).optional();
1164
+ }
1165
+ return inputSchema.meta(fieldMeta);
1166
+ }
1167
+
1168
+ // src/fields/text/inputSchema.ts
1169
+ function textFieldToInputSchema(props) {
1170
+ const { field } = props;
1171
+ const fieldMinError = field.min?.error ?? "This field is too short.";
1172
+ const fieldMaxError = field.max?.error ?? "This field is too long.";
1173
+ let inputSchema = z.string().default(field.defaultValue);
1174
+ if (field.required) {
1175
+ inputSchema = z.string().min(1, "This field is required.").default(field.defaultValue);
1176
+ }
1177
+ if (field.min) {
1178
+ if (field.max) {
1179
+ inputSchema = z.string().min(field.min.value, fieldMinError).max(field.max.value, fieldMaxError).default(field.defaultValue);
1180
+ } else {
1181
+ inputSchema = z.string().min(field.min.value, fieldMinError).default(field.defaultValue);
1182
+ }
1183
+ } else if (field.max) {
1184
+ inputSchema = z.string().max(field.max.value, fieldMaxError).default(field.defaultValue);
1185
+ }
1186
+ return applyBaseInputSchemaMeta({ field, inputSchema });
1187
+ }
1188
+
1189
+ // src/fields/number/inputSchema.ts
1190
+ import { z as z2 } from "zod";
1191
+ function numberFieldToInputSchema(props) {
1192
+ const { field } = props;
1193
+ const fieldMinError = field.min?.error ?? "This field is too small.";
1194
+ const fieldMaxError = field.max?.error ?? "This field is too large.";
1195
+ let inputSchema = z2.number().default(field.defaultValue);
1196
+ if (field.min) {
1197
+ let defaultValue = field.defaultValue < field.min.value ? field.min.value : field.defaultValue;
1198
+ if (field.max) {
1199
+ if (field.defaultValue > field.max.value) {
1200
+ defaultValue = field.min.value;
1201
+ }
1202
+ inputSchema = z2.number().min(field.min.value, fieldMinError).max(field.max.value, fieldMaxError).default(defaultValue);
1203
+ } else {
1204
+ inputSchema = z2.number().min(field.min.value, fieldMinError).default(defaultValue);
1205
+ }
1206
+ } else if (field.max) {
1207
+ let defaultValue = field.defaultValue > field.max.value ? field.max.value : field.defaultValue;
1208
+ inputSchema = z2.number().max(field.max.value, fieldMaxError).default(defaultValue);
1209
+ }
1210
+ return applyBaseInputSchemaMeta({ field, inputSchema });
1211
+ }
1212
+
1213
+ // src/fields/checkbox/inputSchema.ts
1214
+ import { z as z3 } from "zod";
1215
+ function checkboxFieldToInputSchema(props) {
1216
+ const { field } = props;
1217
+ const inputSchema = z3.boolean().default(field.defaultValue);
1218
+ return applyBaseInputSchemaMeta({ field, inputSchema });
1219
+ }
1220
+
1221
+ // src/fields/date/inputSchema.ts
1222
+ import { z as z4 } from "zod";
1223
+ function dateFieldToInputSchema(props) {
1224
+ const { field } = props;
1225
+ let inputSchema = z4.number();
1226
+ if (field.required) {
1227
+ inputSchema = z4.number().default((/* @__PURE__ */ new Date()).getTime());
1228
+ }
1229
+ return applyBaseInputSchemaMeta({ field, inputSchema });
1230
+ }
1231
+
1232
+ // src/fields/select/inputSchema.ts
1233
+ import { z as z5 } from "zod";
1234
+ function selectFieldToInputSchema(props) {
1235
+ const { field } = props;
1236
+ const optionValues = field.options.map((o) => o.value);
1237
+ const itemSchema = optionValues.length > 0 ? z5.enum(optionValues) : z5.string();
1238
+ let inputSchema = z5.array(itemSchema).default(field.defaultValue);
1239
+ if (!field.hasMany) {
1240
+ inputSchema = z5.array(itemSchema).max(1, "Only one value may be selected.").default(field.defaultValue);
1241
+ }
1242
+ return applyBaseInputSchemaMeta({ field, inputSchema });
1243
+ }
1244
+
1245
+ // src/fields/url/inputSchema.ts
1246
+ import { z as z6 } from "zod";
1247
+ function urlFieldToInputSchema(props) {
1248
+ const { field } = props;
1249
+ let inputSchema = z6.url();
1250
+ if (field.required) {
1251
+ inputSchema = z6.url().min(1, "This field is required.");
1252
+ if (field.defaultValue) {
1253
+ inputSchema = z6.url().min(1, "This field is required.").default(field.defaultValue);
1254
+ }
1255
+ } else if (field.defaultValue !== void 0) {
1256
+ inputSchema = z6.union([z6.url(), z6.literal("")]).default(field.defaultValue);
1257
+ }
1258
+ return applyBaseInputSchemaMeta({ field, inputSchema });
1259
+ }
1260
+
1261
+ // src/fields/color/formats.ts
1262
+ var COLOR_FORMATS = {
1263
+ hex: {
1264
+ format: "hex",
1265
+ /** `#E8622A`, `#E8622A80`. Shorthand is rejected — the picker never emits it. */
1266
+ pattern: /^#[0-9a-fA-F]{6}(?:[0-9a-fA-F]{2})?$/
1267
+ },
1268
+ rgb: {
1269
+ format: "rgb",
1270
+ /** `rgb(232, 98, 42)`, `rgba(232, 98, 42, 0.5)`. */
1271
+ pattern: /^rgba?\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*(?:,\s*(?:0|1|0?\.\d+)\s*)?\)$/
1272
+ },
1273
+ hsl: {
1274
+ format: "hsl",
1275
+ /** `hsl(17.7, 81%, 54%)`, `hsla(17.7, 81%, 54%, 0.5)`. */
1276
+ pattern: /^hsla?\(\s*-?\d+(?:\.\d+)?\s*,\s*\d+(?:\.\d+)?%\s*,\s*\d+(?:\.\d+)?%\s*(?:,\s*(?:0|1|0?\.\d+)\s*)?\)$/
1277
+ },
1278
+ oklch: {
1279
+ format: "oklch",
1280
+ /** `oklch(65.7% 0.179 40.9)`, `oklch(65.7% 0.179 40.9 / 0.5)`. */
1281
+ pattern: /^oklch\(\s*\d+(?:\.\d+)?%\s+\d+(?:\.\d+)?\s+-?\d+(?:\.\d+)?\s*(?:\/\s*(?:0|1|0?\.\d+)\s*)?\)$/
1282
+ }
1283
+ };
1284
+ var COLOR_FORMAT_PATTERNS = Object.values(COLOR_FORMATS).map((f) => f.pattern);
1285
+ var CSS_VAR_REFERENCE = /^var\(--[A-Za-z0-9_-]+\)$/;
1286
+
1287
+ // src/fields/color/inputSchema.ts
1288
+ import { z as z7 } from "zod";
1289
+ var FORMAT_EXAMPLES = {
1290
+ hex: "#E8622A",
1291
+ rgb: "rgb(232, 98, 42)",
1292
+ hsl: "hsl(17.7, 81%, 54%)",
1293
+ oklch: "oklch(65.7% 0.179 40.9)"
1294
+ };
1295
+ function colorPattern(props) {
1296
+ const sources = COLOR_FORMAT_PATTERNS.map((pattern) => pattern.source);
1297
+ if (props.themeColors) sources.push(CSS_VAR_REFERENCE.source);
1298
+ return new RegExp(sources.join("|"));
1299
+ }
1300
+ function colorMessage(props) {
1301
+ const example = FORMAT_EXAMPLES[props.field.format];
1302
+ const base = `Enter a colour, e.g. ${example}.`;
1303
+ if (!props.field.themeColors) return base;
1304
+ return `${base} A theme token such as var(--primary) is also accepted.`;
1305
+ }
1306
+ function colorFieldToInputSchema(props) {
1307
+ const { field } = props;
1308
+ const pattern = colorPattern({ themeColors: field.themeColors });
1309
+ const message = colorMessage({ field });
1310
+ let inputSchema = z7.string().regex(pattern, message);
1311
+ if (field.required) {
1312
+ inputSchema = z7.string().min(1, "This field is required.").regex(pattern, message);
1313
+ if (field.defaultValue) {
1314
+ inputSchema = inputSchema.default(field.defaultValue);
1315
+ }
1316
+ } else if (field.defaultValue !== void 0) {
1317
+ inputSchema = z7.union([z7.string().regex(pattern, message), z7.literal("")]).default(field.defaultValue);
1318
+ }
1319
+ return applyBaseInputSchemaMeta({ field, inputSchema });
1320
+ }
1321
+
1322
+ // src/fields/relationship/inputSchema.ts
1323
+ import { z as z8 } from "zod";
1324
+ function relationshipFieldToInputSchema(props) {
1325
+ const { field } = props;
1326
+ const inputSchema = z8.array(z8.string()).default(ADMIN_FIELDS.relationship.defaultValue);
1327
+ return applyBaseInputSchemaMeta({ field, inputSchema });
1328
+ }
1329
+
1330
+ // src/fields/array/inputSchema.ts
1331
+ import { z as z12 } from "zod";
1332
+
1333
+ // src/fields/group/inputSchema.ts
1334
+ import { z as z9 } from "zod";
1335
+ function groupFieldToInputSchema(props) {
1336
+ const { field } = props;
1337
+ const subSchemas = Object.fromEntries(
1338
+ Object.entries(field.fields).map(([key, subField]) => [
1339
+ key,
1340
+ adminFieldToInputSchema({ field: subField })
1341
+ ])
1342
+ );
1343
+ const schema = z9.object(subSchemas).default(field.defaultValue ?? {});
1344
+ return applyBaseInputSchemaMeta({ field, inputSchema: schema });
1345
+ }
1346
+
1347
+ // src/fields/blocks/inputSchema.ts
1348
+ import { z as z10 } from "zod";
1349
+ function blocksFieldToInputSchema(props) {
1350
+ const { field } = props;
1351
+ const blockSchemas = field.blocks.map((block) => {
1352
+ const userSubSchemas = Object.fromEntries(
1353
+ Object.entries(block.fields).map(([key, subField]) => [
1354
+ key,
1355
+ adminFieldToInputSchema({ field: subField })
1356
+ ])
1357
+ );
1358
+ return z10.object({
1359
+ blockType: z10.literal(block.blockType),
1360
+ blockName: z10.string().optional(),
1361
+ id: z10.string(),
1362
+ ...userSubSchemas
1363
+ });
1364
+ });
1365
+ const itemSchema = blockSchemas.length <= 1 ? blockSchemas[0] ?? z10.object({ blockType: z10.string(), id: z10.string() }) : (
1366
+ // @ts-expect-error mismatched zod types, works in practice
1367
+ z10.discriminatedUnion("blockType", blockSchemas)
1368
+ );
1369
+ let schema = z10.array(itemSchema);
1370
+ if (field.min) {
1371
+ schema = schema.min(field.min, `At least ${field.min} ${field.labels.plural} required.`);
1372
+ }
1373
+ if (field.max) {
1374
+ schema = schema.max(field.max, `No more than ${field.max} ${field.labels.plural} allowed.`);
1375
+ }
1376
+ schema = schema.default(field.defaultValue ?? []);
1377
+ return applyBaseInputSchemaMeta({ field, inputSchema: schema });
1378
+ }
1379
+
1380
+ // src/fields/upload/inputSchema.ts
1381
+ import { z as z11 } from "zod";
1382
+ function uploadFieldToInputSchema(props) {
1383
+ const inputSchema = z11.array(z11.string());
1384
+ return applyBaseInputSchemaMeta({ field: props.field, inputSchema });
1385
+ }
1386
+
1387
+ // src/fields/inputSchemas/index.ts
1388
+ function adminFieldToInputSchema(props) {
1389
+ const fieldType = props.field.type;
1390
+ switch (props.field.type) {
1391
+ case ADMIN_FIELDS.text.type:
1392
+ return textFieldToInputSchema({ field: props.field });
1393
+ case ADMIN_FIELDS.number.type:
1394
+ return numberFieldToInputSchema({ field: props.field });
1395
+ case ADMIN_FIELDS.checkbox.type:
1396
+ return checkboxFieldToInputSchema({ field: props.field });
1397
+ case ADMIN_FIELDS.date.type:
1398
+ return dateFieldToInputSchema({ field: props.field });
1399
+ case ADMIN_FIELDS.select.type:
1400
+ return selectFieldToInputSchema({ field: props.field });
1401
+ case ADMIN_FIELDS.url.type:
1402
+ return urlFieldToInputSchema({ field: props.field });
1403
+ case ADMIN_FIELDS.color.type:
1404
+ return colorFieldToInputSchema({ field: props.field });
1405
+ case ADMIN_FIELDS.relationship.type:
1406
+ return relationshipFieldToInputSchema({ field: props.field });
1407
+ case ADMIN_FIELDS.array.type:
1408
+ return arrayFieldToInputSchema({ field: props.field });
1409
+ case ADMIN_FIELDS.group.type:
1410
+ return groupFieldToInputSchema({ field: props.field });
1411
+ case ADMIN_FIELDS.blocks.type:
1412
+ return blocksFieldToInputSchema({ field: props.field });
1413
+ case ADMIN_FIELDS.upload.type:
1414
+ return uploadFieldToInputSchema({ field: props.field });
1415
+ default: {
1416
+ const unhandled = props.field;
1417
+ throw new Error(
1418
+ `unrecognized field type: ${fieldType} \u2014 ${JSON.stringify(unhandled)}`
1419
+ );
1420
+ }
1421
+ }
1422
+ }
1423
+
1424
+ // src/fields/array/inputSchema.ts
1425
+ function arrayFieldToInputSchema(props) {
1426
+ const { field } = props;
1427
+ const fieldMinError = field.min?.error ?? "This field is too short.";
1428
+ const fieldMaxError = field.max?.error ?? "This field is too long.";
1429
+ const itemsInputSchema = adminFieldToInputSchema({ field: field.items });
1430
+ let inputSchema = z12.array(itemsInputSchema).default(field.defaultValue ?? []);
1431
+ if (field.required) {
1432
+ inputSchema = z12.array(itemsInputSchema).superRefine((val, ctx) => {
1433
+ if (val === void 0 || val === null) {
1434
+ ctx.addIssue({
1435
+ code: "custom",
1436
+ message: "This field is required."
1437
+ });
1438
+ }
1439
+ }).default(field.defaultValue ?? []);
1440
+ }
1441
+ if (field.min) {
1442
+ if (field.max) {
1443
+ inputSchema = z12.array(itemsInputSchema).min(field.min.value, fieldMinError).max(field.max.value, fieldMaxError).default(field.defaultValue ?? []);
1444
+ } else {
1445
+ inputSchema = z12.array(itemsInputSchema).min(field.min.value, fieldMinError).default(field.defaultValue ?? []);
1446
+ }
1447
+ } else if (field.max) {
1448
+ inputSchema = z12.array(itemsInputSchema).max(field.max.value, fieldMaxError).default(field.defaultValue ?? []);
1449
+ }
1450
+ let finalSchema = inputSchema;
1451
+ if (!field.required && field.defaultValue !== void 0) {
1452
+ finalSchema = inputSchema.default(field.defaultValue);
1453
+ }
1454
+ finalSchema = applyBaseInputSchemaMeta({
1455
+ field,
1456
+ inputSchema: finalSchema
1457
+ });
1458
+ return finalSchema;
1459
+ }
1460
+
1461
+ // src/globals/utils.ts
1462
+ function getGlobalDefaultValues(props) {
1463
+ const res = {};
1464
+ for (const [fieldKey, fieldDef] of Object.entries(props.global.fields)) {
1465
+ if (fieldDef.admin.hidden) continue;
1466
+ if (props.document && Boolean(props.document[fieldKey])) {
1467
+ res[fieldKey] = props.document[fieldKey];
1468
+ } else {
1469
+ res[fieldKey] = fieldDef.defaultValue;
1470
+ }
1471
+ }
1472
+ return res;
1473
+ }
1474
+ function getGlobalInputSchema(props) {
1475
+ const res = {};
1476
+ for (const [fieldKey, fieldDef] of Object.entries(props.global.fields)) {
1477
+ if (fieldDef.admin.hidden) continue;
1478
+ res[fieldKey] = adminFieldToInputSchema({ field: fieldDef });
1479
+ }
1480
+ return z13.object({ ...res });
1481
+ }
1482
+
1483
+ // src/api/globals/upsert.server.ts
1484
+ var STRIPPED_KEYS = /* @__PURE__ */ new Set(["_id", "_creationTime", "_slug"]);
1485
+ async function upsertGlobal(args) {
1486
+ const { ctx, slug, data } = args;
1487
+ const globalConfig = args.config.globals.find((g) => g.slug === args.slug);
1488
+ if (!globalConfig) {
1489
+ throw new ConvexError2(`No global registered with slug "${args.slug}"`);
1490
+ }
1491
+ if (args.config.access !== void 0) {
1492
+ const { access, action, resource } = resolveAccessCall({
1493
+ config: args.config,
1494
+ access: args.access,
1495
+ defaultAction: CRUD_ACTIONS.read,
1496
+ resource: args.slug
1497
+ });
1498
+ hasPermission({
1499
+ throwOnDenied: true,
1500
+ access,
1501
+ user: args.auth?.user ?? {},
1502
+ organization: args.auth?.organization,
1503
+ resource,
1504
+ action
1505
+ });
1506
+ }
1507
+ const userFields = {};
1508
+ for (const [k, v2] of Object.entries(data)) {
1509
+ if (!STRIPPED_KEYS.has(k)) userFields[k] = v2;
1510
+ }
1511
+ const schema = getGlobalInputSchema({ global: globalConfig });
1512
+ const result = schema.safeParse(userFields);
1513
+ if (!result.success) {
1514
+ throw new ConvexError2({
1515
+ message: "Global validation failed",
1516
+ errors: result.error.message
1517
+ });
1518
+ }
1519
+ const existingGlobal = await ctx.db.query("vex_globals").withIndex("by_slug", (q) => q.eq("slug", slug)).first();
1520
+ if (existingGlobal) {
1521
+ await ctx.db.patch(existingGlobal._id, { data: result.data });
1522
+ return existingGlobal._id;
1523
+ }
1524
+ const id = await ctx.db.insert("vex_globals", {
1525
+ slug,
1526
+ data: result.data
1527
+ });
1528
+ return id;
1529
+ }
1530
+
1531
+ // src/api/server.ts
1532
+ function collectionsApi({
1533
+ config,
1534
+ query,
1535
+ mutation,
1536
+ getAuth
1537
+ }) {
1538
+ return {
1539
+ find: query({
1540
+ args: {
1541
+ collection: v.string(),
1542
+ populate: v.optional(v.any()),
1543
+ depth: v.optional(v.number()),
1544
+ limit: v.optional(v.number()),
1545
+ paginationOpts: v.optional(
1546
+ paginationOptsValidator.extend({ totalDocs: v.optional(v.boolean()) })
1547
+ )
1548
+ },
1549
+ handler: async (ctx, args) => {
1550
+ const auth = await resolveGetAuth({ ctx, config, getAuth });
1551
+ return await find({
1552
+ ctx,
1553
+ auth,
1554
+ collection: args.collection,
1555
+ populate: args.populate,
1556
+ depth: args.depth,
1557
+ config,
1558
+ limit: args.limit,
1559
+ paginationOpts: args.paginationOpts
1560
+ });
1561
+ }
1562
+ }),
1563
+ get: query({
1564
+ args: {
1565
+ collection: v.string(),
1566
+ id: v.string(),
1567
+ populate: v.optional(v.any()),
1568
+ depth: v.optional(v.number())
1569
+ },
1570
+ handler: async (ctx, args) => {
1571
+ const auth = await resolveGetAuth({ ctx, config, getAuth });
1572
+ return await get({
1573
+ auth,
1574
+ ctx,
1575
+ collection: args.collection,
1576
+ id: args.id,
1577
+ populate: args.populate,
1578
+ depth: args.depth,
1579
+ config
1580
+ });
1581
+ }
1582
+ }),
1583
+ search: query({
1584
+ args: {
1585
+ collection: v.string(),
1586
+ searchIndexName: v.string(),
1587
+ searchField: v.string(),
1588
+ query: v.string(),
1589
+ limit: v.optional(v.number()),
1590
+ populate: v.optional(v.any()),
1591
+ depth: v.optional(v.number()),
1592
+ paginationOpts: v.optional(
1593
+ paginationOptsValidator.extend({ totalDocs: v.optional(v.boolean()) })
1594
+ )
1595
+ },
1596
+ handler: async (ctx, args) => {
1597
+ const auth = await resolveGetAuth({ ctx, config, getAuth });
1598
+ return search({
1599
+ auth,
1600
+ ctx,
1601
+ collection: args.collection,
1602
+ query: args.query,
1603
+ searchIndexName: args.searchIndexName,
1604
+ searchField: args.searchField,
1605
+ limit: args.limit,
1606
+ populate: args.populate,
1607
+ depth: args.depth,
1608
+ paginationOpts: args.paginationOpts,
1609
+ config
1610
+ });
1611
+ }
1612
+ }),
1613
+ // MUTATIONS
1614
+ create: mutation({
1615
+ args: {
1616
+ collection: v.string(),
1617
+ data: v.any()
1618
+ },
1619
+ returns: v.string(),
1620
+ handler: async (ctx, args) => {
1621
+ const auth = await resolveGetAuth({ ctx, config, getAuth });
1622
+ return await create({
1623
+ auth,
1624
+ ctx,
1625
+ config,
1626
+ collection: args.collection,
1627
+ data: args.data
1628
+ });
1629
+ }
1630
+ }),
1631
+ update: mutation({
1632
+ args: {
1633
+ id: v.string(),
1634
+ collection: v.string(),
1635
+ data: v.any()
1636
+ },
1637
+ handler: async (ctx, args) => {
1638
+ const auth = await resolveGetAuth({ ctx, config, getAuth });
1639
+ return update({
1640
+ auth,
1641
+ ctx,
1642
+ config,
1643
+ collection: args.collection,
1644
+ id: args.id,
1645
+ data: args.data
1646
+ });
1647
+ }
1648
+ }),
1649
+ remove: mutation({
1650
+ args: {
1651
+ collection: v.string(),
1652
+ ids: v.array(v.string()),
1653
+ softDelete: v.optional(v.string())
1654
+ },
1655
+ handler: async (ctx, args) => {
1656
+ const auth = await resolveGetAuth({ ctx, config, getAuth });
1657
+ return remove({
1658
+ auth,
1659
+ collection: args.collection,
1660
+ ctx,
1661
+ config,
1662
+ ids: args.ids,
1663
+ softDelete: args.softDelete
1664
+ });
1665
+ }
1666
+ })
1667
+ };
1668
+ }
1669
+ function globalsApi({
1670
+ config,
1671
+ query,
1672
+ mutation,
1673
+ getAuth
1674
+ }) {
1675
+ return {
1676
+ get: query({
1677
+ args: {
1678
+ slug: v.string(),
1679
+ populate: v.optional(v.any())
1680
+ },
1681
+ handler: async (ctx, args) => {
1682
+ const auth = await resolveGetAuth({ ctx, config, getAuth });
1683
+ return await getGlobal({
1684
+ auth,
1685
+ ctx,
1686
+ slug: args.slug,
1687
+ populate: args.populate,
1688
+ config
1689
+ });
1690
+ }
1691
+ }),
1692
+ find: query({
1693
+ args: {},
1694
+ handler: async (ctx) => {
1695
+ const auth = await resolveGetAuth({ ctx, config, getAuth });
1696
+ return await findGlobals({ auth, ctx, config });
1697
+ }
1698
+ }),
1699
+ upsert: mutation({
1700
+ args: {
1701
+ slug: v.string(),
1702
+ data: v.any()
1703
+ },
1704
+ returns: v.string(),
1705
+ handler: async (ctx, args) => {
1706
+ const auth = await resolveGetAuth({ ctx, config, getAuth });
1707
+ return await upsertGlobal({
1708
+ auth,
1709
+ ctx,
1710
+ config,
1711
+ slug: args.slug,
1712
+ data: args.data
1713
+ });
1714
+ }
1715
+ })
1716
+ };
1717
+ }
1718
+ async function resolveGetAuth({
1719
+ config,
1720
+ ctx,
1721
+ getAuth
1722
+ }) {
1723
+ let auth = void 0;
1724
+ if (config.access !== void 0) {
1725
+ if (!getAuth) {
1726
+ throw new VexAccessConfigError(
1727
+ "getAuth not configured in collectionsApi. configure @ /src/convex/vex.ts"
1728
+ );
1729
+ }
1730
+ auth = await getAuth(ctx);
1731
+ }
1732
+ return auth;
1733
+ }
1734
+ function vexServerApi(options) {
1735
+ const { config, getAuth } = options;
1736
+ async function inject(ctx, bypassAccess) {
1737
+ const authConfig = bypassAccess === true ? { ...config, access: void 0 } : config;
1738
+ return { config, auth: await resolveGetAuth({ ctx, config: authConfig, getAuth }) };
1739
+ }
1740
+ return {
1741
+ get: async (args) => {
1742
+ const { access, ...rest } = args;
1743
+ return await get({
1744
+ access,
1745
+ ...rest,
1746
+ ...await inject(rest.ctx, access?.bypass)
1747
+ });
1748
+ },
1749
+ // Cast to the overloaded interface: an implementation signature cannot be
1750
+ // structurally assignable to two call signatures at once.
1751
+ find: (async (args) => {
1752
+ const { access, ...rest } = args;
1753
+ return await find({
1754
+ access,
1755
+ ...rest,
1756
+ ...await inject(rest.ctx, access?.bypass)
1757
+ });
1758
+ }),
1759
+ search: (async (args) => {
1760
+ const { access, ...rest } = args;
1761
+ return await search({
1762
+ access,
1763
+ ...rest,
1764
+ ...await inject(rest.ctx, access?.bypass)
1765
+ });
1766
+ }),
1767
+ create: async (args) => {
1768
+ const { access, ...rest } = args;
1769
+ return await create({
1770
+ access,
1771
+ ...rest,
1772
+ ...await inject(rest.ctx, access?.bypass)
1773
+ });
1774
+ },
1775
+ update: async (args) => {
1776
+ const { access, ...rest } = args;
1777
+ return await update({
1778
+ access,
1779
+ ...rest,
1780
+ ...await inject(rest.ctx, access?.bypass)
1781
+ });
1782
+ },
1783
+ remove: async (args) => {
1784
+ const { access, ...rest } = args;
1785
+ return await remove({
1786
+ access,
1787
+ ...rest,
1788
+ ...await inject(rest.ctx, access?.bypass)
1789
+ });
1790
+ },
1791
+ globals: {
1792
+ get: async (args) => {
1793
+ const { access, ...rest } = args;
1794
+ return await getGlobal({
1795
+ access,
1796
+ ...rest,
1797
+ ...await inject(rest.ctx, access?.bypass)
1798
+ });
1799
+ },
1800
+ find: async (args) => {
1801
+ const { access, ...rest } = args;
1802
+ return await findGlobals({
1803
+ access,
1804
+ ...rest,
1805
+ ...await inject(rest.ctx, access?.bypass)
1806
+ });
1807
+ },
1808
+ upsert: async (args) => {
1809
+ const { access, ...rest } = args;
1810
+ return await upsertGlobal({
1811
+ access,
1812
+ ...rest,
1813
+ ...await inject(rest.ctx, access?.bypass)
1814
+ });
1815
+ }
1816
+ }
1817
+ };
1818
+ }
1819
+
1820
+ export {
1821
+ ADMIN_FIELDS,
1822
+ textFieldToInputSchema,
1823
+ numberFieldToInputSchema,
1824
+ checkboxFieldToInputSchema,
1825
+ dateFieldToInputSchema,
1826
+ selectFieldToInputSchema,
1827
+ urlFieldToInputSchema,
1828
+ COLOR_FORMATS,
1829
+ COLOR_FORMAT_PATTERNS,
1830
+ CSS_VAR_REFERENCE,
1831
+ colorFieldToInputSchema,
1832
+ relationshipFieldToInputSchema,
1833
+ groupFieldToInputSchema,
1834
+ blocksFieldToInputSchema,
1835
+ uploadFieldToInputSchema,
1836
+ adminFieldToInputSchema,
1837
+ arrayFieldToInputSchema,
1838
+ getGlobalDefaultValues,
1839
+ getGlobalInputSchema,
1840
+ dataType,
1841
+ VexAccessError,
1842
+ VexAccessConfigError,
1843
+ createFilterConstraintBuilder,
1844
+ createIndexConstraintBuilder,
1845
+ readIndexConstraints,
1846
+ createAccessQueryBuilder,
1847
+ readAccessCondition,
1848
+ CRUD_ACTIONS,
1849
+ DRAFT_ACTIONS,
1850
+ QUERY_ACTIONS,
1851
+ PERMISSION_MODES,
1852
+ PERMISSION_SCOPES,
1853
+ WILDCARD_KEY,
1854
+ ADMIN_CUSTOM_SUBJECTS,
1855
+ hasPermission,
1856
+ resolveActionCheck,
1857
+ resolveAccessIndex,
1858
+ resolveAccessConstraint,
1859
+ pickQueryIndex,
1860
+ resolveAccessCall,
1861
+ resolveCollectionSlug,
1862
+ buildDepthPopulate,
1863
+ find,
1864
+ get,
1865
+ search,
1866
+ create,
1867
+ update,
1868
+ remove,
1869
+ getGlobal,
1870
+ findGlobals,
1871
+ upsertGlobal,
1872
+ collectionsApi,
1873
+ globalsApi,
1874
+ resolveGetAuth,
1875
+ vexServerApi
1876
+ };
1877
+ //# sourceMappingURL=chunk-2DU7JDEX.js.map