@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,738 @@
1
+ import { CollectionConfig } from "../types";
2
+ import { GlobalConfig } from "../globals";
3
+ import type { AuthOrgDocument, AuthUserDocument, DocumentBySlug, GlobalDocumentBySlug, CollectionSlug, IndexFieldsBySlug, IndexNameFor } from "../types/generated";
4
+ import { ADMIN_CUSTOM_SUBJECTS, WILDCARD_KEY, type PermissionMode, type CrudAction, type DraftAction, type AdminCustomSubjectSlug, QueryAction } from "./constants";
5
+ import { ConvexError } from "convex/values";
6
+ import type { Expression, FilterBuilder, IndexRange, IndexRangeBuilder } from "convex/server";
7
+ import type { AccessConditionResult, DeclaredDoc, AccessPredicateBuilder, AccessQueryBuilder } from "./constraintTypes";
8
+ /**
9
+ * Any config that may contribute a resource subject: a collection or a global.
10
+ * Structural — the slug literal (and `versions.drafts`, when present) is all
11
+ * the type system reads from it.
12
+ */
13
+ export type AccessResource = CollectionConfig | GlobalConfig;
14
+ /**
15
+ * Single permission check result — boolean shorthand (all/none) or a
16
+ * field-mode object restricting the check to named fields.
17
+ *
18
+ */
19
+ /**
20
+ * Props passed to a permission callback.
21
+ *
22
+ * The `data` key exists only for data-carrying subjects; the `organization`
23
+ * key exists only when `orgCollectionSlug` is configured. Built with
24
+ * intersections (not conditional property types) so the keys are truly
25
+ * absent — not present-but-`never` — when unavailable.
26
+ *
27
+ * @typeParam TData - Document type for the subject; `never` when the subject has no data.
28
+ * @typeParam TUser - User document shape (registry lookup on the user collection slug).
29
+ * @typeParam TOrg - Organization document shape; `never` when not configured.
30
+ */
31
+ export type PermissionCallbackProps<TData = unknown, TUser = Record<string, unknown>, TOrg = Record<string, unknown>> = {
32
+ user: TUser;
33
+ } & ([TData] extends [never] ? unknown : {
34
+ data: TData;
35
+ }) & ([TOrg] extends [never] ? unknown : {
36
+ organization: TOrg;
37
+ });
38
+ /**
39
+ * The plain permission check shapes shared by every action: a static boolean or a
40
+ * callback. The object form is {@link ConstrainedPermissionCheck}; this is the leaf a
41
+ * rule's optional `filter` property also accepts.
42
+ *
43
+ * A callback returning `undefined` is treated as deny — "inconclusive" must never
44
+ * read as an implicit allow. @internal
45
+ */
46
+ type BasePermissionCheck<TData, TUser, TOrg> = boolean | ((props: PermissionCallbackProps<TData, TUser, TOrg>) => boolean | undefined);
47
+ /** A range callback as applied to a Convex query. @internal */
48
+ export type IndexRangeFn = (q: IndexRangeBuilder<any, any, 0>) => IndexRange;
49
+ /**
50
+ * A compiled access filter as applied to a Convex query — the output of
51
+ * `resolveAccessConstraint`.
52
+ *
53
+ * A thunk rather than an `Expression<boolean>` because a filter expression can only
54
+ * be built from the query's OWN `FilterBuilder`, which the resolver never sees: the
55
+ * caller supplies it inside `.filter((q) => …)`. Mirrors {@link IndexRangeFn}, which
56
+ * is deferred for the same reason and uses the same `any` table-info convention.
57
+ *
58
+ * @internal
59
+ */
60
+ export type AccessFilterFn = (q: FilterBuilder<any>) => Expression<boolean>;
61
+ /**
62
+ * A concrete index a query will use: the output of `resolveAccessIndex` and,
63
+ * after arbitration, of `pickQueryIndex`. Both stages produce the same shape,
64
+ * so there is one type for both.
65
+ *
66
+ * Distinct from what the user *authors*: a rule declares its index inside
67
+ * `q.withIndex(name, (ix) => …)`, whose range is a function **of the caller**.
68
+ * Resolving one binds the caller in, leaving the plain
69
+ * `(q) => …` builder here and widening `name` to `string` — by resolve time
70
+ * the resource generic is gone. `AccessIndex` is the template; this is that
71
+ * template applied to one caller.
72
+ *
73
+ * `range` is optional because after arbitration the winner may be a *caller's*
74
+ * `withIndex`, and a caller may legitimately name an index with no range purely
75
+ * to order results. `buildQuery` already branches on this
76
+ * (`api/find/server.ts` — `range ? withIndex(name, range) : withIndex(name)`),
77
+ * so an absent range needs no placeholder.
78
+ *
79
+ * An access-sourced index always carries one, guaranteed by
80
+ * {@link AccessQueryBuilder.withIndex} requiring its range callback rather than
81
+ * restated as a second type here — a range-less access index would scan the whole
82
+ * table, since every document has an index entry (fields that are missing are
83
+ * indexed as `undefined`).
84
+ */
85
+ export interface QueryIndex {
86
+ /** Index name to query. */
87
+ name: string;
88
+ /** Range to apply; omitted for an ordering-only caller index. */
89
+ range?: IndexRangeFn;
90
+ }
91
+ /**
92
+ * Props for a rule's `constraints` callback: the caller, optionally the
93
+ * organization, and `q` — the builder the rule records onto. No `data`:
94
+ * constraints run once per query, before any document is read (contrast
95
+ * {@link PermissionCallbackProps}, which the sibling `filter` property uses).
96
+ *
97
+ * `q`'s TYPE is what gates index pushdown per action (DD 14). A query-shaped
98
+ * action gets an {@link AccessQueryBuilder} — `filter` plus `withIndex`; every
99
+ * other action gets an {@link AccessPredicateBuilder}, the same shape minus
100
+ * `withIndex`, so a rule reads identically either way and `q.withIndex` is simply
101
+ * absent where there is no query to narrow. One property, two builder types, and
102
+ * nothing to discriminate at the object level.
103
+ *
104
+ * @typeParam TData - Document type constraints are typed against.
105
+ * @typeParam TUser - User document shape.
106
+ * @typeParam TOrg - Organization document shape; `never` if not configured.
107
+ * @typeParam TQ - The builder for this action: `AccessQueryBuilder` on query
108
+ * actions, `AccessPredicateBuilder` on mutations.
109
+ */
110
+ export type ConstraintsCallbackProps<TData = unknown, TUser = Record<string, unknown>, TOrg = Record<string, unknown>, TQ = AccessPredicateBuilder<TData>> = {
111
+ user: TUser;
112
+ q: TQ;
113
+ } & ([TOrg] extends [never] ? unknown : {
114
+ organization: TOrg;
115
+ });
116
+ /**
117
+ * A rule's constraint-recording callback.
118
+ *
119
+ * ONE signature returning `boolean | ConstraintResult`, never a union of two
120
+ * function types: verified against `tsc` that a union of differently-shaped
121
+ * callbacks breaks contextual typing of the destructured `props` — every
122
+ * parameter infers `any` instead of TypeScript picking a member. `boolean` is
123
+ * primitive and `ConstraintResult` is nominal, so discriminating the RESULT at
124
+ * runtime (`typeof result === "boolean"`) stays unambiguous.
125
+ *
126
+ * @typeParam TData - Document type constraints are typed against.
127
+ * @typeParam TUser - User document shape.
128
+ * @typeParam TOrg - Organization document shape; `never` if not configured.
129
+ * @typeParam TQ - The builder for this action.
130
+ */
131
+ export type ConstraintsCallback<TData = unknown, TUser = Record<string, unknown>, TOrg = Record<string, unknown>, TQ = AccessPredicateBuilder<TData>> = (props: ConstraintsCallbackProps<TData, TUser, TOrg, TQ>) => boolean | AccessConditionResult;
132
+ /**
133
+ * The constraint-builder object form of a permission check — ONE shape for every
134
+ * action.
135
+ *
136
+ * `constraints` narrows what gets read: compiled to a `withIndex` range when the
137
+ * rule called `q.withIndex(…)`, otherwise to a `.filter()` expression, and in
138
+ * either case interpreted per-document as a JS predicate
139
+ * (`compileConstraints`). `filter` is an OPTIONAL additional per-document check
140
+ * for what constraints cannot express — array membership, string operations,
141
+ * cross-table reads, all outside `FilterBuilder`'s surface, so they stay
142
+ * callbacks permanently.
143
+ *
144
+ * `filter` augments `constraints`; it never replaces it. A rule that declares
145
+ * only `constraints` is already checked per-document too, via
146
+ * `constraintsToPredicate` in `hasPermission`. A bare `filter`-only shape is
147
+ * therefore rejected — a callback with no descriptor cannot narrow a query, so
148
+ * write it as a bare callback instead and accept the full scan knowingly.
149
+ *
150
+ * Index pushdown is opted into INSIDE the callback
151
+ * ({@link AccessQueryBuilder.withIndex}), not by a sibling property. That is
152
+ * what lets `q` bind to one index's real field tuple and check field ORDER at
153
+ * compile time; a sibling property cannot be seen by its neighbour's callback
154
+ * type.
155
+ *
156
+ * @typeParam TData - Document type for the subject.
157
+ * @typeParam TUser - User document shape.
158
+ * @typeParam TOrg - Organization document shape; `never` if not configured.
159
+ * @typeParam TQ - The builder for this action.
160
+ */
161
+ export interface ConstrainedPermissionCheck<TData = unknown, TUser = Record<string, unknown>, TOrg = Record<string, unknown>, TQ = AccessPredicateBuilder<TData>> {
162
+ /** Narrows what gets read. Required — see the type doc. */
163
+ constraints: ConstraintsCallback<TData, TUser, TOrg, TQ>;
164
+ /** Optional per-document check augmenting `constraints`. Never a substitute. */
165
+ filter?: BasePermissionCheck<TData, TUser, TOrg>;
166
+ }
167
+ /**
168
+ * Every check shape valid on **any** action, query-shaped or not: the plain leaf
169
+ * shapes plus the constraint-builder object form (DD 14).
170
+ *
171
+ * This is the tier `RolePermissions` hands a non-query action. The only difference
172
+ * from {@link PermissionCheck} is `q`'s type: a query action's `q` has `withIndex`,
173
+ * this one's does not.
174
+ *
175
+ * **Why `ConstrainedPermissionCheck` sits here rather than inside
176
+ * {@link BasePermissionCheck}.** Both are now valid on every action, so folding
177
+ * them looks tempting. It cannot be done: `BasePermissionCheck` is also the type of
178
+ * the `filter` property *inside* both object forms, so folding the composite into
179
+ * it makes `filter` able to hold another whole constrained check —
180
+ * `{ constraints, filter: { constraints, filter: … } }` — an infinite regress the
181
+ * compiler accepts and `hasPermission`'s resolver has no meaning for. `filter` is
182
+ * the per-document escape hatch for what constraints cannot express; a constraints
183
+ * object is not a leaf check. "Base" stays the irreducible shapes — a value or a
184
+ * function — and the composites point at it, never the other way round.
185
+ *
186
+ * @typeParam TData - Document type for the subject.
187
+ * @typeParam TUser - User document shape.
188
+ * @typeParam TOrg - Organization document shape; `never` if not configured.
189
+ */
190
+ export type AnyActionPermissionCheck<TData = unknown, TUser = Record<string, unknown>, TOrg = Record<string, unknown>> = BasePermissionCheck<TData, TUser, TOrg> | ConstrainedPermissionCheck<TData, TUser, TOrg, AccessPredicateBuilder<TData>>;
191
+ /**
192
+ * A single permission check on a **query-shaped** action: the plain leaf shapes,
193
+ * and the constraint form with `q` upgraded to an {@link AccessQueryBuilder} so
194
+ * `q.withIndex(…)` is available.
195
+ *
196
+ * The ONLY difference from {@link AnyActionPermissionCheck} is `q`'s type. Index
197
+ * pushdown is gated by giving a query action a builder
198
+ * that HAS `withIndex` and a mutation one that does not — so writing
199
+ * `q.withIndex(…)` on a create is a missing-method error at the exact call, rather
200
+ * than a whole-object shape rejection pointing at the wrong line (DD 14).
201
+ *
202
+ * A callback returning `undefined` is treated as deny.
203
+ *
204
+ * @typeParam TData - Document type for the subject.
205
+ * @typeParam TUser - User document shape.
206
+ * @typeParam TOrg - Organization document shape; `never` if not configured.
207
+ * @typeParam TIndexFields - The resource's index name → field tuple map, which
208
+ * `q.withIndex` resolves against.
209
+ */
210
+ export type PermissionCheck<TData = unknown, TUser = Record<string, unknown>, TOrg = Record<string, unknown>, TIndexFields extends Record<string, readonly string[]> = Record<string, readonly string[]>> = BasePermissionCheck<TData, TUser, TOrg> | ConstrainedPermissionCheck<TData, TUser, TOrg, AccessQueryBuilder<TData, TIndexFields>>;
211
+ /**
212
+ * One entry in the subject registry: the action union, the data shape passed
213
+ * to callbacks, and the index registry `q.withIndex` resolves against.
214
+ */
215
+ export interface SubjectEntry {
216
+ /** Union of actions this subject supports. */
217
+ action: string;
218
+ /** Actions on this subject whose `q` carries `withIndex`. `never` when none do. */
219
+ queryAction: string;
220
+ /** Document/context type; `never` for subjects without data. */
221
+ data: unknown;
222
+ /** Union of access-index names declared on this resource; `never` for non-indexable subjects. */
223
+ indexes: string;
224
+ /**
225
+ * The resource's declared indexes as name → field tuple, in declaration order.
226
+ * `{}` for subjects with no table to index (custom resources, admin subjects),
227
+ * which makes `q.withIndex` uncallable there rather than absent.
228
+ *
229
+ * Carries TUPLES, not just the names in `indexes`, because
230
+ * {@link AccessQueryBuilder.withIndex} binds `q` to one index's real field order
231
+ * — that is what makes positional constraint typing reachable.
232
+ */
233
+ indexFields: Record<string, readonly string[]>;
234
+ }
235
+ /** Extract the slug literal from a resource config. @internal */
236
+ type ExtractSlug<T> = T extends {
237
+ slug: infer S extends string;
238
+ } ? S : never;
239
+ /**
240
+ * Document type for a slug via the generated registry (collections, then
241
+ * globals; wide fallback pre-generation). @internal
242
+ */
243
+ type InferDocTypeFromSlug<S extends string> = S extends keyof DocumentBySlug ? DocumentBySlug[S] : S extends keyof GlobalDocumentBySlug ? GlobalDocumentBySlug[S] : Record<string, unknown>;
244
+ /**
245
+ * Document type for a resource config via its slug literal. @internal
246
+ */
247
+ type InferDocType<T> = T extends {
248
+ slug: infer S extends string;
249
+ } ? InferDocTypeFromSlug<S> : Record<string, unknown>;
250
+ /**
251
+ * The document type a permission callback receives for resource `S`.
252
+ *
253
+ * Exported because a project writing its own composable access checks has to name
254
+ * this type. Accepts any slug string: {@link InferDocTypeFromSlug} resolves
255
+ * collections, then globals, then falls back wide, so a global slug works here too.
256
+ *
257
+ * @typeParam S - Resource slug.
258
+ */
259
+ export type AccessDocFor<S extends string> = DeclaredDoc<InferDocTypeFromSlug<S>>;
260
+ /**
261
+ * Resource `S`'s index name → field tuple map, which `q.withIndex` resolves against.
262
+ *
263
+ * @typeParam S - Resource slug.
264
+ */
265
+ export type AccessIndexFieldsFor<S extends string> = S extends keyof IndexFieldsBySlug ? IndexFieldsBySlug[S] : Record<string, readonly string[]>;
266
+ /**
267
+ * The exact check type `permissions[role][S][queryAction]` accepts.
268
+ *
269
+ * This is the return type for a project-defined helper on a query-shaped action
270
+ * (`read`, `readDrafts`) — the helper hands back a check, so nothing wraps it and the
271
+ * call site reads identically to writing the check inline.
272
+ *
273
+ * @typeParam S - Resource slug.
274
+ * @typeParam TUser - The project's user document type.
275
+ * @typeParam TOrg - The project's organization document type, or `never`.
276
+ */
277
+ /**
278
+ * The field union a helper can use to READ a value off `AccessDocFor<S>`.
279
+ *
280
+ * A bare `keyof`, deliberately: a parameter bounded by it is *provably* a key of the
281
+ * document, so `data[field]` needs no cast. Wrapping it in a widening conditional —
282
+ * "fall back to `string` when the registry is unaugmented" — breaks exactly that,
283
+ * because TypeScript cannot prove an unreduced conditional is a key, and the read
284
+ * fails with "Type 'F' cannot be used to index type".
285
+ *
286
+ * The cost of staying indexable: for a slug the registry does not know, the document
287
+ * is the wide fallback whose index signature `DeclaredDoc` strips, leaving this
288
+ * `never`. A field-reading helper therefore only types after `vex generate` — already
289
+ * true of the rest of authoring an access config, since slugs and index names come
290
+ * from the same registry.
291
+ *
292
+ * @typeParam S - Resource slug.
293
+ */
294
+ export type AccessDocFieldFor<S extends string> = keyof AccessDocFor<S> & string;
295
+ /**
296
+ * Fields on `S` that LEAD a declared index.
297
+ *
298
+ * Only a leading field can open an index range, so a helper that pushes a comparison
299
+ * into an index must restrict its field argument to these — otherwise it silently
300
+ * degrades to a full scan.
301
+ *
302
+ * @typeParam S - Resource slug.
303
+ */
304
+ export type AccessIndexedFieldFor<S extends string> = {
305
+ [N in keyof AccessIndexFieldsFor<S>]: AccessIndexFieldsFor<S>[N] extends readonly [
306
+ infer F,
307
+ ...unknown[]
308
+ ] ? F : never;
309
+ }[keyof AccessIndexFieldsFor<S>] & string;
310
+ /**
311
+ * The index on `S` whose leading field is `F`.
312
+ *
313
+ * Lets a helper's runtime index lookup be typed as the index name it resolves to,
314
+ * rather than a bare `string` that `q.withIndex` refuses.
315
+ *
316
+ * @typeParam S - Resource slug.
317
+ * @typeParam F - A field that leads an index on `S`.
318
+ */
319
+ export type AccessIndexNameFor<S extends string, F> = {
320
+ [N in keyof AccessIndexFieldsFor<S>]: AccessIndexFieldsFor<S>[N] extends readonly [
321
+ F,
322
+ ...unknown[]
323
+ ] ? N : never;
324
+ }[keyof AccessIndexFieldsFor<S>] & string;
325
+ /**
326
+ * The stored value type of field `F` on `S`.
327
+ *
328
+ * @typeParam S - Resource slug.
329
+ * @typeParam F - Field name.
330
+ */
331
+ export type AccessFieldValueFor<S extends string, F> = F extends keyof AccessDocFor<S> ? AccessDocFor<S>[F] : never;
332
+ /**
333
+ * Indexed fields on `S` whose stored value admits `V`.
334
+ *
335
+ * Narrows a value-specific helper — a "published only" read, say — to the fields that
336
+ * can actually hold that value, so naming the wrong field is a compile error rather
337
+ * than a comparison that silently matches nothing. Handles both a scalar field and an
338
+ * array-valued one (`select` and relationship fields store arrays).
339
+ *
340
+ * @typeParam S - Resource slug.
341
+ * @typeParam V - The value the field must be able to hold.
342
+ */
343
+ export type AccessIndexedFieldWithValue<S extends string, V> = {
344
+ [F in AccessIndexedFieldFor<S>]: NonNullable<AccessFieldValueFor<S, F>> extends readonly (infer E)[] ? [V] extends [E] ? F : never : [V] extends [NonNullable<AccessFieldValueFor<S, F>>] ? F : never;
345
+ }[AccessIndexedFieldFor<S>];
346
+ /**
347
+ * What a check builder needs from a resource config: the slug, to bind `S`, and the
348
+ * fields, to recover an index NAME from a field name at runtime.
349
+ *
350
+ * Structural rather than `CollectionConfig` so a `GlobalConfig` satisfies it too.
351
+ *
352
+ * @typeParam S - Resource slug.
353
+ */
354
+ export type AccessResourceRef<S extends string> = {
355
+ readonly fields: Readonly<Record<string, {
356
+ readonly index?: string;
357
+ readonly type?: string;
358
+ }>>;
359
+ readonly slug: S;
360
+ };
361
+ /**
362
+ * The check type `permissions[role][S][queryAction]` accepts, with the project's user
363
+ * and organization documents resolved from the generated registry.
364
+ *
365
+ * `vex generate` emits the slugs `defineAccess` was configured with
366
+ * ({@link AuthSlugs}), so the project's user and organization documents resolve from
367
+ * the registry — nothing needs passing in, and a project's helpers need no local
368
+ * type aliases at all. `AccessMutationCheck` is the non-query counterpart: its `q`
369
+ * is an {@link AccessPredicateBuilder}, no `withIndex`, because a single-document
370
+ * authorization has no range to narrow (DD 14).
371
+ *
372
+ * @typeParam S - Resource slug.
373
+ */
374
+ export type AccessCheck<S extends string> = PermissionCheck<AccessDocFor<S>, AuthUserDocument, AuthOrgDocument, AccessIndexFieldsFor<S>>;
375
+ /**
376
+ * The check type a NON-query action (`create`/`update`/`delete`, custom actions)
377
+ * accepts, with user and organization resolved from the registry.
378
+ *
379
+ * @typeParam S - Resource slug.
380
+ */
381
+ export type AccessMutationCheck<S extends string> = AnyActionPermissionCheck<AccessDocFor<S>, AuthUserDocument, AuthOrgDocument>;
382
+ /**
383
+ * Index-name union for a resource config via its slug literal, from the
384
+ * generated {@link IndexNameFor} registry (wide `string` fallback
385
+ * pre-generation). @internal
386
+ */
387
+ type ExtractIndexNames<T> = T extends {
388
+ slug: infer S extends string;
389
+ } ? IndexNameFor<S> : string;
390
+ /**
391
+ * The declared-index field-tuple map for a resource config's slug — the shape
392
+ * {@link AccessQueryBuilder.withIndex} resolves against. `{}` pre-generation, so
393
+ * `withIndex` accepts nothing rather than accepting anything. @internal
394
+ */
395
+ type ExtractIndexFields<T> = T extends {
396
+ slug: infer S extends string;
397
+ } ? S extends keyof IndexFieldsBySlug ? IndexFieldsBySlug[S] : PreGenerationIndexFields : PreGenerationIndexFields;
398
+ /**
399
+ * Pre-generation fallback for {@link ExtractIndexFields}: any index name, and a
400
+ * FIXED-LENGTH tuple of eight `string` slots per index.
401
+ *
402
+ * The length matters more than it looks. `ConstraintBuilder` terminates its chain
403
+ * on `PlusOne<N> extends TFields["length"]`, and a plain `readonly string[]` has
404
+ * `length: number` — which that test treats as already satisfied, so the chain would
405
+ * end after ONE constraint. A fixed-length tuple keeps `length` a numeric literal,
406
+ * so chaining behaves the same before and after `vex generate`; only field NAMES and
407
+ * per-field value types widen. Eight comfortably exceeds any realistic compound
408
+ * index. @internal
409
+ */
410
+ type PreGenerationIndexFields = Record<string, readonly [string, string, string, string, string, string, string, string]>;
411
+ /** True when a resource config declares `versions.drafts: true`. @internal */
412
+ type HasDrafts<T> = T extends {
413
+ versions?: {
414
+ drafts?: infer D extends boolean;
415
+ };
416
+ } ? D extends true ? true : false : false;
417
+ /**
418
+ * Subject entry synthesized for the user/organization collections.
419
+ *
420
+ * Resolved from the slug via the generated registry, so these subjects exist with
421
+ * full document and index typing WITHOUT the collection being passed in
422
+ * `resources` — the adapter owns those tables and merges them later, inside
423
+ * `defineConfig`. CRUD-only: auth tables are never versioned. @internal
424
+ */
425
+ type AuthSubjectEntry<S extends string, TCustomActions> = {
426
+ action: CrudAction | CustomActionsFor<S, TCustomActions>["query"] | CustomActionsFor<S, TCustomActions>["mutation"];
427
+ queryAction: QueryAction | CustomActionsFor<S, TCustomActions>["query"];
428
+ data: DeclaredDoc<InferDocTypeFromSlug<S>>;
429
+ indexes: IndexNameFor<S>;
430
+ indexFields: AccessIndexFieldsFor<S>;
431
+ };
432
+ /**
433
+ * The user subject, plus the organization subject when one is configured.
434
+ *
435
+ * The `string extends S` guards are load-bearing: for a non-literal slug the mapped
436
+ * type would become an INDEX SIGNATURE, making every subject key valid in
437
+ * `RolePermissions` and silently destroying typo-checking across the whole matrix.
438
+ * @internal
439
+ */
440
+ type AuthSubjects<TUserSlug extends CollectionSlug, TOrgSlug extends CollectionSlug | undefined, TCustomActions> = (string extends TUserSlug ? unknown : {
441
+ [K in TUserSlug]: AuthSubjectEntry<K, TCustomActions>;
442
+ }) & (TOrgSlug extends string ? string extends TOrgSlug ? unknown : {
443
+ [K in TOrgSlug]: AuthSubjectEntry<K, TCustomActions>;
444
+ } : unknown);
445
+ /**
446
+ * The complete subject registry: resources (keyed by slug, CRUD + conditional
447
+ * draft actions), custom resources, and the core built-in subjects from
448
+ * {@link ADMIN_CUSTOM_SUBJECTS}.
449
+ *
450
+ * @typeParam TResources - Structural resource tuple (`{ slug, versions? }`).
451
+ * @typeParam TCustomResources - Custom resource declarations.
452
+ * @typeParam TUserSlug - User collection slug
453
+ * @typeParam TOrgSlug - Organization collection slug
454
+ * @typeParam TCustomActions extends Record<string, CustomActionsInput> = {}, // NEW
455
+ */
456
+ export type SubjectMap<TResources extends readonly AccessResource[] = AccessResource[], TCustomResources extends Record<string, CustomResourceInput> = Record<string, CustomResourceInput>, TUserSlug extends CollectionSlug = CollectionSlug, TOrgSlug extends CollectionSlug | undefined = undefined, TCustomActions extends Partial<Record<TResources[number]["slug"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>> = {}> = {
457
+ [R in TResources[number] as ExtractSlug<R>]: {
458
+ action: CrudAction | (HasDrafts<R> extends true ? DraftAction : never) | CustomActionsFor<ExtractSlug<R>, TCustomActions>["query"] | CustomActionsFor<ExtractSlug<R>, TCustomActions>["mutation"];
459
+ queryAction: QueryAction | CustomActionsFor<ExtractSlug<R>, TCustomActions>["query"];
460
+ data: DeclaredDoc<InferDocType<R>>;
461
+ indexes: ExtractIndexNames<R>;
462
+ indexFields: ExtractIndexFields<R>;
463
+ };
464
+ } & {
465
+ [K in keyof TCustomResources]: {
466
+ action: TCustomResources[K]["actions"][number];
467
+ queryAction: never;
468
+ data: TCustomResources[K]["data"] extends DataTypeCarrier<infer D> ? D : never;
469
+ indexes: never;
470
+ indexFields: {};
471
+ };
472
+ } & {
473
+ [K in AdminCustomSubjectSlug]: {
474
+ action: (typeof ADMIN_CUSTOM_SUBJECTS)[K]["actions"][number];
475
+ queryAction: never;
476
+ data: never;
477
+ indexes: never;
478
+ indexFields: {};
479
+ };
480
+ } & Omit<AuthSubjects<TUserSlug, TOrgSlug, TCustomActions>, ExtractSlug<TResources[number]> | keyof TCustomResources | AdminCustomSubjectSlug>;
481
+ /**
482
+ * Phantom carrier for a custom resource's `data` type. Created by
483
+ * {@link dataType}; never inspected at runtime.
484
+ */
485
+ export interface DataTypeCarrier<T = never> {
486
+ readonly __phantom?: T;
487
+ }
488
+ /**
489
+ * Declares the data type callbacks (and `hasPermission` callers) receive for a
490
+ * custom resource.
491
+ *
492
+ * @example
493
+ * ```ts
494
+ * customResources: {
495
+ * reviews: { actions: ["approve", "reject"], data: dataType<{ queue: string }>() },
496
+ * }
497
+ * ```
498
+ * @returns a plain object '{}'
499
+ */
500
+ export declare function dataType<T>(): DataTypeCarrier<T>;
501
+ /** Custom actions for one resource, split by whether `q` gets `withIndex`. */
502
+ export type CustomActionsInput = {
503
+ query?: readonly string[];
504
+ mutation?: readonly string[];
505
+ };
506
+ /** The custom query/mutation action unions declared for slug `S`. @internal */
507
+ type CustomActionsFor<S extends string, TCA> = S extends keyof TCA ? {
508
+ query: TCA[S] extends {
509
+ query: readonly (infer Q extends string)[];
510
+ } ? Q : never;
511
+ mutation: TCA[S] extends {
512
+ mutation: readonly (infer M extends string)[];
513
+ } ? M : never;
514
+ } : {
515
+ query: never;
516
+ mutation: never;
517
+ };
518
+ /**
519
+ * A custom (non-collection) subject declaration: its action list and an
520
+ * optional typed data carrier. One canonical form — no array shorthand.
521
+ */
522
+ export type CustomResourceInput = {
523
+ actions: readonly string[];
524
+ data?: DataTypeCarrier<unknown>;
525
+ };
526
+ /**
527
+ * Per-role permission matrix, typed against the resolved {@link SubjectMap}.
528
+ *
529
+ * Each subject key accepts `boolean` (all actions) or a per-action map whose
530
+ * keys are that subject's action union plus the action-level wildcard
531
+ * ({@link WILDCARD_KEY}).
532
+ *
533
+ * **Only `withIndex` is gated on {@link QueryAction}** (DD 14). A query-shaped
534
+ * action gets the full {@link PermissionCheck}, whose `q` carries `withIndex` —
535
+ * there is a query to narrow.
536
+ * Every other action gets the plain shapes plus
537
+ * {@link ConstrainedPermissionCheck}: a create/update/delete has no query to
538
+ * narrow, but its constraints are still meaningful, interpreted per-document via
539
+ * `constraintsToPredicate`. Restricting the object form to query actions is what
540
+ * used to force a read+update pair to express one predicate twice — once as a
541
+ * constraint, once as a hand-written callback — which is the dual-expression
542
+ * footgun this design removes.
543
+ *
544
+ * The action-level wildcard stays plain: it spans actions of mixed shape, so a
545
+ * constraint written there could not be typed against one document consistently.
546
+ * The role-level wildcard is boolean-only.
547
+ * Precedence: explicit action > subject wildcard > role wildcard > `defaults`.
548
+ *
549
+ * @typeParam TSubjects - The resolved {@link SubjectMap}.
550
+ * @typeParam TUser - User document shape.
551
+ * @typeParam TOrg - Organization document shape, or `never`.
552
+ */
553
+ export type RolePermissions<TSubjects extends Record<string, SubjectEntry>, TUser = Record<string, unknown>, TOrg = never, TUserSlug extends CollectionSlug = CollectionSlug, TOrgSlug extends CollectionSlug | undefined = never> = {
554
+ [S in keyof TSubjects | TUserSlug | (TOrgSlug extends string ? TOrgSlug : never)]?: boolean | ({
555
+ [A in TSubjects[S]["action"]]?: A extends QueryAction ? PermissionCheck<TSubjects[S]["data"], TUser, TOrg, TSubjects[S]["indexFields"]> : A extends Exclude<CrudAction | DraftAction, QueryAction> | typeof WILDCARD_KEY ? AnyActionPermissionCheck<TSubjects[S]["data"], TUser, TOrg> : A extends TSubjects[S]["queryAction"] ? PermissionCheck<TSubjects[S]["data"], TUser, TOrg, TSubjects[S]["indexFields"]> : AnyActionPermissionCheck<TSubjects[S]["data"], TUser, TOrg>;
556
+ } & {
557
+ [W in typeof WILDCARD_KEY]?: AnyActionPermissionCheck<TSubjects[S]["data"], TUser, TOrg>;
558
+ });
559
+ } & {
560
+ [W in typeof WILDCARD_KEY]?: boolean;
561
+ };
562
+ /**
563
+ * Input shape for the `defineAccess` builder.
564
+ *
565
+ * @typeParam TRoles - Tuple of role name literals.
566
+ * @typeParam TResources - Structural resource tuple (`{ slug, versions? }`).
567
+ * @typeParam TCustomResources - Custom resource declarations.
568
+ * @typeParam TUserCollection - `{ slug }` shape naming the user collection.
569
+ * @typeParam TOrgCollection - `{ slug }` shape naming the org collection; `undefined` if absent.
570
+ *
571
+ * @see {@link VexAccessConfig} for the resolved runtime shape.
572
+ */
573
+ export interface VexAccessConfigInput<TRoles extends readonly string[], TResources extends readonly AccessResource[] = readonly AccessResource[], TCustomResources extends Record<string, CustomResourceInput> = {}, TUserSlug extends CollectionSlug = CollectionSlug, TOrgSlug extends CollectionSlug | undefined = undefined, TCustomActions extends Partial<Record<TResources[number]["slug"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>> = {}> {
574
+ /** Default: `true`. Turn access control on or off. */
575
+ enabled?: boolean;
576
+ /**
577
+ * OPTIONAL. Role applied when a caller's roles resolve empty — no session,
578
+ * or an anonymous user (e.g. Better Auth anonymous plugin) whose
579
+ * `userRolesField` is unset. Explicit roles always win over this fallback.
580
+ * Omitted → empty roles deny, exactly as before.
581
+ */
582
+ anonRole?: TRoles[number];
583
+ /** Role identifiers; keys of the `permissions` matrix. */
584
+ roles: TRoles;
585
+ /** Collections/globals contributing subjects, keyed by slug. */
586
+ resources: TResources;
587
+ /**
588
+ * Extra actions per resource, beyond CRUD. Keys must be declared resource slugs
589
+ * (or the user/org collection slugs).
590
+ *
591
+ * Three constituents, each load-bearing:
592
+ *
593
+ * 1. `TCustomActions` — the inference site; carries the caller's literal declaration
594
+ * into the phantom `SubjectMap`.
595
+ * 2. `Partial<Record<slugs, …>>` — the COMPLETION source. Key completions come from
596
+ * the property's contextual type, and a bare unresolved type parameter offers
597
+ * nothing; this constituent names the slug union directly, which is resolvable in
598
+ * the first inference round (`resources` and the slugs are not context-sensitive).
599
+ * Redundant for checking — the bound already constrains values.
600
+ * 3. The exactness map — the TYPO guard. The `Partial<Record<…>>` bound cannot reject
601
+ * a bad key on its own: an all-optional target is a "weak type", so TypeScript
602
+ * errors only when the object shares ZERO keys with it — one valid entry beside a
603
+ * garbage key passes, because structural subtyping permits extra properties and
604
+ * per-key excess checking does not fire against a generic-constrained inference.
605
+ * Mapping every key NOT in the slug union to `never` makes the garbage entry's
606
+ * value unassignable AT ITS OWN KEY, restoring both the error and its location.
607
+ */
608
+ customActions?: TCustomActions & Partial<Record<TResources[number]["slug"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>> & {
609
+ [K in Exclude<keyof TCustomActions, TResources[number]["slug"] | TUserSlug | Extract<TOrgSlug, string>>]: never;
610
+ };
611
+ /**
612
+ * Custom, non-resource subjects with arbitrary action unions and optional
613
+ * typed data. Example: `{ apiKeys: { actions: ["create", "revoke"] } }`.
614
+ */
615
+ customResources?: TCustomResources;
616
+ /**
617
+ * Slug of the collection whose documents are `user` in callbacks. A plain
618
+ * slug string — the full collection often does not exist at authoring time
619
+ * (auth-adapter collections merge later, inside `defineConfig`); the
620
+ * document type resolves from the generated registry by slug.
621
+ */
622
+ userCollectionSlug: TUserSlug;
623
+ /**
624
+ * REQUIRED. The field on the user document that holds the user's role(s).
625
+ * Value may be `string` or `string[]`; `hasPermission` normalizes both.
626
+ * Callers never pass roles separately — they always ride the user document.
627
+ */
628
+ userRolesField: string;
629
+ /**
630
+ * Slug of the organization collection. When present, `organization` is
631
+ * available (typed via the registry) in every permission callback; when
632
+ * omitted, callbacks have no `organization` key.
633
+ */
634
+ orgCollectionSlug?: TOrgSlug;
635
+ /**
636
+ * Permission matrix: role → subject → check. See {@link RolePermissions}
637
+ * for shapes and wildcard semantics.
638
+ */
639
+ permissions: Record<TRoles[number], RolePermissions<SubjectMap<TResources, TCustomResources, TUserSlug, TOrgSlug, TCustomActions>, InferDocTypeFromSlug<TUserSlug>, TOrgSlug extends string ? InferDocTypeFromSlug<TOrgSlug> : never, TUserSlug, TOrgSlug>>;
640
+ }
641
+ /**
642
+ * Resolved access configuration returned by `defineAccess` — the runtime
643
+ * shape consumed by `hasPermission`.
644
+ *
645
+ * Deliberately VALUE-LEVEL TYPE-ERASED: every call-site guarantee
646
+ * (`resource`/`action` unions, callback `data` types, field keys) rides the
647
+ * phantom `TSubjects` parameter, while the stored fields are wide. This is
648
+ * what lets any concrete config assign to plain `VexAccessConfig` (e.g. the
649
+ * `access` field on `VexConfig`) — a fully-generic config type would be
650
+ * unassignable to any common supertype, because permission callbacks are
651
+ * contravariant in their `data` parameter.
652
+ *
653
+ * @typeParam TSubjects - Phantom {@link SubjectMap} carried for `hasPermission` inference.
654
+ */
655
+ export interface VexAccessConfig<TSubjects extends Record<string, SubjectEntry> = Record<string, SubjectEntry>, TResources extends readonly AccessResource[] = readonly AccessResource[], TUserSlug extends CollectionSlug = CollectionSlug, TOrgSlug extends CollectionSlug | undefined = CollectionSlug | undefined, TCustomActions extends Partial<Record<TResources[number]["slug"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>> = Partial<Record<TResources[number]["slug"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>>> {
656
+ /** Default: `true`. Turn access control on or off. */
657
+ enabled: boolean;
658
+ /**
659
+ * The allback role when there is no user.
660
+ */
661
+ anonRole?: string;
662
+ /** Role names known to the system. */
663
+ roles: readonly string[];
664
+ /** Collections/globals contributing subjects, keyed by slug. */
665
+ resources: TResources;
666
+ /**
667
+ * Custom actions per subject slug, as declared in `defineAccess`. Carried on the
668
+ * RESOLVED config purely so request-time code can tell a declared verb from a typo:
669
+ * an undeclared action resolves through `defaultPermissionMode` (default `allow`),
670
+ * so without this record a misspelled `access.action` silently widens access.
671
+ */
672
+ customActions?: TCustomActions;
673
+ /**
674
+ * Posture for undeclared role/subject/action combinations. Always
675
+ * {@link PERMISSION_MODES.deny} — `defineAccess` pins it and no input field sets it.
676
+ *
677
+ * Retained as a field rather than inlined because `hasPermission` and
678
+ * `resolveAccessRule` branch on it, and an allow posture is expressible as a
679
+ * role-level `"*": true` if it is ever wanted back — per-role and greppable,
680
+ * which a global default never was.
681
+ *
682
+ * @internal
683
+ */
684
+ defaultPermissionMode: PermissionMode;
685
+ /** Slug of the user collection. */
686
+ userCollectionSlug: TUserSlug;
687
+ /** Field on the user document holding role(s) (`string | string[]`). */
688
+ userRolesField: string;
689
+ /** Slug of the organization collection, when configured. */
690
+ orgCollectionSlug?: TOrgSlug;
691
+ /**
692
+ * The permission matrix as authored (checks may be booleans, field-mode
693
+ * objects, or callbacks). Type-erased for storage; `defineAccess` fully
694
+ * type-checks it at authoring time.
695
+ */
696
+ permissions: Record<string, Record<string, unknown>>;
697
+ /**
698
+ * Phantom field carrying {@link SubjectMap} for inference. Optional and
699
+ * never assigned at runtime.
700
+ */
701
+ readonly __subjects?: TSubjects;
702
+ }
703
+ /**
704
+ * Thrown by `hasPermission` when `throwOnDenied: true` and access is denied.
705
+ * Carries the subject and action.
706
+ */
707
+ export declare class VexAccessError extends ConvexError<{
708
+ code: "ACCESS_DENIED";
709
+ resource: string;
710
+ action: string;
711
+ message: string;
712
+ }> {
713
+ /** The subject on which access was denied. */
714
+ resource: string;
715
+ /** The denied action. */
716
+ action: string;
717
+ /**
718
+ * @param options — Structured denial context.
719
+ * @param options.message — Human-readable error message.
720
+ * @param options.resource — Subject name.
721
+ * @param options.action — Action name.
722
+ */
723
+ constructor(options: {
724
+ message?: string;
725
+ resource: string;
726
+ action: string;
727
+ });
728
+ }
729
+ /**
730
+ * Thrown by `defineAccess` on hard configuration errors (custom resource key
731
+ * colliding with a resource slug; empty `actions` array).
732
+ */
733
+ export declare class VexAccessConfigError extends Error {
734
+ /** @param message — Human-readable description of the configuration error. */
735
+ constructor(message: string);
736
+ }
737
+ export {};
738
+ //# sourceMappingURL=types.d.ts.map