@vexcms/core 0.0.19 → 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.
- package/LICENSE +202 -0
- package/dist/.build +1 -0
- package/dist/access/buildChecks.d.ts +58 -0
- package/dist/access/buildChecks.d.ts.map +1 -0
- package/dist/access/canAccessAdminPanel.d.ts +53 -0
- package/dist/access/canAccessAdminPanel.d.ts.map +1 -0
- package/dist/access/compileConstraints.d.ts +155 -0
- package/dist/access/compileConstraints.d.ts.map +1 -0
- package/dist/access/config.d.ts +62 -0
- package/dist/access/config.d.ts.map +1 -0
- package/dist/access/constants.d.ts +95 -0
- package/dist/access/constants.d.ts.map +1 -0
- package/dist/access/constraintTypes.d.ts +337 -0
- package/dist/access/constraintTypes.d.ts.map +1 -0
- package/dist/access/createAccessQueryBuilder.d.ts +59 -0
- package/dist/access/createAccessQueryBuilder.d.ts.map +1 -0
- package/dist/access/createFilterConstraintBuilder.d.ts +26 -0
- package/dist/access/createFilterConstraintBuilder.d.ts.map +1 -0
- package/dist/access/createIndexConstraintBuilder.d.ts +40 -0
- package/dist/access/createIndexConstraintBuilder.d.ts.map +1 -0
- package/dist/access/hasPermission.d.ts +85 -0
- package/dist/access/hasPermission.d.ts.map +1 -0
- package/dist/access/index.d.ts +13 -0
- package/dist/access/index.d.ts.map +1 -0
- package/dist/access/pickQueryIndex.d.ts +42 -0
- package/dist/access/pickQueryIndex.d.ts.map +1 -0
- package/dist/access/resolveAccessRule.d.ts +85 -0
- package/dist/access/resolveAccessRule.d.ts.map +1 -0
- package/dist/access/types.d.ts +738 -0
- package/dist/access/types.d.ts.map +1 -0
- package/dist/access/validateAccessConstraints.d.ts +57 -0
- package/dist/access/validateAccessConstraints.d.ts.map +1 -0
- package/dist/api/client.d.ts +20 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +117 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/convex.d.ts +296 -0
- package/dist/api/convex.d.ts.map +1 -0
- package/dist/api/create/client.d.ts +50 -0
- package/dist/api/create/client.d.ts.map +1 -0
- package/dist/api/create/server.d.ts +43 -0
- package/dist/api/create/server.d.ts.map +1 -0
- package/dist/api/depth.d.ts +39 -0
- package/dist/api/depth.d.ts.map +1 -0
- package/dist/api/find/client.d.ts +90 -0
- package/dist/api/find/client.d.ts.map +1 -0
- package/dist/api/find/server.d.ts +73 -0
- package/dist/api/find/server.d.ts.map +1 -0
- package/dist/api/get/client.d.ts +56 -0
- package/dist/api/get/client.d.ts.map +1 -0
- package/dist/api/get/server.d.ts +49 -0
- package/dist/api/get/server.d.ts.map +1 -0
- package/dist/api/globals/find.client.d.ts +20 -0
- package/dist/api/globals/find.client.d.ts.map +1 -0
- package/dist/api/globals/find.server.d.ts +29 -0
- package/dist/api/globals/find.server.d.ts.map +1 -0
- package/dist/api/globals/get.client.d.ts +46 -0
- package/dist/api/globals/get.client.d.ts.map +1 -0
- package/dist/api/globals/get.server.d.ts +55 -0
- package/dist/api/globals/get.server.d.ts.map +1 -0
- package/dist/api/globals/types.d.ts +65 -0
- package/dist/api/globals/types.d.ts.map +1 -0
- package/dist/api/globals/upsert.client.d.ts +24 -0
- package/dist/api/globals/upsert.client.d.ts.map +1 -0
- package/dist/api/globals/upsert.server.d.ts +47 -0
- package/dist/api/globals/upsert.server.d.ts.map +1 -0
- package/dist/api/populate.d.ts +23 -0
- package/dist/api/populate.d.ts.map +1 -0
- package/dist/api/remove/client.d.ts +58 -0
- package/dist/api/remove/client.d.ts.map +1 -0
- package/dist/api/remove/server.d.ts +65 -0
- package/dist/api/remove/server.d.ts.map +1 -0
- package/dist/api/search/client.d.ts +58 -0
- package/dist/api/search/client.d.ts.map +1 -0
- package/dist/api/search/server.d.ts +54 -0
- package/dist/api/search/server.d.ts.map +1 -0
- package/dist/api/server.d.ts +370 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +33 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/types.d.ts +525 -0
- package/dist/api/types.d.ts.map +1 -0
- package/dist/api/update/client.d.ts +53 -0
- package/dist/api/update/client.d.ts.map +1 -0
- package/dist/api/update/server.d.ts +47 -0
- package/dist/api/update/server.d.ts.map +1 -0
- package/dist/api/utils.d.ts +57 -0
- package/dist/api/utils.d.ts.map +1 -0
- package/dist/auth/mergeCollections.d.ts +28 -0
- package/dist/auth/mergeCollections.d.ts.map +1 -0
- package/dist/auth/types.d.ts +84 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/chunk-2DU7JDEX.js +1877 -0
- package/dist/chunk-2DU7JDEX.js.map +1 -0
- package/dist/chunk-2IB2DCN7.js +75 -0
- package/dist/chunk-2IB2DCN7.js.map +1 -0
- package/dist/collections/config.d.ts +28 -0
- package/dist/collections/config.d.ts.map +1 -0
- package/dist/collections/constants.d.ts +27 -0
- package/dist/collections/constants.d.ts.map +1 -0
- package/dist/collections/index.d.ts +7 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/collections/indexFields.d.ts +20 -0
- package/dist/collections/indexFields.d.ts.map +1 -0
- package/dist/collections/interfaceGen.d.ts +59 -0
- package/dist/collections/interfaceGen.d.ts.map +1 -0
- package/dist/collections/internal/index.d.ts +2 -0
- package/dist/collections/internal/index.d.ts.map +1 -0
- package/dist/collections/types.d.ts +282 -0
- package/dist/collections/types.d.ts.map +1 -0
- package/dist/collections/utils.d.ts +71 -0
- package/dist/collections/utils.d.ts.map +1 -0
- package/dist/collections/validator.d.ts +73 -0
- package/dist/collections/validator.d.ts.map +1 -0
- package/dist/config/config.d.ts +43 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/index.d.ts +5 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/sanitizeConfig.d.ts +82 -0
- package/dist/config/sanitizeConfig.d.ts.map +1 -0
- package/dist/config/types.d.ts +316 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/fields/array/config.d.ts +57 -0
- package/dist/fields/array/config.d.ts.map +1 -0
- package/dist/fields/array/index.d.ts +5 -0
- package/dist/fields/array/index.d.ts.map +1 -0
- package/dist/fields/array/inputSchema.d.ts +32 -0
- package/dist/fields/array/inputSchema.d.ts.map +1 -0
- package/dist/fields/array/types.d.ts +127 -0
- package/dist/fields/array/types.d.ts.map +1 -0
- package/dist/fields/array/validator.d.ts +30 -0
- package/dist/fields/array/validator.d.ts.map +1 -0
- package/dist/fields/baseTypes.d.ts +251 -0
- package/dist/fields/baseTypes.d.ts.map +1 -0
- package/dist/fields/blocks/config.d.ts +63 -0
- package/dist/fields/blocks/config.d.ts.map +1 -0
- package/dist/fields/blocks/index.d.ts +5 -0
- package/dist/fields/blocks/index.d.ts.map +1 -0
- package/dist/fields/blocks/inputSchema.d.ts +22 -0
- package/dist/fields/blocks/inputSchema.d.ts.map +1 -0
- package/dist/fields/blocks/types.d.ts +253 -0
- package/dist/fields/blocks/types.d.ts.map +1 -0
- package/dist/fields/blocks/validator.d.ts +32 -0
- package/dist/fields/blocks/validator.d.ts.map +1 -0
- package/dist/fields/checkbox/config.d.ts +46 -0
- package/dist/fields/checkbox/config.d.ts.map +1 -0
- package/dist/fields/checkbox/index.d.ts +5 -0
- package/dist/fields/checkbox/index.d.ts.map +1 -0
- package/dist/fields/checkbox/inputSchema.d.ts +27 -0
- package/dist/fields/checkbox/inputSchema.d.ts.map +1 -0
- package/dist/fields/checkbox/types.d.ts +67 -0
- package/dist/fields/checkbox/types.d.ts.map +1 -0
- package/dist/fields/checkbox/validator.d.ts +34 -0
- package/dist/fields/checkbox/validator.d.ts.map +1 -0
- package/dist/fields/color/config.d.ts +49 -0
- package/dist/fields/color/config.d.ts.map +1 -0
- package/dist/fields/color/convert.d.ts +72 -0
- package/dist/fields/color/convert.d.ts.map +1 -0
- package/dist/fields/color/formats.d.ts +38 -0
- package/dist/fields/color/formats.d.ts.map +1 -0
- package/dist/fields/color/index.d.ts +8 -0
- package/dist/fields/color/index.d.ts.map +1 -0
- package/dist/fields/color/inputSchema.d.ts +29 -0
- package/dist/fields/color/inputSchema.d.ts.map +1 -0
- package/dist/fields/color/types.d.ts +100 -0
- package/dist/fields/color/types.d.ts.map +1 -0
- package/dist/fields/color/utils.d.ts +100 -0
- package/dist/fields/color/utils.d.ts.map +1 -0
- package/dist/fields/color/validator.d.ts +27 -0
- package/dist/fields/color/validator.d.ts.map +1 -0
- package/dist/fields/constants.d.ts +108 -0
- package/dist/fields/constants.d.ts.map +1 -0
- package/dist/fields/date/config.d.ts +57 -0
- package/dist/fields/date/config.d.ts.map +1 -0
- package/dist/fields/date/index.d.ts +5 -0
- package/dist/fields/date/index.d.ts.map +1 -0
- package/dist/fields/date/inputSchema.d.ts +29 -0
- package/dist/fields/date/inputSchema.d.ts.map +1 -0
- package/dist/fields/date/types.d.ts +147 -0
- package/dist/fields/date/types.d.ts.map +1 -0
- package/dist/fields/date/validator.d.ts +34 -0
- package/dist/fields/date/validator.d.ts.map +1 -0
- package/dist/fields/group/config.d.ts +47 -0
- package/dist/fields/group/config.d.ts.map +1 -0
- package/dist/fields/group/index.d.ts +5 -0
- package/dist/fields/group/index.d.ts.map +1 -0
- package/dist/fields/group/inputSchema.d.ts +27 -0
- package/dist/fields/group/inputSchema.d.ts.map +1 -0
- package/dist/fields/group/types.d.ts +105 -0
- package/dist/fields/group/types.d.ts.map +1 -0
- package/dist/fields/group/validator.d.ts +41 -0
- package/dist/fields/group/validator.d.ts.map +1 -0
- package/dist/fields/index.d.ts +20 -0
- package/dist/fields/index.d.ts.map +1 -0
- package/dist/fields/inputSchemas/index.d.ts +23 -0
- package/dist/fields/inputSchemas/index.d.ts.map +1 -0
- package/dist/fields/inputSchemas/utils.d.ts +25 -0
- package/dist/fields/inputSchemas/utils.d.ts.map +1 -0
- package/dist/fields/interfaceGen.d.ts +15 -0
- package/dist/fields/interfaceGen.d.ts.map +1 -0
- package/dist/fields/number/config.d.ts +48 -0
- package/dist/fields/number/config.d.ts.map +1 -0
- package/dist/fields/number/index.d.ts +5 -0
- package/dist/fields/number/index.d.ts.map +1 -0
- package/dist/fields/number/inputSchema.d.ts +23 -0
- package/dist/fields/number/inputSchema.d.ts.map +1 -0
- package/dist/fields/number/types.d.ts +100 -0
- package/dist/fields/number/types.d.ts.map +1 -0
- package/dist/fields/number/validator.d.ts +39 -0
- package/dist/fields/number/validator.d.ts.map +1 -0
- package/dist/fields/relationship/config.d.ts +45 -0
- package/dist/fields/relationship/config.d.ts.map +1 -0
- package/dist/fields/relationship/index.d.ts +5 -0
- package/dist/fields/relationship/index.d.ts.map +1 -0
- package/dist/fields/relationship/inputSchema.d.ts +31 -0
- package/dist/fields/relationship/inputSchema.d.ts.map +1 -0
- package/dist/fields/relationship/types.d.ts +126 -0
- package/dist/fields/relationship/types.d.ts.map +1 -0
- package/dist/fields/relationship/validator.d.ts +27 -0
- package/dist/fields/relationship/validator.d.ts.map +1 -0
- package/dist/fields/select/config.d.ts +59 -0
- package/dist/fields/select/config.d.ts.map +1 -0
- package/dist/fields/select/index.d.ts +5 -0
- package/dist/fields/select/index.d.ts.map +1 -0
- package/dist/fields/select/inputSchema.d.ts +28 -0
- package/dist/fields/select/inputSchema.d.ts.map +1 -0
- package/dist/fields/select/types.d.ts +89 -0
- package/dist/fields/select/types.d.ts.map +1 -0
- package/dist/fields/select/validator.d.ts +31 -0
- package/dist/fields/select/validator.d.ts.map +1 -0
- package/dist/fields/text/config.d.ts +48 -0
- package/dist/fields/text/config.d.ts.map +1 -0
- package/dist/fields/text/index.d.ts +5 -0
- package/dist/fields/text/index.d.ts.map +1 -0
- package/dist/fields/text/inputSchema.d.ts +24 -0
- package/dist/fields/text/inputSchema.d.ts.map +1 -0
- package/dist/fields/text/types.d.ts +132 -0
- package/dist/fields/text/types.d.ts.map +1 -0
- package/dist/fields/text/validator.d.ts +39 -0
- package/dist/fields/text/validator.d.ts.map +1 -0
- package/dist/fields/types.d.ts +135 -0
- package/dist/fields/types.d.ts.map +1 -0
- package/dist/fields/upload/config.d.ts +24 -0
- package/dist/fields/upload/config.d.ts.map +1 -0
- package/dist/fields/upload/index.d.ts +5 -0
- package/dist/fields/upload/index.d.ts.map +1 -0
- package/dist/fields/upload/inputSchema.d.ts +17 -0
- package/dist/fields/upload/inputSchema.d.ts.map +1 -0
- package/dist/fields/upload/types.d.ts +78 -0
- package/dist/fields/upload/types.d.ts.map +1 -0
- package/dist/fields/upload/validator.d.ts +16 -0
- package/dist/fields/upload/validator.d.ts.map +1 -0
- package/dist/fields/url/config.d.ts +49 -0
- package/dist/fields/url/config.d.ts.map +1 -0
- package/dist/fields/url/index.d.ts +5 -0
- package/dist/fields/url/index.d.ts.map +1 -0
- package/dist/fields/url/inputSchema.d.ts +31 -0
- package/dist/fields/url/inputSchema.d.ts.map +1 -0
- package/dist/fields/url/types.d.ts +70 -0
- package/dist/fields/url/types.d.ts.map +1 -0
- package/dist/fields/url/validator.d.ts +37 -0
- package/dist/fields/url/validator.d.ts.map +1 -0
- package/dist/fields/utils.d.ts +47 -0
- package/dist/fields/utils.d.ts.map +1 -0
- package/dist/fields/validators/index.d.ts +23 -0
- package/dist/fields/validators/index.d.ts.map +1 -0
- package/dist/fields/validators/utils.d.ts +24 -0
- package/dist/fields/validators/utils.d.ts.map +1 -0
- package/dist/framework.d.ts +227 -0
- package/dist/framework.d.ts.map +1 -0
- package/dist/globals/config/config.d.ts +1 -0
- package/dist/globals/config/config.d.ts.map +1 -0
- package/dist/globals/config/types.d.ts +1 -0
- package/dist/globals/config/types.d.ts.map +1 -0
- package/dist/globals/config.d.ts +47 -0
- package/dist/globals/config.d.ts.map +1 -0
- package/dist/globals/index.d.ts +5 -0
- package/dist/globals/index.d.ts.map +1 -0
- package/dist/globals/interfaceGen.d.ts +33 -0
- package/dist/globals/interfaceGen.d.ts.map +1 -0
- package/dist/globals/types.d.ts +175 -0
- package/dist/globals/types.d.ts.map +1 -0
- package/dist/globals/utils.d.ts +37 -0
- package/dist/globals/utils.d.ts.map +1 -0
- package/dist/index.d.ts +15 -3165
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2101 -3470
- package/dist/index.js.map +1 -1
- package/dist/media/api/index.d.ts +5 -0
- package/dist/media/api/index.d.ts.map +1 -0
- package/dist/media/api/mutations.d.ts +76 -0
- package/dist/media/api/mutations.d.ts.map +1 -0
- package/dist/media/api/queries.d.ts +28 -0
- package/dist/media/api/queries.d.ts.map +1 -0
- package/dist/media/api/server.d.ts +68 -0
- package/dist/media/api/server.d.ts.map +1 -0
- package/dist/media/api/types.d.ts +171 -0
- package/dist/media/api/types.d.ts.map +1 -0
- package/dist/media/config.d.ts +25 -0
- package/dist/media/config.d.ts.map +1 -0
- package/dist/media/index.d.ts +6 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media/types.d.ts +332 -0
- package/dist/media/types.d.ts.map +1 -0
- package/dist/media/utils.d.ts +35 -0
- package/dist/media/utils.d.ts.map +1 -0
- package/dist/media/validators.d.ts +61 -0
- package/dist/media/validators.d.ts.map +1 -0
- package/dist/schema/generateVexSchema.d.ts +36 -0
- package/dist/schema/generateVexSchema.d.ts.map +1 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/migrate.d.ts +73 -0
- package/dist/schema/migrate.d.ts.map +1 -0
- package/dist/types/generateVexTypes.d.ts +41 -0
- package/dist/types/generateVexTypes.d.ts.map +1 -0
- package/dist/types/generated.d.ts +382 -0
- package/dist/types/generated.d.ts.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils.d.ts +57 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +67 -24
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/server.ts","../src/api/depth.ts","../../../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","../../../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","../src/api/populate.ts","../src/access/constants.ts","../src/access/types.ts","../src/access/createFilterConstraintBuilder.ts","../src/access/createIndexConstraintBuilder.ts","../src/access/createAccessQueryBuilder.ts","../src/access/compileConstraints.ts","../src/access/hasPermission.ts","../src/access/resolveAccessRule.ts","../src/access/pickQueryIndex.ts","../src/fields/constants.ts","../src/api/utils.ts","../src/api/find/server.ts","../src/api/get/server.ts","../src/api/search/server.ts","../src/api/create/server.ts","../src/api/update/server.ts","../src/api/remove/server.ts","../src/api/globals/get.server.ts","../src/api/globals/find.server.ts","../src/api/globals/upsert.server.ts","../src/globals/utils.ts","../src/fields/text/inputSchema.ts","../src/fields/inputSchemas/utils.ts","../src/fields/number/inputSchema.ts","../src/fields/checkbox/inputSchema.ts","../src/fields/date/inputSchema.ts","../src/fields/select/inputSchema.ts","../src/fields/url/inputSchema.ts","../src/fields/color/formats.ts","../src/fields/color/inputSchema.ts","../src/fields/relationship/inputSchema.ts","../src/fields/array/inputSchema.ts","../src/fields/group/inputSchema.ts","../src/fields/blocks/inputSchema.ts","../src/fields/upload/inputSchema.ts","../src/fields/inputSchemas/index.ts"],"sourcesContent":["import {\n paginationOptsValidator,\n type MutationBuilder,\n type FunctionVisibility,\n type GenericDataModel,\n type QueryBuilder,\n type RegisteredQuery,\n GenericQueryCtx,\n GenericMutationCtx,\n} from \"convex/server\";\nimport { GenericId, v } from \"convex/values\";\nimport type { VexConfig } from \"../config\";\nimport type {\n CollectionSlug,\n GlobalPopulateShape,\n GlobalSlug,\n VexDocumentGlobal,\n} from \"../types/generated\";\nimport type { FindServerArgs } from \"./find/server\";\nimport type { GetServerArgs } from \"./get/server\";\nimport type { SearchServerArgs } from \"./search/server\";\nimport type { CreateServerArgs } from \"./create/server\";\nimport type { UpdateServerArgs } from \"./update/server\";\nimport type { RemoveServerArgs } from \"./remove/server\";\nimport type { GetGlobalReturn, GetGlobalServerArgs } from \"./globals/get.server\";\nimport type { UpsertGlobalServerArgs } from \"./globals/upsert.server\";\nimport type { GenericGlobalsQueryServerArgs } from \"./globals/types\";\nimport type {\n AccessCallOptions,\n DocReturnItem,\n GetReturn,\n PaginationOptions,\n PaginationResult,\n PopulateShape,\n} from \"./types\";\nimport { find } from \"./find/server\";\nimport { get } from \"./get/server\";\nimport { search } from \"./search/server\";\nimport { create } from \"./create/server\";\nimport { update } from \"./update/server\";\nimport { remove } from \"./remove/server\";\nimport { getGlobal } from \"./globals/get.server\";\nimport { findGlobals } from \"./globals/find.server\";\nimport { upsertGlobal } from \"./globals/upsert.server\";\nimport { VexGlobalsGetArgs } from \"./convex\";\nimport { VexAccessConfigError } from \"../access\";\nimport { VexApiAuth } from \"./types\";\n\nexport { buildDepthPopulate } from \"./depth\";\n\nexport { find } from \"./find/server\";\nexport type { FindServerArgs } from \"./find/server\";\n\nexport { get } from \"./get/server\";\nexport type { GetServerArgs } from \"./get/server\";\n\nexport { search } from \"./search/server\";\nexport type { SearchServerArgs } from \"./search/server\";\n\nexport { create } from \"./create/server\";\nexport type { CreateServerArgs } from \"./create/server\";\n\nexport { update } from \"./update/server\";\nexport type { UpdateServerArgs } from \"./update/server\";\n\nexport { remove } from \"./remove/server\";\nexport type { RemoveServerArgs } from \"./remove/server\";\n\nexport { getGlobal } from \"./globals/get.server\";\nexport type { GetGlobalServerArgs } from \"./globals/get.server\";\nexport { findGlobals } from \"./globals/find.server\";\nexport { upsertGlobal } from \"./globals/upsert.server\";\nexport type { UpsertGlobalServerArgs } from \"./globals/upsert.server\";\n\n/**\n * Registers the full collection CRUD surface — `find`, `get`, `search`,\n * `create`, `update`, `remove` — as Convex endpoints, with RBAC enforcement\n * resolved entirely on the server.\n *\n * All query/mutation logic lives in the imported server functions; this\n * factory only provides the arg validators, the `query()`/`mutation()`\n * wrappers Convex needs at the network boundary, and the auth seam.\n *\n * **Identity never crosses the wire as an argument.** Callers (React\n * components, Next.js preloads, raw clients) cannot pass a user or\n * organization — there is deliberately no `auth` arg in any endpoint's\n * validator, so a client cannot impersonate its way past a permission check.\n * Instead, each handler resolves the caller once per request via the\n * server-side `getAuth` callback, which reads the authenticated connection\n * (`ctx.auth`) and returns the current user document and active organization.\n * The resolved {@link VexApiAuth} is forwarded to the underlying server\n * functions, which run {@link hasPermission} against `config.access`:\n * reads filter denied documents out (and `get` returns `null`), writes throw\n * {@link VexAccessError}.\n *\n * Enforcement is strictly opt-in, and misconfiguration fails loudly:\n * - No `config.access` → RBAC is off; `getAuth` is never called and every\n * operation behaves exactly as before the seam existed.\n * - `config.access` set but `getAuth` omitted → {@link VexAccessConfigError}\n * on the first request (see {@link resolveGetAuth}) — a permission matrix\n * with no caller resolver is a project bug, surfaced immediately.\n * - `getAuth` resolving `undefined` (unauthenticated caller) → checks run\n * with no user, so no roles resolve and access is denied, unless access.anonRole\n * is configured, in which case that role will be used.\n *\n * Because identity rides the Convex connection token, the same endpoints work\n * unchanged from every runtime: client subscriptions (`useQuery`) on an\n * authenticated Convex provider, and server preloads by passing the JWT\n * (`preloadQuery(api.vex.find, args, { token })`) — preloaded results come\n * back already permission-filtered. Auth plugins supply the resolver (e.g.\n * `@vexcms/better-auth` exports one that loads the user document and active\n * organization from the session); core only knows the `getAuth` shape.\n *\n * @typeParam DataModel - The project's generated Convex data model.\n * @typeParam Visibility - Function visibility of the supplied builders;\n * defaults to `\"public\"`.\n * @param props - Factory configuration.\n * @param props.config - The resolved `VexConfig`; `config.access` (when set)\n * is the permission matrix every operation enforces.\n * @param props.query - The project's Convex `query` builder.\n * @param props.mutation - The project's Convex `mutation` builder.\n * @param props.getAuth - Server-side resolver for the current caller: receives\n * the handler's ctx and returns `{ user, organization? }` (or `undefined`\n * when unauthenticated). Called once per request, and only when\n * `config.access` is configured. Never exposed to clients.\n * @returns Registered `find` / `get` / `search` queries and\n * `create` / `update` / `remove` mutations for `convex/vex.ts` to re-export.\n *\n * @example\n * ```ts\n * // apps/test/convex/vex.ts\n * import { collectionsApi } from \"@vexcms/core/server\";\n * import { createGetAuth } from \"@vexcms/better-auth/server\";\n * import { mutation, query } from \"./_generated/server\";\n * import config from \"~/vex.config\";\n *\n * export const { find, get, search, create, update, remove } = collectionsApi({\n * config,\n * query,\n * mutation,\n * getAuth: createGetAuth(), // resolves user + active org from ctx.auth\n * });\n * ```\n *\n * @see {@link hasPermission} for resolution semantics (roles, wildcards, field maps)\n * @see {@link VexApiAuth} for the resolved caller shape\n */\nexport function collectionsApi<\n DataModel extends GenericDataModel,\n Visibility extends FunctionVisibility = \"public\",\n>({\n config,\n query,\n mutation,\n getAuth,\n}: {\n config: VexConfig;\n query: QueryBuilder<DataModel, Visibility>;\n mutation: MutationBuilder<DataModel, Visibility>;\n getAuth?: (\n ctx: GenericQueryCtx<DataModel> | GenericMutationCtx<DataModel>,\n ) => Promise<VexApiAuth | undefined>;\n}) {\n return {\n find: query({\n args: {\n collection: v.string(),\n populate: v.optional(v.any()),\n depth: v.optional(v.number()),\n limit: v.optional(v.number()),\n paginationOpts: v.optional(\n paginationOptsValidator.extend({ totalDocs: v.optional(v.boolean()) }),\n ),\n },\n handler: async (ctx, args) => {\n const auth = await resolveGetAuth({ ctx, config, getAuth });\n return await find({\n ctx,\n auth,\n collection: args.collection as CollectionSlug,\n populate: args.populate,\n depth: args.depth,\n config,\n limit: args.limit,\n paginationOpts: args.paginationOpts as never,\n });\n },\n }),\n\n get: query({\n args: {\n collection: v.string(),\n id: v.string(),\n populate: v.optional(v.any()),\n depth: v.optional(v.number()),\n },\n handler: async (ctx, args) => {\n const auth = await resolveGetAuth({ ctx, config, getAuth });\n return await get({\n auth,\n ctx,\n collection: args.collection as CollectionSlug,\n id: args.id as GenericId<CollectionSlug>,\n populate: args.populate,\n depth: args.depth,\n config,\n });\n },\n }),\n\n search: query({\n args: {\n collection: v.string(),\n searchIndexName: v.string(),\n searchField: v.string(),\n query: v.string(),\n limit: v.optional(v.number()),\n populate: v.optional(v.any()),\n depth: v.optional(v.number()),\n paginationOpts: v.optional(\n paginationOptsValidator.extend({ totalDocs: v.optional(v.boolean()) }),\n ),\n },\n handler: async (ctx, args) => {\n const auth = await resolveGetAuth({ ctx, config, getAuth });\n return search({\n auth,\n ctx,\n collection: args.collection as CollectionSlug,\n query: args.query,\n searchIndexName: args.searchIndexName,\n searchField: args.searchField,\n limit: args.limit,\n populate: args.populate,\n depth: args.depth,\n paginationOpts: args.paginationOpts as never,\n config,\n });\n },\n }),\n\n // MUTATIONS\n create: mutation({\n args: {\n collection: v.string(),\n data: v.any(),\n },\n returns: v.string(),\n handler: async (ctx, args) => {\n const auth = await resolveGetAuth({ ctx, config, getAuth });\n return await create({\n auth,\n ctx,\n config,\n collection: args.collection as CollectionSlug,\n data: args.data,\n });\n },\n }),\n\n update: mutation({\n args: {\n id: v.string(),\n collection: v.string(),\n data: v.any(),\n },\n handler: async (ctx, args) => {\n const auth = await resolveGetAuth({ ctx, config, getAuth });\n return update({\n auth,\n ctx,\n config,\n collection: args.collection as CollectionSlug,\n id: args.id as GenericId<CollectionSlug>,\n data: args.data,\n });\n },\n }),\n\n remove: mutation({\n args: {\n collection: v.string(),\n ids: v.array(v.string()),\n softDelete: v.optional(v.string()),\n },\n handler: async (ctx, args) => {\n const auth = await resolveGetAuth({ ctx, config, getAuth });\n return remove({\n auth,\n collection: args.collection as CollectionSlug,\n ctx,\n config,\n ids: args.ids as GenericId<CollectionSlug>[],\n softDelete: args.softDelete,\n });\n },\n }),\n };\n}\n\n/**\n * Registers `globals.get`, `globals.find`, and `globals.update` as Convex\n * query and mutation endpoints under `api.vex.globals.*`.\n *\n * Call once in `convex/vex.ts` alongside `queryApi` and `mutationApi`.\n *\n * @param props.config - The resolved `VexConfig`.\n * @param props.query - Convex `query` builder. Defaults to `internalQueryGeneric`.\n * @param props.mutation - Convex `mutation` builder. Defaults to `internalMutationGeneric`.\n * @param props.getAuth - Server-side resolver for the current caller: receives\n * the handler's ctx and returns `{ user, organization? }` (or `undefined`\n * when unauthenticated). Called once per request, and only when\n * `config.access` is configured. Never exposed to clients.\n * @returns `{ globals }` with `.get`, `.find`, `.update` registered handlers.\n *\n * @example\n * ```ts\n * // apps/test/convex/vex.ts\n * import { queryApi, mutationApi, globalsApi } from \"@vexcms/core/server\";\n * import { query, mutation } from \"./_generated/server\";\n * import config from \"../src/vex.config\";\n *\n * export const { find, get, search } = queryApi(config, query);\n * export const { create, update, remove } = mutationApi(config, mutation);\n * export const { globals } = globalsApi(config, query, mutation);\n * // → api.vex.globals.get, api.vex.globals.find, api.vex.globals.update\n * ```\n */\nexport function globalsApi<\n DataModel extends GenericDataModel,\n Visibility extends FunctionVisibility = \"public\",\n>({\n config,\n query,\n mutation,\n getAuth,\n}: {\n config: VexConfig;\n query: QueryBuilder<DataModel, Visibility>;\n mutation: MutationBuilder<DataModel, Visibility>;\n getAuth?: (\n ctx: GenericQueryCtx<DataModel> | GenericMutationCtx<DataModel>,\n ) => Promise<VexApiAuth | undefined>;\n}) {\n return {\n get: query({\n args: {\n slug: v.string(),\n populate: v.optional(v.any()),\n },\n handler: async (ctx, args) => {\n const auth = await resolveGetAuth({ ctx, config, getAuth });\n return await getGlobal({\n auth,\n ctx,\n slug: args.slug as GlobalSlug,\n populate: args.populate,\n config,\n });\n },\n }) as RegisteredQuery<Visibility, VexGlobalsGetArgs, VexDocumentGlobal | null>,\n\n find: query({\n args: {},\n handler: async (ctx) => {\n const auth = await resolveGetAuth({ ctx, config, getAuth });\n return await findGlobals({ auth, ctx, config });\n },\n }),\n\n upsert: mutation({\n args: {\n slug: v.string(),\n data: v.any(),\n },\n returns: v.string(),\n handler: async (ctx, args) => {\n const auth = await resolveGetAuth({ ctx, config, getAuth });\n return await upsertGlobal({\n auth,\n ctx,\n config,\n slug: args.slug as GlobalSlug,\n data: args.data as Record<string, unknown>,\n });\n },\n }),\n };\n}\n\n/**\n * Resolves the current caller for RBAC enforcement — the single auth\n * resolution point every `collectionsApi` handler runs before its operation.\n *\n * Behavior by configuration:\n * - No `config.access` → RBAC is off. Returns `undefined` without ever\n * calling `getAuth` — zero auth overhead for projects without access\n * control.\n * - `config.access` set but no `getAuth` supplied → throws\n * {@link VexAccessConfigError}. A permission matrix with no way to resolve\n * callers is a project misconfiguration; failing loudly at the first\n * request beats silently denying (or worse, allowing) everything.\n * - Both configured → returns whatever `getAuth(ctx)` resolves: the caller's\n * user document and active organization, or `undefined` when the request\n * is unauthenticated. Downstream checks treat `undefined` as a user with\n * no roles — deny. If access.anonRole is configured, downstream checks will\n * fallback to that role.\n *\n * Identity always derives from the handler's `ctx` (the authenticated Convex\n * connection), never from client-supplied arguments — see\n * {@link collectionsApi} for the trust-model rationale.\n *\n * @typeParam TDataModel - The project's generated Convex data model.\n * @param props - Resolution inputs.\n * @param props.config - The resolved `VexConfig`; only `config.access`'s\n * presence is consulted here.\n * @param props.ctx - The current handler's query or mutation context.\n * @param props.getAuth - The project's server-side caller resolver (typically\n * supplied by an auth plugin, e.g. `@vexcms/better-auth`).\n * @returns The resolved {@link VexApiAuth}, or `undefined` when RBAC is off\n * or the request is unauthenticated.\n * @throws {VexAccessConfigError} When `config.access` is configured but no\n * `getAuth` resolver was passed to `collectionsApi`.\n */\nexport async function resolveGetAuth<TDataModel extends GenericDataModel>({\n config,\n ctx,\n getAuth,\n}: {\n config: VexConfig;\n ctx: GenericQueryCtx<TDataModel> | GenericMutationCtx<TDataModel>;\n getAuth?: (\n ctx: GenericQueryCtx<TDataModel> | GenericMutationCtx<TDataModel>,\n ) => Promise<VexApiAuth | undefined>;\n}) {\n let auth: VexApiAuth | undefined = undefined;\n if (config.access !== undefined) {\n if (!getAuth) {\n throw new VexAccessConfigError(\n \"getAuth not configured in collectionsApi. configure @ /src/convex/vex.ts\",\n );\n }\n auth = await getAuth(ctx);\n }\n return auth;\n}\n\n// ── Bound server API ────────────────────────────────────────────────────────\n//\n// `config` is static for a project, but `auth` is per-request (it derives from\n// `ctx`). So the factory binds `config` once and resolves `auth` on every call,\n// which is why it takes a `getAuth` callback rather than an auth value.\n//\n// Core cannot discover `config` implicitly: it lives in the consumer's project,\n// and a module-level singleton would be import-order dependent and untestable.\n// Binding explicitly, once, in one project file is the tradeoff.\n\n/**\n * Args accepted by a bound helper from {@link vexServerApi} — the\n * underlying server args minus `config` and `auth`, which the factory injects.\n *\n * @typeParam TArgs - The wrapped function's original server args.\n */\nexport type BoundServerArgs<TArgs> = Omit<TArgs, \"auth\" | \"config\">;\n\n/**\n * Minimal shape the overloaded `find`/`search` wrappers destructure internally.\n * Not part of the public contract — {@link BoundFind} / {@link BoundSearch} are.\n *\n * @internal\n */\ntype BoundPassthroughArgs<DataModel extends GenericDataModel> = {\n ctx: GenericQueryCtx<DataModel> | GenericMutationCtx<DataModel>;\n access?: AccessCallOptions;\n};\n\n/**\n * `find`, bound to a config/auth pair. Preserves both server overloads:\n * without `paginationOpts` the result is an array, with it a `PaginationResult`.\n *\n * Return types inline `DocReturnItem<…>[]` rather than naming `FindReturn` so\n * hover resolves to `Doc[]` — see the display note in `./types`.\n */\nexport interface BoundFind<DataModel extends GenericDataModel> {\n <\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n >(\n args: BoundServerArgs<FindServerArgs<DataModel, TCollectionSlug, TPopulate, D>> & {\n paginationOpts?: never;\n },\n ): Promise<DocReturnItem<TCollectionSlug, TPopulate, D>[]>;\n <\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n >(\n args: BoundServerArgs<FindServerArgs<DataModel, TCollectionSlug, TPopulate, D>> & {\n paginationOpts: PaginationOptions;\n },\n ): Promise<PaginationResult<DocReturnItem<TCollectionSlug, TPopulate, D>>>;\n}\n\n/** `search`, bound to a config/auth pair. Preserves both server overloads. */\nexport interface BoundSearch<DataModel extends GenericDataModel> {\n <\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n >(\n args: BoundServerArgs<SearchServerArgs<DataModel, TCollectionSlug, TPopulate, D>> & {\n paginationOpts?: never;\n },\n ): Promise<DocReturnItem<TCollectionSlug, TPopulate, D>[]>;\n <\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n >(\n args: BoundServerArgs<SearchServerArgs<DataModel, TCollectionSlug, TPopulate, D>> & {\n paginationOpts: PaginationOptions;\n },\n ): Promise<PaginationResult<DocReturnItem<TCollectionSlug, TPopulate, D>>>;\n}\n\n/**\n * The collection and globals surface returned by {@link vexServerApi},\n * with `config` and `auth` already applied. Every member keeps the slug,\n * populate, and depth generics of the function it wraps, so return types narrow\n * exactly as the unbound versions do.\n */\nexport interface VexServerApi<DataModel extends GenericDataModel> {\n /** Fetch one document by id. */\n get: <\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n >(\n args: BoundServerArgs<GetServerArgs<DataModel, TCollectionSlug, TPopulate, D>>,\n ) => Promise<GetReturn<TCollectionSlug, TPopulate, D>>;\n /** List documents, optionally paginated. */\n find: BoundFind<DataModel>;\n /** Text-search documents, optionally paginated. */\n search: BoundSearch<DataModel>;\n /** Insert a document; resolves to the new id. */\n create: <TCollectionSlug extends CollectionSlug>(\n args: BoundServerArgs<CreateServerArgs<DataModel, TCollectionSlug>>,\n ) => Promise<string>;\n /** Patch a document. */\n update: <TCollectionSlug extends CollectionSlug>(\n args: BoundServerArgs<UpdateServerArgs<DataModel, TCollectionSlug>>,\n ) => Promise<void>;\n /** Delete (or soft-delete) documents. */\n remove: <TCollectionSlug extends CollectionSlug>(\n args: BoundServerArgs<RemoveServerArgs<DataModel, TCollectionSlug>>,\n ) => Promise<void>;\n globals: {\n /** Fetch one global as a flat document. */\n get: <\n TSlug extends GlobalSlug,\n const TPopulate extends GlobalPopulateShape<TSlug> = Record<string, never>,\n const D extends number = 0,\n >(\n args: BoundServerArgs<GetGlobalServerArgs<DataModel, TSlug, TPopulate, D>>,\n ) => Promise<GetGlobalReturn<TSlug, TPopulate, D>>;\n /** List every saved global. Mixed-slug, so intentionally un-narrowed. */\n find: (\n args: BoundServerArgs<GenericGlobalsQueryServerArgs<DataModel>>,\n ) => Promise<VexDocumentGlobal[]>;\n /** Create or update a global; resolves to its id. */\n upsert: <TSlug extends GlobalSlug = GlobalSlug>(\n args: BoundServerArgs<UpsertGlobalServerArgs<DataModel, TSlug>>,\n ) => Promise<string>;\n };\n}\n\n/**\n * Binds `config` and a `getAuth` resolver to the whole server API, so callers\n * never pass either again.\n *\n * Without this, every call site must remember `config` *and* `auth`. Forgetting\n * `config` silently disables RBAC for that call; passing `config` without\n * `auth` denies (or throws) on every check. Binding once removes both failure\n * modes by construction.\n *\n * Call it once in the project and re-export the result:\n *\n * @typeParam DataModel - The project's generated Convex data model. Required —\n * it cannot be inferred (`getAuth` is optional, so there is no reliable\n * inference site), and letting it fall back to `GenericDataModel` produces a\n * confusing `ctx` mismatch at every call site rather than an error here.\n * @param options - Binding inputs.\n * @param options.config - The resolved `VexConfig` from `defineConfig`.\n * @param options.getAuth - Resolves the caller from `ctx` on every call\n * (typically `createGetAuth()` from an auth adapter). Required whenever\n * `config.access` is set; {@link resolveGetAuth} throws if it is missing.\n * @returns The full {@link VexServerApi} with `config`/`auth` pre-applied.\n *\n * @example\n * ```ts\n * // convex/vexApi.ts — written once\n * import { vexServerApi } from \"@vexcms/core/server\";\n * import config from \"~/vex.config\";\n * import type { DataModel } from \"./_generated/dataModel\";\n * import { getAuth } from \"./vexContext\";\n *\n * export const { get, find, search, create, update, remove, globals } =\n * vexServerApi<DataModel>({ config, getAuth });\n * ```\n *\n * ```ts\n * // anywhere else — no config, no auth, RBAC enforced\n * import { find } from \"./vexApi\";\n *\n * export const list = query({\n * handler: async (ctx) => await find({ ctx, collection: \"pages\" }),\n * });\n * // ^? Page[]\n *\n * // a genuinely public read, opted out explicitly\n * export const bySlug = query({\n * args: { slug: v.string() },\n * handler: async (ctx, args) =>\n * await find({\n * ctx,\n * collection: \"pages\",\n * withIndex: { name: \"by_slug\", range: (q) => q.eq(\"slug\", args.slug) },\n * limit: 1,\n * access: { bypass: true },\n * }),\n * });\n * ```\n *\n * @see {@link collectionsApi} to register the generic CRUD endpoints instead\n * @see {@link resolveGetAuth} for the per-call auth resolution this performs\n */\nexport function vexServerApi<DataModel extends GenericDataModel>(options: {\n config: VexConfig;\n getAuth?: (\n ctx: GenericQueryCtx<DataModel> | GenericMutationCtx<DataModel>,\n ) => Promise<VexApiAuth | undefined>;\n}): VexServerApi<DataModel> {\n const { config, getAuth } = options;\n\n /*\n * Resolves the `{ config, auth }` pair to inject for one call.\n */\n async function inject(\n ctx: GenericQueryCtx<DataModel> | GenericMutationCtx<DataModel>,\n bypassAccess?: boolean,\n ): Promise<{ config: VexConfig; auth: VexApiAuth | undefined }> {\n // The stripped config is used ONLY to resolve auth: `resolveGetAuth` returns\n // `undefined` without invoking `getAuth` when RBAC is off, so a bypassed call\n // costs no session lookup. The ORIGINAL config is what gets forwarded - the raw\n // function's `resolveAccessCall` enforces the bypass itself, and forwarding the\n // stripped config would make its dev guard see \"bypass set but RBAC off\" and\n // warn spuriously on every legitimate bypass. One enforcement seam, not two.\n const authConfig: VexConfig = bypassAccess === true ? { ...config, access: undefined } : config;\n return { config, auth: await resolveGetAuth({ ctx, config: authConfig, getAuth }) };\n }\n\n // Each wrapper below is a generic pass-through, so the spread of `rest` cannot\n // be re-checked against the callee's mutually-exclusive `populate`/`depth`\n // conditionals. The public signatures on `VexServerApi` are the contract; the\n // internal `as never` casts are confined to these five lines and mirror the\n // casts the underlying server functions already use.\n return {\n get: async (args) => {\n const { access, ...rest } = args;\n return (await get({\n access,\n ...rest,\n ...(await inject(rest.ctx, access?.bypass)),\n } as never)) as never;\n },\n // Cast to the overloaded interface: an implementation signature cannot be\n // structurally assignable to two call signatures at once.\n find: (async (args: BoundPassthroughArgs<DataModel>) => {\n const { access, ...rest } = args;\n return (await find({\n access,\n ...rest,\n ...(await inject(rest.ctx, access?.bypass)),\n } as never)) as never;\n }) as BoundFind<DataModel>,\n search: (async (args: BoundPassthroughArgs<DataModel>) => {\n const { access, ...rest } = args;\n return (await search({\n access,\n ...rest,\n ...(await inject(rest.ctx, access?.bypass)),\n } as never)) as never;\n }) as BoundSearch<DataModel>,\n create: async (args) => {\n const { access, ...rest } = args;\n return await create({\n access,\n ...rest,\n ...(await inject(rest.ctx, access?.bypass)),\n } as never);\n },\n update: async (args) => {\n const { access, ...rest } = args;\n return await update({\n access,\n ...rest,\n ...(await inject(rest.ctx, access?.bypass)),\n } as never);\n },\n remove: async (args) => {\n const { access, ...rest } = args;\n return await remove({\n access,\n ...rest,\n ...(await inject(rest.ctx, access?.bypass)),\n } as never);\n },\n globals: {\n get: async (args) => {\n const { access, ...rest } = args;\n return (await getGlobal({\n access,\n ...rest,\n ...(await inject(rest.ctx, access?.bypass)),\n } as never)) as never;\n },\n find: async (args) => {\n const { access, ...rest } = args;\n return await findGlobals({\n access,\n ...rest,\n ...(await inject(rest.ctx, access?.bypass)),\n } as never);\n },\n upsert: async (args) => {\n const { access, ...rest } = args;\n return await upsertGlobal({\n access,\n ...rest,\n ...(await inject(rest.ctx, access?.bypass)),\n } as never);\n },\n },\n };\n}\n","import type { AdminField } from \"../fields\";\nimport type { VexConfig } from \"../config\";\nimport { PopulateShape } from \"./types\";\n\n/**\n * Builds a `PopulateShape`-compatible object covering every relationship field\n * on `slug`'s collection down to `depth` levels.\n *\n * This is the runtime counterpart of the compile-time `DepthPopulate<TCollectionSlug, D>`\n * type. The returned object can be passed directly to `populateDocs` because it\n * matches the `PopulateShape` structure:\n * - Leaf fields (at the requested depth, or targets with no relationships) → `true`\n * - Intermediate fields → `{ populate: <nested result> }`\n *\n * The function relies on the live `VexConfig` available in the `queryApi`\n * closure. It never touches the database — it only walks the in-memory config.\n *\n * **Circular schema guard:** the `visited` set prevents infinite recursion when\n * two collections point to each other. A circular edge is treated as a leaf\n * (`true`) — the field is populated one level, but the cycle is cut.\n *\n * @param config - The user's resolved `VexConfig`.\n * @param slug - The collection or global slug to start from.\n * @param depth - Levels to descend. `0` returns `{}` (no population).\n * @param visited - Internal cycle guard — callers must not supply this.\n * @returns A plain object usable as the `populate` arg to `populateDocs`.\n *\n * @example depth 1 — all direct relationships populated\n * ```ts\n * buildDepthPopulate(config, \"posts\", 1)\n * // → { author: true, category: true }\n * ```\n * @example depth 2 — relationships of relationships also populated\n * ```ts\n * buildDepthPopulate(config, \"posts\", 2)\n * // → { author: { populate: { team: true } }, category: true }\n * // (category has no relationships → collapses to `true`)\n * ```\n */\nexport function buildDepthPopulate<TPopulate extends PopulateShape = PopulateShape>(\n config: VexConfig,\n slug: string,\n depth: number,\n visited: ReadonlySet<string> = new Set(),\n): TPopulate {\n if (depth <= 0) return {} as TPopulate;\n\n const source =\n config.collections.find((c) => c.slug === slug) ?? config.globals?.find((g) => g.slug === slug);\n if (!source) return {} as TPopulate;\n\n const nextVisited = new Set(visited).add(slug);\n const result: TPopulate = {} as TPopulate;\n\n for (const [key, field] of Object.entries(source.fields as Record<string, AdminField>)) {\n if (field.type !== \"relationship\") continue;\n\n const targetSlug = (field as Extract<AdminField, { type: \"relationship\" }>).collection.slug;\n\n // Leaf level OR circular edge → `true` (populate this field, no further recursion)\n if (depth === 1 || nextVisited.has(targetSlug)) {\n result[key as keyof TPopulate] = true as TPopulate[keyof TPopulate];\n continue;\n }\n\n const nested = buildDepthPopulate(config, targetSlug, depth - 1, nextVisited);\n // Target has no relationship fields → collapse to `true`\n result[key as keyof TPopulate] =\n Object.keys(nested).length > 0\n ? ({ populate: nested } as TPopulate[keyof TPopulate])\n : (true as TPopulate[keyof TPopulate]);\n }\n\n return result;\n}\n","/**\n * asyncMap returns the results of applying an async function over an list.\n *\n * The list can even be a promise, or an iterable like a Set.\n * @param list - Iterable object of items, e.g. an Array, Set, Object.keys\n * @param asyncTransform\n * @returns\n */\nexport async function asyncMap(list, asyncTransform) {\n const promises = [];\n let index = 0;\n list = await list;\n for (const item of list) {\n promises.push(asyncTransform(item, index));\n index += 1;\n }\n return Promise.all(promises);\n}\n/**\n * Filters out null elements from an array.\n * @param list List of elements that might be null.\n * @returns List of elements with nulls removed.\n */\nexport function pruneNull(list) {\n return list.filter((i) => i !== null);\n}\nexport class NullDocumentError extends Error {\n}\n/**\n * Throws if there is a null element in the array.\n * @param list List of elements that might have a null element.\n * @returns Same list of elements with a refined type.\n */\nexport function nullThrows(doc, message) {\n if (doc === null) {\n throw new NullDocumentError(message ?? \"Unexpected null document.\");\n }\n return doc;\n}\n/**\n * pick helps you pick keys from an object more concisely.\n *\n * e.g. `pick({a: v.string(), b: v.number()}, [\"a\"])` is equivalent to\n * `{a: v.string()}`\n * The alternative could be something like:\n * ```js\n * const obj = { a: v.string(), b: v.number() };\n * // pick does the following\n * const { a } = obj;\n * const onlyA = { a };\n * ```\n *\n * @param obj The object to pick from. Often like { a: v.string() }\n * @param keys The keys to pick from the object.\n * @returns A new object with only the keys you picked and their values.\n */\nexport function pick(obj, keys) {\n return Object.fromEntries(Object.entries(obj).filter(([k]) => keys.includes(k)));\n}\n/**\n * omit helps you omit keys from an object more concisely.\n *\n * e.g. `omit({a: v.string(), b: v.number()}, [\"a\"])` is equivalent to\n * `{b: v.number()}`\n *\n * The alternative could be something like:\n * ```js\n * const obj = { a: v.string(), b: v.number() };\n * // omit does the following\n * const { a, ...rest } = obj;\n * const withoutA = rest;\n * ```\n *\n * @param obj The object to return a copy of without the specified keys.\n * @param keys The keys to omit from the object.\n * @returns A new object with the keys you omitted removed.\n */\nexport function omit(obj, keys) {\n return Object.fromEntries(Object.entries(obj).filter(([k]) => !keys.includes(k)));\n}\n/**\n * Removes the _id and _creationTime fields from an object.\n * This enables easily cloning a Convex document like:\n * ```ts\n * const doc = await db.get(id);\n * const clone = withoutSystemFields(doc);\n * await db.insert(table, clone);\n * ```\n * @param obj The object to remove the _id and _creationTime fields from.\n * @returns A new object with the _id and _creationTime fields removed.\n */\nexport function withoutSystemFields(obj) {\n return omit(obj, [\"_id\", \"_creationTime\"]);\n}\n// Type utils:\nconst _error = Symbol();\n/**\n * A utility to validate truthiness at runtime, providing a type guard\n *\n * @example\n * ```ts\n * const x: string | null = getValue();\n * assert(x);\n * // x is now of type string\n * ```\n * You can also provide a function, to avoid doing expensive string templating.\n * @param arg A value to assert the truthiness of.\n * @param message An optional message to throw if the value is not truthy, or a function to generate the message.\n */\nexport function assert(value, message) {\n if (!value) {\n throw new Error(typeof message === \"function\" ? message() : message);\n }\n}\n","import { asyncMap, nullThrows } from \"../index.js\";\nexport async function getOrThrow(ctx, arg1, arg2) {\n const [table, id] = arg2 !== undefined ? [arg1, arg2] : [null, arg1];\n const doc = table\n ? await ctx.db.get(table, id)\n : // eslint-disable-next-line @convex-dev/explicit-table-ids -- table not available here\n await ctx.db.get(id);\n if (!doc) {\n throw new Error(`Could not find id ${id}`);\n }\n return doc;\n}\nexport async function getAll(db, arg1, arg2) {\n const [table, ids] = arg2 !== undefined ? [arg1, arg2] : [null, arg1];\n return table\n ? asyncMap(ids, (id) => db.get(table, id))\n : asyncMap(ids, (id) => db.get(id));\n}\nexport async function getAllOrThrow(db, arg1, arg2) {\n const [table, ids] = arg2 !== undefined ? [arg1, arg2] : [null, arg1];\n if (table) {\n return await asyncMap(ids, (id) => getOrThrow({ db }, table, id));\n }\n else {\n return await asyncMap(ids, (id) => getOrThrow({ db }, id));\n }\n}\nfunction firstIndexField(index, field) {\n if (field)\n return field;\n if (index.startsWith(\"by_\"))\n return index.slice(3);\n return index;\n}\n/**\n * Get a document matching the given value for a specified field.\n *\n * `null` if not found.\n * Useful for fetching a document with a one-to-one relationship via backref.\n * Requires the table to have an index on the field named the same as the field.\n * e.g. `defineTable({ fieldA: v.string() }).index(\"fieldA\", [\"fieldA\"])`\n *\n * Getting 'string' is not assignable to parameter of type 'never'?\n * Make sure your index is named after your field.\n *\n * @param db DatabaseReader, passed in from the function ctx\n * @param table The table to fetch the target document from.\n * @param index The index on that table to look up the specified value by.\n * @param value The value to look up the document by, often an ID.\n * @param field The field on that table that should match the specified value.\n * Optional if the index is named after the field.\n * @returns The document matching the value, or null if none found.\n */\nexport async function getOneFrom(db, table, index, value, ...fieldArg) {\n const field = firstIndexField(index, fieldArg[0]);\n return db\n .query(table)\n .withIndex(index, (q) => q.eq(field, value))\n .unique();\n}\n/**\n * Get a document matching the given value for a specified field.\n *\n * Throws if not found.\n * Useful for fetching a document with a one-to-one relationship via backref.\n * Requires the table to have an index on the field named the same as the field.\n * e.g. `defineTable({ fieldA: v.string() }).index(\"fieldA\", [\"fieldA\"])`\n *\n * Getting 'string' is not assignable to parameter of type 'never'?\n * Make sure your index is named after your field.\n *\n * @param db DatabaseReader, passed in from the function ctx\n * @param table The table to fetch the target document from.\n * @param index The index on that table to look up the specified value by.\n * @param value The value to look up the document by, often an ID.\n * @param field The field on that table that should match the specified value.\n * Optional if the index is named after the field.\n * @returns The document matching the value. Throws if not found.\n */\nexport async function getOneFromOrThrow(db, table, index, value, ...fieldArg) {\n const field = firstIndexField(index, fieldArg[0]);\n const ret = await db\n .query(table)\n .withIndex(index, (q) => q.eq(field, value))\n .unique();\n return nullThrows(ret, `Can't find a document in ${table} with field ${field} equal to ${value}`);\n}\n/**\n * Get a list of documents matching the given value for a specified field.\n *\n * Useful for fetching many documents related to a given value via backrefs.\n * Requires the table to have an index on the field named the same as the field.\n * e.g. `defineTable({ fieldA: v.string() }).index(\"fieldA\", [\"fieldA\"])`\n *\n * Getting 'string' is not assignable to parameter of type 'never'?\n * Make sure your index is named after your field.\n *\n * @param db DatabaseReader, passed in from the function ctx\n * @param table The table to fetch the target document from.\n * @param index The index on that table to look up the specified value by.\n * @param value The value to look up the document by, often an ID.\n * @param field The field on that table that should match the specified value.\n * Optional if the index is named after the field.\n * @returns The documents matching the value, if any.\n */\nexport async function getManyFrom(db, table, index, value, ...fieldArg) {\n const field = firstIndexField(index, fieldArg[0]);\n return db\n .query(table)\n .withIndex(index, (q) => q.eq(field, value))\n .collect();\n}\n// many-to-many via lookup table\n/**\n * Get related documents by using a join table.\n *\n * Any missing documents referenced by the join table will be null.\n * It will find all join table entries matching a value, then look up all the\n * documents pointed to by the join table entries. Useful for many-to-many\n * relationships.\n *\n * Requires your join table to have an index on the fromField named the same as\n * the fromField, and another field that is an Id type.\n * e.g. `defineTable({ a: v.string(), b: v.id(\"users\") }).index(\"a\", [\"a\"])`\n *\n * Getting 'string' is not assignable to parameter of type 'never'?\n * Make sure your index is named after your field.\n *\n * @param db DatabaseReader, passed in from the function ctx\n * @param table The table to fetch the target document from.\n * @param toField The ID field on the table pointing at target documents.\n * @param index The index on the join table to look up the specified value by.\n * @param value The value to look up the documents in join table by.\n * @param field The field on the join table to match the specified value.\n * Optional if the index is named after the field.\n * @returns The documents targeted by matching documents in the table, if any.\n */\nexport async function getManyVia(db, table, toField, index, value, ...fieldArg) {\n return await asyncMap(await getManyFrom(db, table, index, value, ...fieldArg), async (link) => {\n const id = link[toField];\n try {\n // eslint-disable-next-line @convex-dev/explicit-table-ids -- table not available here\n return (await db.get(id));\n }\n catch {\n // eslint-disable-next-line @convex-dev/explicit-table-ids -- table not available here\n return (await db.system.get(id));\n }\n });\n}\n/**\n * Get related documents by using a join table.\n *\n * Throws an error if any documents referenced by the join table are missing.\n * It will find all join table entries matching a value, then look up all the\n * documents pointed to by the join table entries. Useful for many-to-many\n * relationships.\n *\n * Requires your join table to have an index on the fromField named the same as\n * the fromField, and another field that is an Id type.\n * e.g. `defineTable({ a: v.string(), b: v.id(\"users\") }).index(\"a\", [\"a\"])`\n *\n * Getting 'string' is not assignable to parameter of type 'never'?\n * Make sure your index is named after your field.\n *\n * @param db DatabaseReader, passed in from the function ctx\n * @param table The table to fetch the target document from.\n * @param toField The ID field on the table pointing at target documents.\n * @param index The index on the join table to look up the specified value by.\n * @param value The value to look up the documents in join table by.\n * @param field The field on the join table to match the specified value.\n * Optional if the index is named after the field.\n * @returns The documents targeted by matching documents in the table, if any.\n */\nexport async function getManyViaOrThrow(db, table, toField, index, value, ...fieldArg) {\n return await asyncMap(await getManyFrom(db, table, index, value, ...fieldArg), async (link) => {\n const id = link[toField];\n try {\n return nullThrows(\n // eslint-disable-next-line @convex-dev/explicit-table-ids -- table not available here\n (await db.get(id)), `Can't find document ${id} referenced in ${table}'s field ${toField} for ${fieldArg[0] ?? index} equal to ${value}`);\n }\n catch {\n return nullThrows(\n // eslint-disable-next-line @convex-dev/explicit-table-ids -- table not available here\n (await db.system.get(id)), `Can't find document ${id} referenced in ${table}'s field ${toField} for ${fieldArg[0] ?? index} equal to ${value}`);\n }\n });\n}\n","import { asyncMap } from \"convex-helpers\";\nimport { getAll } from \"convex-helpers/server/relationships\";\nimport type { GenericDataModel, GenericQueryCtx, TableNamesInDataModel } from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\nimport type { CollectionSlug } from \"../types/generated\";\nimport type { Populated, PopulateShape } from \"./types\";\n\n/**\n * Walks `docs` and replaces each relationship field listed in `populate` with\n * the resolved target doc(s). Returns a shallow-copied array; original docs\n * are not mutated.\n *\n * The `TPopulate` generic is the caller-supplied populate shape. The return\n * type `Populated<TCollectionSlug, TPopulate>[]` is what TypeScript sees at call sites\n * — the `as unknown as` cast at the bottom bridges runtime\n * `Record<string, unknown>[]` to the compile-time `Populated` shape. All\n * callers (`find`, `get`, `search` server functions) benefit without writing\n * their own casts.\n *\n * @param ctx - The Convex query context (any DataModel).\n * @param docs - Documents to populate.\n * @param populate - Relationship fields to resolve, optionally nested.\n * @returns Same docs with relationship Id arrays replaced by Doc arrays,\n * typed as `Populated<TCollectionSlug, TPopulate>[]`.\n */\nexport async function populateDocs<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug = CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = PopulateShape<TCollectionSlug>,\n>(\n ctx: GenericQueryCtx<DataModel>,\n docs: Record<string, unknown>[],\n populate: TPopulate,\n): Promise<Populated<TCollectionSlug, TPopulate>[]> {\n const result = await asyncMap(docs, async (doc) => {\n const out: Record<string, unknown> = { ...doc };\n for (const [fieldKey, opts] of Object.entries(populate)) {\n const ids = doc[fieldKey];\n if (!Array.isArray(ids)) continue;\n\n const targets = await getAll(ctx.db, ids as GenericId<TableNamesInDataModel<DataModel>>[]);\n const filtered = targets.filter((t): t is NonNullable<typeof t> => t !== null);\n\n if (\n typeof opts === \"object\" &&\n opts !== null &&\n \"populate\" in opts &&\n opts.populate &&\n filtered.length > 0\n ) {\n out[fieldKey] = await populateDocs(ctx, filtered, opts.populate);\n } else {\n out[fieldKey] = filtered;\n }\n }\n return out;\n });\n // Single cast: the runtime walk produces the correct shape that\n // Populated<TCollectionSlug, TPopulate>[] describes, but TypeScript can't verify\n // a mapped type transformation from a runtime Object.entries loop.\n return result as unknown as Populated<TCollectionSlug, TPopulate>[];\n}\n","/**\n * CRUD actions available on every resource subject (collections and globals).\n *\n * Draft actions ({@link DRAFT_ACTIONS}) are added conditionally when a resource\n * declares `versions.drafts: true`.\n */\nexport const CRUD_ACTIONS = {\n create: \"create\",\n read: \"read\",\n update: \"update\",\n delete: \"delete\",\n} as const;\n/** CRUD action union, derived from {@link CRUD_ACTIONS}. */\nexport type CrudAction = (typeof CRUD_ACTIONS)[keyof typeof CRUD_ACTIONS];\n\n/**\n * Draft workflow actions — present on a resource subject only when its config\n * declares `versions.drafts: true` (globals today; collections with Spec 36).\n */\nexport const DRAFT_ACTIONS = {\n readDrafts: \"readDrafts\",\n saveDraft: \"saveDraft\",\n publish: \"publish\",\n unpublish: \"unpublish\",\n} as const;\n/** Draft action union, derived from {@link DRAFT_ACTIONS}. */\nexport type DraftAction = (typeof DRAFT_ACTIONS)[keyof typeof DRAFT_ACTIONS];\n\n/**\n * Actions whose read shape is a query rather than a single-document check —\n * the only actions an indexed `{ filter, withIndex }` permission check may\n * target (design doc §3: `withIndex` on `create`/`update`/`delete` would\n * narrow nothing, since those authorize one document, not a range).\n */\nexport const QUERY_ACTIONS = {\n read: CRUD_ACTIONS.read,\n readDrafts: DRAFT_ACTIONS.readDrafts,\n} as const;\n/** Query-shaped action union, derived from {@link QUERY_ACTIONS}. */\nexport type QueryAction = (typeof QUERY_ACTIONS)[keyof typeof QUERY_ACTIONS];\n\n/**\n * Field-mode object modes: `allow` = only the listed fields, `deny` = all but\n * the listed fields.\n */\nexport const PERMISSION_MODES = {\n allow: \"allow\",\n deny: \"deny\",\n} as const;\n/** Field-mode object mode, derived from {@link PERMISSION_MODES}. */\nexport type PermissionMode = (typeof PERMISSION_MODES)[keyof typeof PERMISSION_MODES];\n\n/**\n * How `hasPermission` answers a *quantified* question: when no `data` is\n * supplied and a role's check is a callback that needs the document, the scope\n * decides what \"yes\" would even mean.\n *\n * - `doc` — \"may they do this to THIS document?\" `data` is required; a callback\n * that needs it and does not get it throws {@link VexAccessError}. Opt into\n * this in edit views and row actions, where a silent `false` would surface as\n * a permanently disabled control and read like a misconfigured matrix.\n * - `any` — \"may they do this to AT LEAST ONE document?\" Resolves such a callback\n * to `true` without invoking it. Use for nav/sidebar/list gating; per-document\n * filtering still happens downstream in `find`/`get`.\n * - `all` (default) — \"may they do this to EVERY document?\" Resolves such a\n * callback to `false`: a per-document condition cannot hold for all of them.\n * Fail-closed, so omitting `scope` never throws and never over-permits.\n *\n * Static boolean checks are unaffected by scope, and any scope evaluates the\n * callback normally once `data` is supplied.\n */\nexport const PERMISSION_SCOPES = {\n doc: \"doc\",\n any: \"any\",\n all: \"all\",\n} as const;\n/** Permission evaluation scope, derived from {@link PERMISSION_SCOPES}. */\nexport type PermissionScope = (typeof PERMISSION_SCOPES)[keyof typeof PERMISSION_SCOPES];\n\n/**\n * Wildcard key usable at two matrix levels: role level (`admin: { [WILDCARD_KEY]:\n * true }`, boolean only) and action level inside a per-action map (any\n * `PermissionCheck`). Precedence: explicit action > subject wildcard > role\n * wildcard > `defaults`.\n */\nexport const WILDCARD_KEY = \"*\" as const;\n\n/**\n * Built-in non-resource subjects contributed by core. Every entry becomes a\n * subject in the {@link SubjectMap} exactly like a user-declared custom\n * resource (no data, no fields).\n */\nexport const ADMIN_CUSTOM_SUBJECTS = {\n adminPanel: {\n key: \"adminPanel\",\n actions: [\"access\", \"impersonate\"],\n },\n} as const;\n\n/** Union of built-in subject slugs (currently `\"adminPanel\"`). */\nexport type AdminCustomSubjectSlug = keyof typeof ADMIN_CUSTOM_SUBJECTS;\n","import { CollectionConfig } from \"../types\";\nimport { GlobalConfig } from \"../globals\";\nimport type {\n AuthOrgDocument,\n AuthUserDocument,\n DocumentBySlug,\n GlobalDocumentBySlug,\n CollectionSlug,\n IndexFieldsBySlug,\n IndexNameFor,\n} from \"../types/generated\";\nimport {\n ADMIN_CUSTOM_SUBJECTS,\n WILDCARD_KEY,\n type PermissionMode,\n type CrudAction,\n type DraftAction,\n type AdminCustomSubjectSlug,\n QueryAction,\n} from \"./constants\";\nimport { ConvexError } from \"convex/values\";\nimport type { Expression, FilterBuilder, IndexRange, IndexRangeBuilder } from \"convex/server\";\nimport type {\n AccessConditionResult,\n DeclaredDoc,\n AccessPredicateBuilder,\n AccessQueryBuilder,\n} from \"./constraintTypes\";\n\n/**\n * Any config that may contribute a resource subject: a collection or a global.\n * Structural — the slug literal (and `versions.drafts`, when present) is all\n * the type system reads from it.\n */\nexport type AccessResource = CollectionConfig | GlobalConfig;\n\n/**\n * Single permission check result — boolean shorthand (all/none) or a\n * field-mode object restricting the check to named fields.\n *\n */\n\n/**\n * Props passed to a permission callback.\n *\n * The `data` key exists only for data-carrying subjects; the `organization`\n * key exists only when `orgCollectionSlug` is configured. Built with\n * intersections (not conditional property types) so the keys are truly\n * absent — not present-but-`never` — when unavailable.\n *\n * @typeParam TData - Document type for the subject; `never` when the subject has no data.\n * @typeParam TUser - User document shape (registry lookup on the user collection slug).\n * @typeParam TOrg - Organization document shape; `never` when not configured.\n */\nexport type PermissionCallbackProps<\n TData = unknown,\n TUser = Record<string, unknown>,\n TOrg = Record<string, unknown>,\n> = {\n user: TUser;\n} & ([TData] extends [never] ? unknown : { data: TData }) &\n ([TOrg] extends [never] ? unknown : { organization: TOrg });\n\n/**\n * The plain permission check shapes shared by every action: a static boolean or a\n * callback. The object form is {@link ConstrainedPermissionCheck}; this is the leaf a\n * rule's optional `filter` property also accepts.\n *\n * A callback returning `undefined` is treated as deny — \"inconclusive\" must never\n * read as an implicit allow. @internal\n */\ntype BasePermissionCheck<TData, TUser, TOrg> =\n | boolean\n | ((props: PermissionCallbackProps<TData, TUser, TOrg>) => boolean | undefined);\n\n/** A range callback as applied to a Convex query. @internal */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type IndexRangeFn = (q: IndexRangeBuilder<any, any, 0>) => IndexRange;\n\n/**\n * A compiled access filter as applied to a Convex query — the output of\n * `resolveAccessConstraint`.\n *\n * A thunk rather than an `Expression<boolean>` because a filter expression can only\n * be built from the query's OWN `FilterBuilder`, which the resolver never sees: the\n * caller supplies it inside `.filter((q) => …)`. Mirrors {@link IndexRangeFn}, which\n * is deferred for the same reason and uses the same `any` table-info convention.\n *\n * @internal\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type AccessFilterFn = (q: FilterBuilder<any>) => Expression<boolean>;\n\n/**\n * A concrete index a query will use: the output of `resolveAccessIndex` and,\n * after arbitration, of `pickQueryIndex`. Both stages produce the same shape,\n * so there is one type for both.\n *\n * Distinct from what the user *authors*: a rule declares its index inside\n * `q.withIndex(name, (ix) => …)`, whose range is a function **of the caller**.\n * Resolving one binds the caller in, leaving the plain\n * `(q) => …` builder here and widening `name` to `string` — by resolve time\n * the resource generic is gone. `AccessIndex` is the template; this is that\n * template applied to one caller.\n *\n * `range` is optional because after arbitration the winner may be a *caller's*\n * `withIndex`, and a caller may legitimately name an index with no range purely\n * to order results. `buildQuery` already branches on this\n * (`api/find/server.ts` — `range ? withIndex(name, range) : withIndex(name)`),\n * so an absent range needs no placeholder.\n *\n * An access-sourced index always carries one, guaranteed by\n * {@link AccessQueryBuilder.withIndex} requiring its range callback rather than\n * restated as a second type here — a range-less access index would scan the whole\n * table, since every document has an index entry (fields that are missing are\n * indexed as `undefined`).\n */\nexport interface QueryIndex {\n /** Index name to query. */\n name: string;\n /** Range to apply; omitted for an ordering-only caller index. */\n range?: IndexRangeFn;\n}\n\n/**\n * Props for a rule's `constraints` callback: the caller, optionally the\n * organization, and `q` — the builder the rule records onto. No `data`:\n * constraints run once per query, before any document is read (contrast\n * {@link PermissionCallbackProps}, which the sibling `filter` property uses).\n *\n * `q`'s TYPE is what gates index pushdown per action (DD 14). A query-shaped\n * action gets an {@link AccessQueryBuilder} — `filter` plus `withIndex`; every\n * other action gets an {@link AccessPredicateBuilder}, the same shape minus\n * `withIndex`, so a rule reads identically either way and `q.withIndex` is simply\n * absent where there is no query to narrow. One property, two builder types, and\n * nothing to discriminate at the object level.\n *\n * @typeParam TData - Document type constraints are typed against.\n * @typeParam TUser - User document shape.\n * @typeParam TOrg - Organization document shape; `never` if not configured.\n * @typeParam TQ - The builder for this action: `AccessQueryBuilder` on query\n * actions, `AccessPredicateBuilder` on mutations.\n */\nexport type ConstraintsCallbackProps<\n TData = unknown,\n TUser = Record<string, unknown>,\n TOrg = Record<string, unknown>,\n TQ = AccessPredicateBuilder<TData>,\n> = {\n user: TUser;\n q: TQ;\n} & ([TOrg] extends [never] ? unknown : { organization: TOrg });\n\n/**\n * A rule's constraint-recording callback.\n *\n * ONE signature returning `boolean | ConstraintResult`, never a union of two\n * function types: verified against `tsc` that a union of differently-shaped\n * callbacks breaks contextual typing of the destructured `props` — every\n * parameter infers `any` instead of TypeScript picking a member. `boolean` is\n * primitive and `ConstraintResult` is nominal, so discriminating the RESULT at\n * runtime (`typeof result === \"boolean\"`) stays unambiguous.\n *\n * @typeParam TData - Document type constraints are typed against.\n * @typeParam TUser - User document shape.\n * @typeParam TOrg - Organization document shape; `never` if not configured.\n * @typeParam TQ - The builder for this action.\n */\nexport type ConstraintsCallback<\n TData = unknown,\n TUser = Record<string, unknown>,\n TOrg = Record<string, unknown>,\n TQ = AccessPredicateBuilder<TData>,\n> = (props: ConstraintsCallbackProps<TData, TUser, TOrg, TQ>) => boolean | AccessConditionResult;\n\n/**\n * The constraint-builder object form of a permission check — ONE shape for every\n * action.\n *\n * `constraints` narrows what gets read: compiled to a `withIndex` range when the\n * rule called `q.withIndex(…)`, otherwise to a `.filter()` expression, and in\n * either case interpreted per-document as a JS predicate\n * (`compileConstraints`). `filter` is an OPTIONAL additional per-document check\n * for what constraints cannot express — array membership, string operations,\n * cross-table reads, all outside `FilterBuilder`'s surface, so they stay\n * callbacks permanently.\n *\n * `filter` augments `constraints`; it never replaces it. A rule that declares\n * only `constraints` is already checked per-document too, via\n * `constraintsToPredicate` in `hasPermission`. A bare `filter`-only shape is\n * therefore rejected — a callback with no descriptor cannot narrow a query, so\n * write it as a bare callback instead and accept the full scan knowingly.\n *\n * Index pushdown is opted into INSIDE the callback\n * ({@link AccessQueryBuilder.withIndex}), not by a sibling property. That is\n * what lets `q` bind to one index's real field tuple and check field ORDER at\n * compile time; a sibling property cannot be seen by its neighbour's callback\n * type.\n *\n * @typeParam TData - Document type for the subject.\n * @typeParam TUser - User document shape.\n * @typeParam TOrg - Organization document shape; `never` if not configured.\n * @typeParam TQ - The builder for this action.\n */\nexport interface ConstrainedPermissionCheck<\n TData = unknown,\n TUser = Record<string, unknown>,\n TOrg = Record<string, unknown>,\n TQ = AccessPredicateBuilder<TData>,\n> {\n /** Narrows what gets read. Required — see the type doc. */\n constraints: ConstraintsCallback<TData, TUser, TOrg, TQ>;\n /** Optional per-document check augmenting `constraints`. Never a substitute. */\n filter?: BasePermissionCheck<TData, TUser, TOrg>;\n}\n\n/**\n * Every check shape valid on **any** action, query-shaped or not: the plain leaf\n * shapes plus the constraint-builder object form (DD 14).\n *\n * This is the tier `RolePermissions` hands a non-query action. The only difference\n * from {@link PermissionCheck} is `q`'s type: a query action's `q` has `withIndex`,\n * this one's does not.\n *\n * **Why `ConstrainedPermissionCheck` sits here rather than inside\n * {@link BasePermissionCheck}.** Both are now valid on every action, so folding\n * them looks tempting. It cannot be done: `BasePermissionCheck` is also the type of\n * the `filter` property *inside* both object forms, so folding the composite into\n * it makes `filter` able to hold another whole constrained check —\n * `{ constraints, filter: { constraints, filter: … } }` — an infinite regress the\n * compiler accepts and `hasPermission`'s resolver has no meaning for. `filter` is\n * the per-document escape hatch for what constraints cannot express; a constraints\n * object is not a leaf check. \"Base\" stays the irreducible shapes — a value or a\n * function — and the composites point at it, never the other way round.\n *\n * @typeParam TData - Document type for the subject.\n * @typeParam TUser - User document shape.\n * @typeParam TOrg - Organization document shape; `never` if not configured.\n */\nexport type AnyActionPermissionCheck<\n TData = unknown,\n TUser = Record<string, unknown>,\n TOrg = Record<string, unknown>,\n> =\n | BasePermissionCheck<TData, TUser, TOrg>\n | ConstrainedPermissionCheck<TData, TUser, TOrg, AccessPredicateBuilder<TData>>;\n\n/**\n * A single permission check on a **query-shaped** action: the plain leaf shapes,\n * and the constraint form with `q` upgraded to an {@link AccessQueryBuilder} so\n * `q.withIndex(…)` is available.\n *\n * The ONLY difference from {@link AnyActionPermissionCheck} is `q`'s type. Index\n * pushdown is gated by giving a query action a builder\n * that HAS `withIndex` and a mutation one that does not — so writing\n * `q.withIndex(…)` on a create is a missing-method error at the exact call, rather\n * than a whole-object shape rejection pointing at the wrong line (DD 14).\n *\n * A callback returning `undefined` is treated as deny.\n *\n * @typeParam TData - Document type for the subject.\n * @typeParam TUser - User document shape.\n * @typeParam TOrg - Organization document shape; `never` if not configured.\n * @typeParam TIndexFields - The resource's index name → field tuple map, which\n * `q.withIndex` resolves against.\n */\nexport type PermissionCheck<\n TData = unknown,\n TUser = Record<string, unknown>,\n TOrg = Record<string, unknown>,\n TIndexFields extends Record<string, readonly string[]> = Record<string, readonly string[]>,\n> =\n | BasePermissionCheck<TData, TUser, TOrg>\n | ConstrainedPermissionCheck<TData, TUser, TOrg, AccessQueryBuilder<TData, TIndexFields>>;\n\n/**\n * One entry in the subject registry: the action union, the data shape passed\n * to callbacks, and the index registry `q.withIndex` resolves against.\n */\nexport interface SubjectEntry {\n /** Union of actions this subject supports. */\n action: string;\n /** Actions on this subject whose `q` carries `withIndex`. `never` when none do. */\n queryAction: string;\n /** Document/context type; `never` for subjects without data. */\n data: unknown;\n /** Union of access-index names declared on this resource; `never` for non-indexable subjects. */\n indexes: string;\n /**\n * The resource's declared indexes as name → field tuple, in declaration order.\n * `{}` for subjects with no table to index (custom resources, admin subjects),\n * which makes `q.withIndex` uncallable there rather than absent.\n *\n * Carries TUPLES, not just the names in `indexes`, because\n * {@link AccessQueryBuilder.withIndex} binds `q` to one index's real field order\n * — that is what makes positional constraint typing reachable.\n */\n indexFields: Record<string, readonly string[]>;\n}\n\n// ── Inference helpers (registry-based via GeneratedVexTypes augmentation) ──\n\n/** Extract the slug literal from a resource config. @internal */\ntype ExtractSlug<T> = T extends { slug: infer S extends string } ? S : never;\n\n/**\n * Document type for a slug via the generated registry (collections, then\n * globals; wide fallback pre-generation). @internal\n */\ntype InferDocTypeFromSlug<S extends string> = S extends keyof DocumentBySlug\n ? DocumentBySlug[S]\n : S extends keyof GlobalDocumentBySlug\n ? GlobalDocumentBySlug[S]\n : Record<string, unknown>;\n\n/**\n * Document type for a resource config via its slug literal. @internal\n */\ntype InferDocType<T> = T extends { slug: infer S extends string }\n ? InferDocTypeFromSlug<S>\n : Record<string, unknown>;\n\n/**\n * The document type a permission callback receives for resource `S`.\n *\n * Exported because a project writing its own composable access checks has to name\n * this type. Accepts any slug string: {@link InferDocTypeFromSlug} resolves\n * collections, then globals, then falls back wide, so a global slug works here too.\n *\n * @typeParam S - Resource slug.\n */\nexport type AccessDocFor<S extends string> = DeclaredDoc<InferDocTypeFromSlug<S>>;\n\n/**\n * Resource `S`'s index name → field tuple map, which `q.withIndex` resolves against.\n *\n * @typeParam S - Resource slug.\n */\nexport type AccessIndexFieldsFor<S extends string> = S extends keyof IndexFieldsBySlug\n ? IndexFieldsBySlug[S]\n : Record<string, readonly string[]>;\n\n/**\n * The exact check type `permissions[role][S][queryAction]` accepts.\n *\n * This is the return type for a project-defined helper on a query-shaped action\n * (`read`, `readDrafts`) — the helper hands back a check, so nothing wraps it and the\n * call site reads identically to writing the check inline.\n *\n * @typeParam S - Resource slug.\n * @typeParam TUser - The project's user document type.\n * @typeParam TOrg - The project's organization document type, or `never`.\n */\n/**\n * The field union a helper can use to READ a value off `AccessDocFor<S>`.\n *\n * A bare `keyof`, deliberately: a parameter bounded by it is *provably* a key of the\n * document, so `data[field]` needs no cast. Wrapping it in a widening conditional —\n * \"fall back to `string` when the registry is unaugmented\" — breaks exactly that,\n * because TypeScript cannot prove an unreduced conditional is a key, and the read\n * fails with \"Type 'F' cannot be used to index type\".\n *\n * The cost of staying indexable: for a slug the registry does not know, the document\n * is the wide fallback whose index signature `DeclaredDoc` strips, leaving this\n * `never`. A field-reading helper therefore only types after `vex generate` — already\n * true of the rest of authoring an access config, since slugs and index names come\n * from the same registry.\n *\n * @typeParam S - Resource slug.\n */\nexport type AccessDocFieldFor<S extends string> = keyof AccessDocFor<S> & string;\n\n/**\n * Fields on `S` that LEAD a declared index.\n *\n * Only a leading field can open an index range, so a helper that pushes a comparison\n * into an index must restrict its field argument to these — otherwise it silently\n * degrades to a full scan.\n *\n * @typeParam S - Resource slug.\n */\nexport type AccessIndexedFieldFor<S extends string> = {\n [N in keyof AccessIndexFieldsFor<S>]: AccessIndexFieldsFor<S>[N] extends readonly [\n infer F,\n ...unknown[],\n ]\n ? F\n : never;\n}[keyof AccessIndexFieldsFor<S>] &\n string;\n\n/**\n * The index on `S` whose leading field is `F`.\n *\n * Lets a helper's runtime index lookup be typed as the index name it resolves to,\n * rather than a bare `string` that `q.withIndex` refuses.\n *\n * @typeParam S - Resource slug.\n * @typeParam F - A field that leads an index on `S`.\n */\nexport type AccessIndexNameFor<S extends string, F> = {\n [N in keyof AccessIndexFieldsFor<S>]: AccessIndexFieldsFor<S>[N] extends readonly [\n F,\n ...unknown[],\n ]\n ? N\n : never;\n}[keyof AccessIndexFieldsFor<S>] &\n string;\n\n/**\n * The stored value type of field `F` on `S`.\n *\n * @typeParam S - Resource slug.\n * @typeParam F - Field name.\n */\nexport type AccessFieldValueFor<S extends string, F> = F extends keyof AccessDocFor<S>\n ? AccessDocFor<S>[F]\n : never;\n\n/**\n * Indexed fields on `S` whose stored value admits `V`.\n *\n * Narrows a value-specific helper — a \"published only\" read, say — to the fields that\n * can actually hold that value, so naming the wrong field is a compile error rather\n * than a comparison that silently matches nothing. Handles both a scalar field and an\n * array-valued one (`select` and relationship fields store arrays).\n *\n * @typeParam S - Resource slug.\n * @typeParam V - The value the field must be able to hold.\n */\nexport type AccessIndexedFieldWithValue<S extends string, V> = {\n [F in AccessIndexedFieldFor<S>]: NonNullable<\n AccessFieldValueFor<S, F>\n > extends readonly (infer E)[]\n ? [V] extends [E]\n ? F\n : never\n : [V] extends [NonNullable<AccessFieldValueFor<S, F>>]\n ? F\n : never;\n}[AccessIndexedFieldFor<S>];\n\n/**\n * What a check builder needs from a resource config: the slug, to bind `S`, and the\n * fields, to recover an index NAME from a field name at runtime.\n *\n * Structural rather than `CollectionConfig` so a `GlobalConfig` satisfies it too.\n *\n * @typeParam S - Resource slug.\n */\nexport type AccessResourceRef<S extends string> = {\n readonly fields: Readonly<Record<string, { readonly index?: string; readonly type?: string }>>;\n readonly slug: S;\n};\n\n/**\n * The check type `permissions[role][S][queryAction]` accepts, with the project's user\n * and organization documents resolved from the generated registry.\n *\n * `vex generate` emits the slugs `defineAccess` was configured with\n * ({@link AuthSlugs}), so the project's user and organization documents resolve from\n * the registry — nothing needs passing in, and a project's helpers need no local\n * type aliases at all. `AccessMutationCheck` is the non-query counterpart: its `q`\n * is an {@link AccessPredicateBuilder}, no `withIndex`, because a single-document\n * authorization has no range to narrow (DD 14).\n *\n * @typeParam S - Resource slug.\n */\nexport type AccessCheck<S extends string> = PermissionCheck<\n AccessDocFor<S>,\n AuthUserDocument,\n AuthOrgDocument,\n AccessIndexFieldsFor<S>\n>;\n\n/**\n * The check type a NON-query action (`create`/`update`/`delete`, custom actions)\n * accepts, with user and organization resolved from the registry.\n *\n * @typeParam S - Resource slug.\n */\nexport type AccessMutationCheck<S extends string> = AnyActionPermissionCheck<\n AccessDocFor<S>,\n AuthUserDocument,\n AuthOrgDocument\n>;\n\n/**\n * Index-name union for a resource config via its slug literal, from the\n * generated {@link IndexNameFor} registry (wide `string` fallback\n * pre-generation). @internal\n */\ntype ExtractIndexNames<T> = T extends { slug: infer S extends string } ? IndexNameFor<S> : string;\n\n/**\n * The declared-index field-tuple map for a resource config's slug — the shape\n * {@link AccessQueryBuilder.withIndex} resolves against. `{}` pre-generation, so\n * `withIndex` accepts nothing rather than accepting anything. @internal\n */\ntype ExtractIndexFields<T> = T extends { slug: infer S extends string }\n ? S extends keyof IndexFieldsBySlug\n ? IndexFieldsBySlug[S]\n : PreGenerationIndexFields\n : PreGenerationIndexFields;\n\n/**\n * Pre-generation fallback for {@link ExtractIndexFields}: any index name, and a\n * FIXED-LENGTH tuple of eight `string` slots per index.\n *\n * The length matters more than it looks. `ConstraintBuilder` terminates its chain\n * on `PlusOne<N> extends TFields[\"length\"]`, and a plain `readonly string[]` has\n * `length: number` — which that test treats as already satisfied, so the chain would\n * end after ONE constraint. A fixed-length tuple keeps `length` a numeric literal,\n * so chaining behaves the same before and after `vex generate`; only field NAMES and\n * per-field value types widen. Eight comfortably exceeds any realistic compound\n * index. @internal\n */\ntype PreGenerationIndexFields = Record<\n string,\n readonly [string, string, string, string, string, string, string, string]\n>;\n\n/** True when a resource config declares `versions.drafts: true`. @internal */\ntype HasDrafts<T> = T extends { versions?: { drafts?: infer D extends boolean } }\n ? D extends true\n ? true\n : false\n : false;\n\n/**\n * Subject entry synthesized for the user/organization collections.\n *\n * Resolved from the slug via the generated registry, so these subjects exist with\n * full document and index typing WITHOUT the collection being passed in\n * `resources` — the adapter owns those tables and merges them later, inside\n * `defineConfig`. CRUD-only: auth tables are never versioned. @internal\n */\ntype AuthSubjectEntry<S extends string, TCustomActions> = {\n action:\n | CrudAction\n | CustomActionsFor<S, TCustomActions>[\"query\"]\n | CustomActionsFor<S, TCustomActions>[\"mutation\"];\n queryAction: QueryAction | CustomActionsFor<S, TCustomActions>[\"query\"];\n data: DeclaredDoc<InferDocTypeFromSlug<S>>;\n indexes: IndexNameFor<S>;\n indexFields: AccessIndexFieldsFor<S>;\n};\n\n/**\n * The user subject, plus the organization subject when one is configured.\n *\n * The `string extends S` guards are load-bearing: for a non-literal slug the mapped\n * type would become an INDEX SIGNATURE, making every subject key valid in\n * `RolePermissions` and silently destroying typo-checking across the whole matrix.\n * @internal\n */\ntype AuthSubjects<\n TUserSlug extends CollectionSlug,\n TOrgSlug extends CollectionSlug | undefined,\n TCustomActions,\n> = (string extends TUserSlug\n ? unknown\n : { [K in TUserSlug]: AuthSubjectEntry<K, TCustomActions> }) &\n (TOrgSlug extends string\n ? string extends TOrgSlug\n ? unknown\n : { [K in TOrgSlug]: AuthSubjectEntry<K, TCustomActions> }\n : unknown);\n\n/**\n * The complete subject registry: resources (keyed by slug, CRUD + conditional\n * draft actions), custom resources, and the core built-in subjects from\n * {@link ADMIN_CUSTOM_SUBJECTS}.\n *\n * @typeParam TResources - Structural resource tuple (`{ slug, versions? }`).\n * @typeParam TCustomResources - Custom resource declarations.\n * @typeParam TUserSlug - User collection slug\n * @typeParam TOrgSlug - Organization collection slug\n * @typeParam TCustomActions extends Record<string, CustomActionsInput> = {}, // NEW\n */\nexport type SubjectMap<\n TResources extends readonly AccessResource[] = AccessResource[],\n TCustomResources extends Record<string, CustomResourceInput> = Record<\n string,\n CustomResourceInput\n >,\n TUserSlug extends CollectionSlug = CollectionSlug,\n TOrgSlug extends CollectionSlug | undefined = undefined,\n TCustomActions extends Partial<\n Record<TResources[number][\"slug\"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>\n > = {},\n> = {\n [R in TResources[number] as ExtractSlug<R>]: {\n action:\n | CrudAction\n | (HasDrafts<R> extends true ? DraftAction : never)\n | CustomActionsFor<ExtractSlug<R>, TCustomActions>[\"query\"]\n | CustomActionsFor<ExtractSlug<R>, TCustomActions>[\"mutation\"];\n queryAction: QueryAction | CustomActionsFor<ExtractSlug<R>, TCustomActions>[\"query\"];\n // Index signature stripped: generated documents extend `VexDocument`, whose\n // `[key: string]: unknown` otherwise makes `data.anythingAtAll` read as `unknown`\n // in every permission callback instead of erroring.\n data: DeclaredDoc<InferDocType<R>>;\n indexes: ExtractIndexNames<R>;\n indexFields: ExtractIndexFields<R>;\n };\n} & {\n [K in keyof TCustomResources]: {\n action: TCustomResources[K][\"actions\"][number];\n queryAction: never;\n data: TCustomResources[K][\"data\"] extends DataTypeCarrier<infer D> ? D : never;\n indexes: never;\n indexFields: {};\n };\n} & {\n [K in AdminCustomSubjectSlug]: {\n action: (typeof ADMIN_CUSTOM_SUBJECTS)[K][\"actions\"][number];\n queryAction: never;\n data: never;\n indexes: never;\n indexFields: {};\n };\n} & Omit<\n AuthSubjects<TUserSlug, TOrgSlug, TCustomActions>,\n ExtractSlug<TResources[number]> | keyof TCustomResources | AdminCustomSubjectSlug\n >;\n\n/**\n * Phantom carrier for a custom resource's `data` type. Created by\n * {@link dataType}; never inspected at runtime.\n */\nexport interface DataTypeCarrier<T = never> {\n readonly __phantom?: T;\n}\n\n/**\n * Declares the data type callbacks (and `hasPermission` callers) receive for a\n * custom resource.\n *\n * @example\n * ```ts\n * customResources: {\n * reviews: { actions: [\"approve\", \"reject\"], data: dataType<{ queue: string }>() },\n * }\n * ```\n * @returns a plain object '{}'\n */\nexport function dataType<T>(): DataTypeCarrier<T> {\n return {};\n}\n\n/** Custom actions for one resource, split by whether `q` gets `withIndex`. */\nexport type CustomActionsInput = {\n query?: readonly string[];\n mutation?: readonly string[];\n};\n\n/** The custom query/mutation action unions declared for slug `S`. @internal */\ntype CustomActionsFor<S extends string, TCA> = S extends keyof TCA\n ? {\n // Each list is matched INDEPENDENTLY, against a REQUIRED property. Matching\n // both at once against optional properties fails wholesale when either list\n // is omitted - and `infer Q` then falls back to its `string` constraint,\n // widening that resource's entire action union and silently disabling\n // typo-checking for it (while every other resource keeps it). Declaring only\n // `mutation` is the common case, so the naive form breaks the default path.\n query: TCA[S] extends { query: readonly (infer Q extends string)[] } ? Q : never;\n mutation: TCA[S] extends { mutation: readonly (infer M extends string)[] } ? M : never;\n }\n : { query: never; mutation: never };\n\n/**\n * A custom (non-collection) subject declaration: its action list and an\n * optional typed data carrier. One canonical form — no array shorthand.\n */\nexport type CustomResourceInput = {\n actions: readonly string[];\n data?: DataTypeCarrier<unknown>;\n};\n\n/**\n * Per-role permission matrix, typed against the resolved {@link SubjectMap}.\n *\n * Each subject key accepts `boolean` (all actions) or a per-action map whose\n * keys are that subject's action union plus the action-level wildcard\n * ({@link WILDCARD_KEY}).\n *\n * **Only `withIndex` is gated on {@link QueryAction}** (DD 14). A query-shaped\n * action gets the full {@link PermissionCheck}, whose `q` carries `withIndex` —\n * there is a query to narrow.\n * Every other action gets the plain shapes plus\n * {@link ConstrainedPermissionCheck}: a create/update/delete has no query to\n * narrow, but its constraints are still meaningful, interpreted per-document via\n * `constraintsToPredicate`. Restricting the object form to query actions is what\n * used to force a read+update pair to express one predicate twice — once as a\n * constraint, once as a hand-written callback — which is the dual-expression\n * footgun this design removes.\n *\n * The action-level wildcard stays plain: it spans actions of mixed shape, so a\n * constraint written there could not be typed against one document consistently.\n * The role-level wildcard is boolean-only.\n * Precedence: explicit action > subject wildcard > role wildcard > `defaults`.\n *\n * @typeParam TSubjects - The resolved {@link SubjectMap}.\n * @typeParam TUser - User document shape.\n * @typeParam TOrg - Organization document shape, or `never`.\n */\nexport type RolePermissions<\n TSubjects extends Record<string, SubjectEntry>,\n TUser = Record<string, unknown>,\n TOrg = never,\n TUserSlug extends CollectionSlug = CollectionSlug,\n TOrgSlug extends CollectionSlug | undefined = never,\n> = {\n [S in keyof TSubjects | TUserSlug | (TOrgSlug extends string ? TOrgSlug : never)]?:\n | boolean\n | ({\n // Gate order is load-bearing, not stylistic. A conditional whose `extends`\n // clause references a type parameter with no inference candidate yet\n // (`customActions` omitted) stays DEFERRED, and a deferred conditional\n // supplies no contextual type - silently turning every implicitly-typed\n // permission callback into an implicit `any`. Testing the closed built-in\n // sets first (plus the wildcard key, which contextual lookup probes against\n // this same template) means only genuinely custom action names reach the\n // `queryAction` branch - and those exist only when `customActions` was\n // declared, at which point the parameter has a candidate and resolves.\n [A in TSubjects[S][\"action\"]]?: A extends QueryAction\n ? PermissionCheck<TSubjects[S][\"data\"], TUser, TOrg, TSubjects[S][\"indexFields\"]>\n : A extends Exclude<CrudAction | DraftAction, QueryAction> | typeof WILDCARD_KEY\n ? AnyActionPermissionCheck<TSubjects[S][\"data\"], TUser, TOrg>\n : A extends TSubjects[S][\"queryAction\"]\n ? PermissionCheck<TSubjects[S][\"data\"], TUser, TOrg, TSubjects[S][\"indexFields\"]>\n : AnyActionPermissionCheck<TSubjects[S][\"data\"], TUser, TOrg>;\n } & {\n [W in typeof WILDCARD_KEY]?: AnyActionPermissionCheck<TSubjects[S][\"data\"], TUser, TOrg>;\n });\n} & {\n /** Role-level wildcard: covers subjects this role never declares. Boolean only. */\n [W in typeof WILDCARD_KEY]?: boolean;\n};\n\n/**\n * Input shape for the `defineAccess` builder.\n *\n * @typeParam TRoles - Tuple of role name literals.\n * @typeParam TResources - Structural resource tuple (`{ slug, versions? }`).\n * @typeParam TCustomResources - Custom resource declarations.\n * @typeParam TUserCollection - `{ slug }` shape naming the user collection.\n * @typeParam TOrgCollection - `{ slug }` shape naming the org collection; `undefined` if absent.\n *\n * @see {@link VexAccessConfig} for the resolved runtime shape.\n */\nexport interface VexAccessConfigInput<\n TRoles extends readonly string[],\n TResources extends readonly AccessResource[] = readonly AccessResource[],\n TCustomResources extends Record<string, CustomResourceInput> = {},\n TUserSlug extends CollectionSlug = CollectionSlug,\n TOrgSlug extends CollectionSlug | undefined = undefined,\n TCustomActions extends Partial<\n Record<TResources[number][\"slug\"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>\n > = {},\n> {\n /** Default: `true`. Turn access control on or off. */\n enabled?: boolean;\n\n /**\n * OPTIONAL. Role applied when a caller's roles resolve empty — no session,\n * or an anonymous user (e.g. Better Auth anonymous plugin) whose\n * `userRolesField` is unset. Explicit roles always win over this fallback.\n * Omitted → empty roles deny, exactly as before.\n */\n anonRole?: TRoles[number];\n\n /** Role identifiers; keys of the `permissions` matrix. */\n roles: TRoles;\n\n /** Collections/globals contributing subjects, keyed by slug. */\n resources: TResources;\n\n /**\n * Extra actions per resource, beyond CRUD. Keys must be declared resource slugs\n * (or the user/org collection slugs).\n *\n * Three constituents, each load-bearing:\n *\n * 1. `TCustomActions` — the inference site; carries the caller's literal declaration\n * into the phantom `SubjectMap`.\n * 2. `Partial<Record<slugs, …>>` — the COMPLETION source. Key completions come from\n * the property's contextual type, and a bare unresolved type parameter offers\n * nothing; this constituent names the slug union directly, which is resolvable in\n * the first inference round (`resources` and the slugs are not context-sensitive).\n * Redundant for checking — the bound already constrains values.\n * 3. The exactness map — the TYPO guard. The `Partial<Record<…>>` bound cannot reject\n * a bad key on its own: an all-optional target is a \"weak type\", so TypeScript\n * errors only when the object shares ZERO keys with it — one valid entry beside a\n * garbage key passes, because structural subtyping permits extra properties and\n * per-key excess checking does not fire against a generic-constrained inference.\n * Mapping every key NOT in the slug union to `never` makes the garbage entry's\n * value unassignable AT ITS OWN KEY, restoring both the error and its location.\n */\n customActions?: TCustomActions &\n Partial<\n Record<TResources[number][\"slug\"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>\n > & {\n [K in Exclude<\n keyof TCustomActions,\n TResources[number][\"slug\"] | TUserSlug | Extract<TOrgSlug, string>\n >]: never;\n };\n\n /**\n * Custom, non-resource subjects with arbitrary action unions and optional\n * typed data. Example: `{ apiKeys: { actions: [\"create\", \"revoke\"] } }`.\n */\n customResources?: TCustomResources;\n\n /**\n * Slug of the collection whose documents are `user` in callbacks. A plain\n * slug string — the full collection often does not exist at authoring time\n * (auth-adapter collections merge later, inside `defineConfig`); the\n * document type resolves from the generated registry by slug.\n */\n userCollectionSlug: TUserSlug;\n\n /**\n * REQUIRED. The field on the user document that holds the user's role(s).\n * Value may be `string` or `string[]`; `hasPermission` normalizes both.\n * Callers never pass roles separately — they always ride the user document.\n */\n userRolesField: string;\n\n /**\n * Slug of the organization collection. When present, `organization` is\n * available (typed via the registry) in every permission callback; when\n * omitted, callbacks have no `organization` key.\n */\n orgCollectionSlug?: TOrgSlug;\n\n /**\n * Permission matrix: role → subject → check. See {@link RolePermissions}\n * for shapes and wildcard semantics.\n */\n permissions: Record<\n TRoles[number],\n RolePermissions<\n SubjectMap<TResources, TCustomResources, TUserSlug, TOrgSlug, TCustomActions>,\n InferDocTypeFromSlug<TUserSlug>,\n TOrgSlug extends string ? InferDocTypeFromSlug<TOrgSlug> : never,\n TUserSlug,\n TOrgSlug\n >\n >;\n}\n\n/**\n * Resolved access configuration returned by `defineAccess` — the runtime\n * shape consumed by `hasPermission`.\n *\n * Deliberately VALUE-LEVEL TYPE-ERASED: every call-site guarantee\n * (`resource`/`action` unions, callback `data` types, field keys) rides the\n * phantom `TSubjects` parameter, while the stored fields are wide. This is\n * what lets any concrete config assign to plain `VexAccessConfig` (e.g. the\n * `access` field on `VexConfig`) — a fully-generic config type would be\n * unassignable to any common supertype, because permission callbacks are\n * contravariant in their `data` parameter.\n *\n * @typeParam TSubjects - Phantom {@link SubjectMap} carried for `hasPermission` inference.\n */\nexport interface VexAccessConfig<\n TSubjects extends Record<string, SubjectEntry> = Record<string, SubjectEntry>,\n TResources extends readonly AccessResource[] = readonly AccessResource[],\n TUserSlug extends CollectionSlug = CollectionSlug,\n // Defaults to the FULL union, not `undefined`. `VexConfig.access` and every helper that\n // accepts an already-resolved config name this type bare, so its defaults define the wide\n // supertype every concrete config must satisfy. `orgCollectionSlug?: TOrgSlug` puts the\n // parameter in the body, so a default of `undefined` made `orgCollectionSlug?: \"orgs\"`\n // unassignable — an org-configured project could not pass its own config to `hasPermission`.\n TOrgSlug extends CollectionSlug | undefined = CollectionSlug | undefined,\n // Defaults to the FULL bound, not `{}`. Like `TOrgSlug` above: `customActions?` puts\n // this parameter in the interface body, so the bare `VexAccessConfig` every helper\n // names is only a supertype of concrete configs if the default covers them all.\n TCustomActions extends Partial<\n Record<TResources[number][\"slug\"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>\n > = Partial<\n Record<TResources[number][\"slug\"] | TUserSlug | Extract<TOrgSlug, string>, CustomActionsInput>\n >,\n> {\n /** Default: `true`. Turn access control on or off. */\n enabled: boolean;\n\n /**\n * The allback role when there is no user.\n */\n anonRole?: string;\n\n /** Role names known to the system. */\n roles: readonly string[];\n\n /** Collections/globals contributing subjects, keyed by slug. */\n resources: TResources;\n\n /**\n * Custom actions per subject slug, as declared in `defineAccess`. Carried on the\n * RESOLVED config purely so request-time code can tell a declared verb from a typo:\n * an undeclared action resolves through `defaultPermissionMode` (default `allow`),\n * so without this record a misspelled `access.action` silently widens access.\n */\n customActions?: TCustomActions;\n\n /**\n * Posture for undeclared role/subject/action combinations. Always\n * {@link PERMISSION_MODES.deny} — `defineAccess` pins it and no input field sets it.\n *\n * Retained as a field rather than inlined because `hasPermission` and\n * `resolveAccessRule` branch on it, and an allow posture is expressible as a\n * role-level `\"*\": true` if it is ever wanted back — per-role and greppable,\n * which a global default never was.\n *\n * @internal\n */\n defaultPermissionMode: PermissionMode;\n\n /** Slug of the user collection. */\n userCollectionSlug: TUserSlug;\n\n /** Field on the user document holding role(s) (`string | string[]`). */\n userRolesField: string;\n\n /** Slug of the organization collection, when configured. */\n orgCollectionSlug?: TOrgSlug;\n\n /**\n * The permission matrix as authored (checks may be booleans, field-mode\n * objects, or callbacks). Type-erased for storage; `defineAccess` fully\n * type-checks it at authoring time.\n */\n permissions: Record<string, Record<string, unknown>>;\n\n /**\n * Phantom field carrying {@link SubjectMap} for inference. Optional and\n * never assigned at runtime.\n */\n readonly __subjects?: TSubjects;\n}\n\n/**\n * Thrown by `hasPermission` when `throwOnDenied: true` and access is denied.\n * Carries the subject and action.\n */\nexport class VexAccessError extends ConvexError<{\n code: \"ACCESS_DENIED\";\n resource: string;\n action: string;\n message: string;\n}> {\n /** The subject on which access was denied. */\n resource: string;\n\n /** The denied action. */\n action: string;\n\n /**\n * @param options — Structured denial context.\n * @param options.message — Human-readable error message.\n * @param options.resource — Subject name.\n * @param options.action — Action name.\n */\n constructor(options: { message?: string; resource: string; action: string }) {\n // `ConvexError.data` MUST be a valid Convex value: `convexToJson` REJECTS\n // `undefined`, and an unserializable payload means Convex cannot deliver the\n // error at all — the client subscription never receives a result and the\n // query hangs in `fetchStatus: \"fetching\"` forever. Every key here is always\n // present; if an OPTIONAL key is ever added, omit it when absent — never pass\n // `undefined`. `message` travels in `data` because `ConvexError` owns\n // `this.message` (it stringifies `data`).\n super({\n code: \"ACCESS_DENIED\",\n resource: options.resource,\n action: options.action,\n message: options.message ?? `Access Denied: ${options.resource}/${options.action}`,\n });\n this.name = \"VexAccessError\";\n this.resource = options.resource;\n this.action = options.action;\n }\n}\n\n/**\n * Thrown by `defineAccess` on hard configuration errors (custom resource key\n * colliding with a resource slug; empty `actions` array).\n */\nexport class VexAccessConfigError extends Error {\n /** @param message — Human-readable description of the configuration error. */\n constructor(message: string) {\n super(message);\n this.name = \"VexAccessConfigError\";\n }\n}\n","import type {\n AccessFilterConstraint,\n ConstraintField,\n ConstraintResult,\n ConstraintValue,\n FilterConstraintBuilder,\n FilterOp,\n} from \"./constraintTypes\";\n\n/**\n * Creates a fresh unindexed constraint builder: the `q` a rule receives when it\n * declares no `withIndex` (DD 13). Comparisons produce a `compare` node;\n * `and`/`or`/`not` wrap the nodes their arguments produced, so a whole\n * expression evaluates bottom-up into one tree.\n *\n * Unlike {@link createIndexConstraintBuilder} this takes **no sink and keeps no\n * state**. The indexed builder records a flat sequence, so someone has to own the\n * list; here the combinators consume values and return values, which means the\n * callback's own return value already *is* the root of the tree. Two calls to this\n * factory cannot interfere because there is nothing to interfere with — every node\n * is freshly allocated and reachable only through what the caller was handed.\n *\n * The framework reads the result by reinterpreting the returned\n * {@link ConstraintResult} as {@link AccessFilterConstraint} — the same erasure this\n * module performs internally on combinator arguments. `ConstraintResult` is nominal\n * precisely so a rule cannot hand back a hand-built object literal; only a real\n * chain call can produce one.\n *\n * @typeParam TDoc - Document shape for the resource this rule governs.\n * @returns A stateless builder. Every call returns an independent object, and\n * nothing is shared between them.\n */\nexport function createFilterConstraintBuilder<TDoc>(): FilterConstraintBuilder<TDoc> {\n /**\n * Reinterprets a combinator argument as the node it actually is at runtime.\n * `ConstraintResult`'s nominal private member exists to stop callers forging a\n * result; it carries no runtime representation, so this is an identity cast.\n *\n * @param result - A value returned by an earlier chain call on this builder.\n * @returns The same object, typed as the tree node it is.\n */\n const asFilterConstraint = (result: ConstraintResult): AccessFilterConstraint<TDoc> =>\n result as unknown as AccessFilterConstraint<TDoc>;\n\n /**\n * Builds one comparison method. The casts erase per-field narrowing that the\n * runtime cannot re-derive: `AccessFilterConstraint`'s `field` is\n * `keyof TDoc & string` and its `value` is indexed from `TDoc`, while these\n * parameters are `string` and `unknown`. The call site already checked both.\n *\n * @param op - The method's own name, matching {@link FilterOp} exactly so a\n * later compile step can switch on it.\n * @returns A method recording one `compare` node under `op`.\n */\n const compare =\n (op: FilterOp) =>\n (field: string, value: unknown): AccessFilterConstraint<TDoc> => ({\n kind: \"compare\",\n field: field as ConstraintField<TDoc>,\n op,\n value: value as ConstraintValue<TDoc, ConstraintField<TDoc>>,\n });\n\n /**\n * Positionless runtime shape. Keying the comparisons off {@link FilterOp} means a\n * new operator there becomes a compile error here rather than a silently missing\n * method — the same discipline `createIndexConstraintBuilder` applies to\n * {@link FilterOp}'s index-only counterpart.\n */\n const builder: Record<\n FilterOp,\n (field: string, value: unknown) => AccessFilterConstraint<TDoc>\n > & {\n and: (...nodes: ConstraintResult[]) => AccessFilterConstraint<TDoc>;\n or: (...nodes: ConstraintResult[]) => AccessFilterConstraint<TDoc>;\n not: (node: ConstraintResult) => AccessFilterConstraint<TDoc>;\n } = {\n eq: compare(\"eq\"),\n neq: compare(\"neq\"),\n gt: compare(\"gt\"),\n gte: compare(\"gte\"),\n lt: compare(\"lt\"),\n lte: compare(\"lte\"),\n and: (...nodes) => ({ kind: \"and\", nodes: nodes.map(asFilterConstraint) }),\n or: (...nodes) => ({ kind: \"or\", nodes: nodes.map(asFilterConstraint) }),\n not: (node) => ({ kind: \"not\", node: asFilterConstraint(node) }),\n };\n\n // The one sanctioned cast in this module: every public method is declared to\n // return the nominal `ConstraintResult`, while the runtime returns the real node.\n // Erasing that here means no per-method cast and no way for a caller to reach the\n // node's shape without going through the framework's own reinterpretation.\n return builder as unknown as FilterConstraintBuilder<TDoc>;\n}\n","import type {\n AccessIndexConstraint,\n ConstraintBuilder,\n ConstraintResult,\n IndexOp,\n} from \"./constraintTypes\";\n\n/**\n * Key the accumulated constraint list is carried on. A symbol so it cannot collide\n * with a field name and does not appear in the builder's public type.\n */\nconst CONSTRAINTS = Symbol(\"vex.access.indexConstraints\");\n\n/** The runtime shape behind a positional chain: methods, plus its own accumulation. */\ntype IndexChainNode<TDoc> = Record<IndexOp, (field: string, value: unknown) => unknown> & {\n [CONSTRAINTS]: readonly AccessIndexConstraint<string, TDoc>[];\n};\n\n/**\n * Creates a positional constraint builder — the `ix` a rule receives inside\n * `q.withIndex(name, (ix) => …)`.\n *\n * **Pure.** Every method returns a NEW builder carrying the accumulated list; there\n * is no shared array and no caller-owned sink. That is what makes \"only the value\n * the callback RETURNS counts\" true rather than aspirational: a chain the rule\n * builds and discards leaves nothing behind, exactly like any other dead expression.\n * The earlier `{ into }` sink made discarded chains apply anyway, which surprised in\n * the one direction that matters — a rule doing more than it appeared to.\n *\n * The TYPE narrows per call — {@link ConstraintBuilder}'s three-interface state\n * machine only exposes the methods still legal at each position. The RUNTIME object\n * does not: each node carries all five methods, cast to the narrowing type on\n * return. Type safety is enforced entirely at the call site, never here.\n *\n * @typeParam TFields - The index's field tuple, in declaration order.\n * @typeParam TDoc - The document shape constraint values are typed from.\n * @returns A builder positioned at field 0, with nothing accumulated.\n */\nexport function createIndexConstraintBuilder<\n TFields extends readonly string[],\n TDoc,\n>(): ConstraintBuilder<TFields, TDoc> {\n /**\n * Builds one node over an accumulated list.\n *\n * Keying the methods off {@link IndexOp} means a new Convex index-range method\n * becomes a compile error here rather than a silently unrecorded operator.\n *\n * @param constraints - Everything accumulated so far, in call order.\n * @returns A node exposing all five operators, each extending the list.\n */\n function node(constraints: readonly AccessIndexConstraint<string, TDoc>[]): IndexChainNode<TDoc> {\n /**\n * Appends one constraint and returns the next node.\n *\n * The cast erases per-field narrowing the runtime cannot re-derive:\n * `AccessIndexConstraint`'s `value` is `TField extends keyof TDoc ? … : never`,\n * a call-site typing device, while these parameters are `string`/`unknown`.\n *\n * @param op - The method's own name, matching {@link IndexOp} exactly.\n * @param field - Field name being constrained.\n * @param value - Constraint value, already validated at the call site.\n * @returns The next node in the chain.\n */\n const extend = (op: IndexOp, field: string, value: unknown): IndexChainNode<TDoc> =>\n node([\n ...constraints,\n { field, op, value } as AccessIndexConstraint<string, TDoc>,\n ]);\n\n return {\n eq: (field, value) => extend(\"eq\", field, value),\n gt: (field, value) => extend(\"gt\", field, value),\n gte: (field, value) => extend(\"gte\", field, value),\n lt: (field, value) => extend(\"lt\", field, value),\n lte: (field, value) => extend(\"lte\", field, value),\n [CONSTRAINTS]: constraints,\n };\n }\n\n // The one sanctioned cast: the runtime shape (five methods, always present) does\n // not match the narrowing interface shape (each stage exposes a different subset).\n return node([]) as unknown as ConstraintBuilder<TFields, TDoc>;\n}\n\n/**\n * Reads the constraints a returned positional chain accumulated.\n *\n * The counterpart to the cast above: `ConstraintResult` is nominal and carries no\n * public shape, so only this module can get the list back out. Called by\n * `createAccessQueryBuilder` on whatever the range callback returned — which is why\n * a discarded chain contributes nothing.\n *\n * @typeParam TDoc - The document shape constraint values are typed from.\n * @param result - The value a range callback returned.\n * @returns Its accumulated constraints, in call order. `[]` when `result` did not\n * come from this module — unreachable through the types, and treated as \"no\n * constraints\" rather than throwing, since `validateAccessConstraints` owns\n * diagnosing a rule that reached past them.\n * @internal\n */\nexport function readIndexConstraints<TDoc>(\n result: ConstraintResult,\n): readonly AccessIndexConstraint<string, TDoc>[] {\n const carried = (result as unknown as Partial<IndexChainNode<TDoc>>)[CONSTRAINTS];\n return carried ?? [];\n}\n","import type {\n AccessConditionResult,\n AccessFilterConstraint,\n AccessIndexConstraint,\n AccessQueryBuilder,\n FilterConstraintBuilder,\n IndexedAccessCondition,\n} from \"./constraintTypes\";\nimport { createFilterConstraintBuilder } from \"./createFilterConstraintBuilder\";\nimport { createIndexConstraintBuilder, readIndexConstraints } from \"./createIndexConstraintBuilder\";\n\n/**\n * What a rule's `constraints` callback resolved to: the condition on which\n * documents the caller may read.\n *\n * Either half may be present, and **both together is the normal case, not a\n * conflict** — it is exactly `.withIndex(name, range).filter(expr)`, Convex's own\n * shape. The range narrows what is READ; the filter rejects rows within that range\n * which the range cannot describe (`neq`, `or`, `not`).\n *\n * Neither present means the callback returned a condition that excludes nothing.\n * Absence of a condition altogether is `undefined`, not an empty object.\n *\n * @typeParam TDoc - Document shape the values are typed against.\n */\nexport interface AccessCondition<TDoc> {\n /** The index the rule chose, and the range it built on it. */\n index?: {\n /** Declared index name passed to `q.withIndex(…)`. */\n name: string;\n /** The range, in call order. Never empty — the range callback is required. */\n constraints: readonly AccessIndexConstraint<string, TDoc>[];\n };\n /** Root of the predicate tree, when the rule added or used `filter`. */\n filter?: AccessFilterConstraint<TDoc>;\n}\n\n/** Key an `AccessCondition` is carried on behind the nominal terminal. */\nconst CONDITION = Symbol(\"vex.access.condition\");\n\n/** The runtime value behind {@link AccessConditionResult}. */\ntype ConditionNode<TDoc> = { [CONDITION]: AccessCondition<TDoc> };\n\n/**\n * Creates the `q` a query-shaped rule receives.\n *\n * Composes the two algebras without letting them mix: `withIndex` runs its range\n * callback against a fresh positional builder and reads the result; `filter` runs\n * its predicate callback against a fresh flat builder. Neither builder escapes its\n * callback, so an index expression can never reach a boolean combinator.\n *\n * **Pure, and only the returned value counts.** Nothing accumulates outside a\n * callback's own return, so a rule that builds a chain and discards it contributes\n * nothing — dead code behaves like dead code.\n *\n * @typeParam TDoc - Document shape constraint values are typed from.\n * @typeParam TIndexFields - The resource's index name → field tuple map.\n * @returns A builder whose `withIndex` / `filter` results carry the condition.\n */\nexport function createAccessQueryBuilder<\n TDoc,\n TIndexFields extends Record<string, readonly string[]>,\n>(): AccessQueryBuilder<TDoc, TIndexFields> {\n /**\n * Wraps a condition in the nominal terminal the callback returns.\n *\n * @param condition - The condition to carry.\n * @returns A value satisfying `AccessConditionResult` at the type level.\n */\n const conditionNode = (condition: AccessCondition<TDoc>): ConditionNode<TDoc> => ({\n [CONDITION]: condition,\n });\n\n /**\n * Runs a predicate callback against a fresh flat builder.\n *\n * @param predicate - The rule's predicate callback.\n * @returns The tree it returned.\n */\n const runFilter = (\n predicate: (q: FilterConstraintBuilder<TDoc>) => unknown,\n ): AccessFilterConstraint<TDoc> =>\n predicate(createFilterConstraintBuilder<TDoc>()) as AccessFilterConstraint<TDoc>;\n\n const builder = {\n withIndex: (name: string, range: (ix: unknown) => never) => {\n const built = range(createIndexConstraintBuilder<readonly string[], TDoc>());\n const constraints = readIndexConstraints<TDoc>(built);\n const base: AccessCondition<TDoc> = { index: { name, constraints } };\n\n return {\n ...conditionNode(base),\n // Continuing with `.filter(…)` produces a NEW condition carrying both\n // halves; the index-only value the rule already holds is unchanged.\n filter: (predicate: (q: FilterConstraintBuilder<TDoc>) => unknown) =>\n conditionNode({ ...base, filter: runFilter(predicate) }),\n };\n },\n filter: (predicate: (q: FilterConstraintBuilder<TDoc>) => unknown) =>\n conditionNode({ filter: runFilter(predicate) }),\n };\n\n return builder as unknown as AccessQueryBuilder<TDoc, TIndexFields>;\n}\n\n/**\n * Reads the condition a returned terminal carries.\n *\n * The counterpart to the casts above: `AccessConditionResult` is nominal and has no\n * public shape, so only this module can get the condition back out.\n *\n * @typeParam TDoc - Document shape the values are typed against.\n * @param result - The value a rule's `constraints` callback returned.\n * @returns Its condition, or `undefined` when `result` did not come from this\n * module — unreachable through the types, and treated as \"no condition\" so a rule\n * that reached past them cannot silently widen into an unfiltered read.\n */\nexport function readAccessCondition<TDoc>(\n result: AccessConditionResult,\n): AccessCondition<TDoc> | undefined {\n // Guarded rather than a bare property read: a `constraints` callback can return\n // `undefined`, `null`, or a primitive (a missing `return`, a value smuggled past a\n // cast), and indexing those for the carrier symbol is a `TypeError`. \"No condition\n // here\" is a legitimate answer this reader must be able to give — callers already\n // treat `undefined` as \"not built through `q`\" and fail safe or fail loud on it.\n if (result === null || (typeof result !== \"object\" && typeof result !== \"function\")) {\n return undefined;\n }\n return (result as unknown as Partial<ConditionNode<TDoc>>)[CONDITION];\n}\n\n/** Re-exported so the type name travels with its reader. */\nexport type { IndexedAccessCondition };\n","import type {\n DocumentByInfo,\n Expression,\n FilterBuilder,\n GenericDocument,\n GenericTableInfo,\n IndexRange,\n} from \"convex/server\";\nimport type {\n AccessFilterConstraint,\n AccessIndexConstraint,\n FilterOp,\n IndexOp,\n} from \"./constraintTypes\";\nimport type { IndexRangeFn } from \"./types\";\n\n/**\n * Structural equality over Convex values.\n *\n * Convex compares values by CONTENT, both in an index range and in a `.filter()`\n * expression. JS `===` compares arrays and objects by REFERENCE, so using it for the\n * predicate made the two compile targets disagree for every array-valued field —\n * which is every `relationship` and `select` field, since both store `v.array(...)`.\n *\n * The divergence was not symmetric, and one direction was unsafe: `eq` produced a\n * false NEGATIVE (the query returned a row the predicate then rejected, so a client\n * denied access the server had granted), while `neq` produced a false POSITIVE (the\n * predicate admitted a row the query had excluded). The second is the dangerous one,\n * and it is why this is content equality rather than a special case for `eq`.\n *\n * `undefined` equals `undefined`, which keeps \"field missing\" and \"field explicitly\n * undefined\" indistinguishable — matching how a document read through bracket access\n * behaves, and matching Convex, which stores neither.\n *\n * @param a - Left value.\n * @param b - Right value.\n * @returns `true` when the two values are structurally equal.\n */\nfunction convexValuesEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n\n // `typeof null === \"object\"`, and `null` is a distinct Convex value from both\n // `undefined` and `{}` — bail before either composite branch can treat it as one.\n if (a === null || b === null || typeof a !== \"object\" || typeof b !== \"object\") return false;\n\n if (a instanceof ArrayBuffer || b instanceof ArrayBuffer) {\n if (!(a instanceof ArrayBuffer) || !(b instanceof ArrayBuffer)) return false;\n if (a.byteLength !== b.byteLength) return false;\n const left = new Uint8Array(a);\n const right = new Uint8Array(b);\n return left.every((byte, i) => byte === right[i]);\n }\n\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) return false;\n return a.every((item, i) => convexValuesEqual(item, b[i]));\n }\n\n const left = a as Record<string, unknown>;\n const right = b as Record<string, unknown>;\n const leftKeys = Object.keys(left);\n if (leftKeys.length !== Object.keys(right).length) return false;\n return leftKeys.every(\n (key) => key in right && convexValuesEqual(left[key], right[key]),\n );\n}\n\n/**\n * JS-side comparator per {@link IndexOp}, used by `constraintsToPredicate`.\n * `as never` narrows past `unknown` for the ordering operators only —\n * `eq` never needs it. Exported so a caller building its own predicate logic\n * (or a future compile target) reuses the same five comparisons rather than\n * re-deriving them.\n *\n * The ordering operators are left as JS relational comparisons: they are only ever\n * recorded against a single field of one type, where JS and Convex agree. Convex's\n * cross-type total order is deliberately not reimplemented here.\n */\nexport const CONSTRAINT_COMPARATORS = {\n eq: convexValuesEqual,\n gt: (a: unknown, b: unknown) => (a as never) > (b as never),\n gte: (a: unknown, b: unknown) => (a as never) >= (b as never),\n lt: (a: unknown, b: unknown) => (a as never) < (b as never),\n lte: (a: unknown, b: unknown) => (a as never) <= (b as never),\n} as const satisfies Record<IndexOp, (a: unknown, b: unknown) => boolean>;\n\n/**\n * Compiles a recorded constraint list to a `withIndex` range function.\n *\n * @param props - Input props.\n * @param props.constraints - The rule's recorded constraints, in the order\n * they were built. May be empty — an unrestricted range.\n * @returns A function usable directly as `find`'s `withIndex` range: applies\n * each constraint's operator to the builder in order and returns the\n * result as an `IndexRange`.\n */\nexport function accessConstraintsToIndexRange<\n TDocument extends GenericDocument = GenericDocument,\n>(props: { constraints: readonly AccessIndexConstraint<string, TDocument>[] }): IndexRangeFn {\n return (q) =>\n props.constraints.reduce<unknown>(\n (builder, c) =>\n // Convex's `IndexRangeBuilder` narrows its RETURN TYPE per call, exactly as\n // `ConstraintBuilder` does — so a dynamic reduce over an untyped op string\n // needs one cast at this boundary. The static safety already happened when\n // the rule's `constraints` callback was type-checked, and again at\n // `defineAccess` time in `validateAccessConstraints`.\n (builder as Record<IndexOp, (field: string, value: unknown) => unknown>)[c.op](\n c.field,\n c.value,\n ),\n q,\n // Every `IndexRangeBuilder` structurally extends `IndexRange`, including one\n // with zero calls applied — so an empty constraint list returns `q` itself,\n // matching Convex's own \"no restriction\" semantics.\n ) as IndexRange;\n}\n\n/**\n * Compiles a recorded constraint list to a single `FilterBuilder` expression,\n * `and`-ing multiple constraints together.\n *\n * @param props - Input props.\n * @param props.constraints - The rule's recorded constraints, in the order\n * they were built. Must be non-empty.\n * @param props.q - The query's own `FilterBuilder`, from `.filter((q) => …)`.\n * @returns A boolean expression usable directly as (or `and`-ed into) a\n * `.filter()` predicate. A single constraint returns its own expression\n * unwrapped; two or more are combined with `and`.\n * @throws {Error} When `props.constraints` is empty — a caller contract\n * violation, not a config-authoring mistake. An unrestricted rule resolves to\n * its boolean shorthand upstream and never reaches a compile target.\n */\nexport function accessConstraintsToFilter<\n TTableInfo extends GenericTableInfo,\n TDocument extends DocumentByInfo<TTableInfo> = DocumentByInfo<TTableInfo>,\n>(props: {\n constraints: readonly AccessIndexConstraint<string, TDocument>[];\n q: FilterBuilder<TTableInfo>;\n}): Expression<boolean> {\n // `c.op`'s literal values are `FilterBuilder`'s own method names by construction\n // (`IndexOp` derives from `IndexRangeBuilder`, whose comparison methods\n // `FilterBuilder` mirrors), so this dispatches correctly. The cast exists only\n // because the compiler cannot narrow `c.op` per array element.\n const q = props.q as unknown as Record<\n IndexOp,\n (left: unknown, right: unknown) => Expression<boolean>\n > & {\n field: (path: string) => unknown;\n and: (...exprs: Expression<boolean>[]) => Expression<boolean>;\n };\n\n const [first, ...rest] = props.constraints.map((c) => q[c.op](q.field(c.field), c.value));\n\n if (first === undefined) {\n // A caller contract violation, not a config-authoring mistake — hence a plain\n // `Error` rather than `VexAccessConfigError`. This function runs at QUERY time,\n // called by `find`/`search`; a config-error type here would misattribute a\n // framework bug to the user's `defineAccess`. An unconstrained rule resolves to\n // its boolean shorthand upstream and never reaches a compile target, so an\n // empty list means the caller skipped that resolution.\n throw new Error(\n \"accessConstraintsToFilter: `constraints` must be non-empty — an unrestricted \" +\n \"rule is resolved to a boolean before it reaches a compile target.\",\n );\n }\n\n // One expression stands alone: wrapping a single predicate in `and()` would add a\n // node for Convex to walk and read no differently.\n return rest.length === 0 ? first : q.and(first, ...rest);\n}\n\n/**\n * Compiles a recorded constraint list to a JS predicate, for interpreting\n * constraints against an already-fetched document (`hasPermission`,\n * client-side `usePermission`).\n *\n * The third compile target for the SAME recorded list, and it agrees with\n * `accessConstraintsToFilter` by construction: both walk the constraints in order\n * and both dispatch on the operator set {@link CONSTRAINT_COMPARATORS} defines, one\n * in JS and one as a Convex expression. `compileConstraints.test.ts` asserts that\n * agreement on a matching and a non-matching document.\n *\n * @param props - Input props.\n * @param props.constraints - The rule's recorded constraints, in the order\n * they were built. May be empty — an unrestricted predicate.\n * @returns A function returning `true` when `doc` satisfies every constraint.\n */\nexport function accessConstraintsToPredicate<TDoc extends GenericDocument>(props: {\n constraints: readonly AccessIndexConstraint<string, TDoc>[];\n}): (doc: TDoc) => boolean {\n // An empty constraint list yields a predicate that is vacuously true\n // (`Array#every` on an empty array), matching\n // `accessConstraintsToIndexRange`'s \"no restriction\" semantics for the same input.\n return (doc) =>\n props.constraints.every((c) => CONSTRAINT_COMPARATORS[c.op](doc[c.field], c.value));\n}\n\n/**\n * JS-side comparator per {@link FilterOp} — {@link CONSTRAINT_COMPARATORS} plus\n * `neq`, which `FilterBuilder` has and `IndexRangeBuilder` does not.\n *\n * Used by {@link accessFilterTreeToPredicate}. Kept as an extension of the index\n * map rather than a parallel one so the two algebras cannot drift on a shared\n * operator: change `gte` once and both compile targets follow.\n */\nexport const FILTER_COMPARATORS = {\n ...CONSTRAINT_COMPARATORS,\n neq: (a: unknown, b: unknown) => !convexValuesEqual(a, b),\n} as const satisfies Record<FilterOp, (a: unknown, b: unknown) => boolean>;\n\n/**\n * Compiles a recorded constraint TREE to a single `FilterBuilder` expression.\n *\n * The tree counterpart of {@link accessConstraintsToFilter}: that one walks the\n * flat positional list an indexed rule records, this one walks the recursive\n * `compare`/`and`/`or`/`not` shape an unindexed rule records. Both target the\n * same `.filter()` slot.\n *\n * There is no empty-tree case to guard. A rule that recorded nothing leaves\n * `AccessCondition.filter` as `undefined`, so absence is represented by\n * the field being missing rather than by an empty node — unlike the flat list,\n * where `[]` is representable and has to be rejected.\n *\n * @typeParam TTableInfo - Convex table info for the query being filtered.\n * @typeParam TDoc - Document shape the recorded values are typed against.\n * @param props - Input props.\n * @param props.node - Root of the recorded tree.\n * @param props.q - The query's own `FilterBuilder`, from `.filter((q) => …)`.\n * @returns A boolean expression usable directly as (or `and`-ed into) a\n * `.filter()` predicate.\n */\nexport function accessFilterTreeToFilter<\n TTableInfo extends GenericTableInfo,\n TDoc extends DocumentByInfo<TTableInfo> = DocumentByInfo<TTableInfo>,\n>(props: { node: AccessFilterConstraint<TDoc>; q: FilterBuilder<TTableInfo> }): Expression<boolean> {\n // `FilterOp`'s literals are `FilterBuilder`'s own comparison method names by\n // construction, and its combinators are `and`/`or`/`not`. The cast exists only\n // because the compiler cannot narrow the operator per node.\n const q = props.q as unknown as Record<\n FilterOp,\n (left: unknown, right: unknown) => Expression<boolean>\n > & {\n field: (path: string) => unknown;\n and: (...exprs: Expression<boolean>[]) => Expression<boolean>;\n or: (...exprs: Expression<boolean>[]) => Expression<boolean>;\n not: (expr: Expression<boolean>) => Expression<boolean>;\n };\n\n /**\n * Compiles one node, recursing through combinators.\n *\n * @param node - The node to compile.\n * @returns Its Convex expression.\n */\n function compile(node: AccessFilterConstraint<TDoc>): Expression<boolean> {\n switch (node.kind) {\n case \"compare\":\n return q[node.op](q.field(node.field), node.value);\n case \"and\":\n return q.and(...node.nodes.map(compile));\n case \"or\":\n return q.or(...node.nodes.map(compile));\n case \"not\":\n return q.not(compile(node.node));\n }\n }\n\n return compile(props.node);\n}\n\n/**\n * Compiles a recorded constraint TREE to a JS predicate, for interpreting an\n * unindexed rule against an already-fetched document.\n *\n * The tree counterpart of {@link accessConstraintsToPredicate}, and it agrees with\n * {@link accessFilterTreeToFilter} by construction: both walk the same nodes in the\n * same order and both dispatch comparisons through {@link FILTER_COMPARATORS}, one\n * in JS and one as a Convex expression.\n *\n * @typeParam TDoc - Document shape the recorded values are typed against.\n * @param props - Input props.\n * @param props.node - Root of the recorded tree.\n * @returns A function returning `true` when `doc` satisfies the tree.\n */\nexport function accessFilterTreeToPredicate<TDoc extends GenericDocument>(props: {\n node: AccessFilterConstraint<TDoc>;\n}): (doc: TDoc) => boolean {\n /**\n * Evaluates one node against `doc`, recursing through combinators.\n *\n * @param node - The node to evaluate.\n * @param doc - The document under test.\n * @returns Whether `doc` satisfies `node`.\n */\n function evaluate(node: AccessFilterConstraint<TDoc>, doc: TDoc): boolean {\n switch (node.kind) {\n case \"compare\":\n return FILTER_COMPARATORS[node.op](doc[node.field], node.value);\n case \"and\":\n // An empty `and()` is vacuously true, matching `Array#every` and the flat\n // compilers' treatment of an empty constraint list.\n return node.nodes.every((child) => evaluate(child, doc));\n case \"or\":\n // An empty `or()` is false — the identity of OR. Reachable only by calling\n // `q.or()` with no arguments, which denies everything; the builder records\n // it faithfully rather than second-guessing the author.\n return node.nodes.some((child) => evaluate(child, doc));\n case \"not\":\n return !evaluate(node.node, doc);\n }\n }\n\n return (doc) => evaluate(props.node, doc);\n}\n","import type { GenericDocument } from \"convex/server\";\nimport { PERMISSION_MODES, PERMISSION_SCOPES, PermissionScope, WILDCARD_KEY } from \"./constants\";\nimport { createAccessQueryBuilder, readAccessCondition } from \"./createAccessQueryBuilder\";\nimport { accessConstraintsToPredicate, accessFilterTreeToPredicate } from \"./compileConstraints\";\nimport { VexAccessError } from \"./types\";\nimport type {\n PermissionCallbackProps,\n PermissionCheck,\n SubjectEntry,\n VexAccessConfig,\n} from \"./types\";\n\n/**\n * Props required for the hasPermission({...}) function\n *\n * @param access - The resolved config from `defineAccess()`. `undefined`\n * disables access control entirely — every check passes.\n * @param user - The authenticated user document. Roles are derived from\n * `user[access.userRolesField]` (`string` or `string[]`); a missing or empty\n * value falls back to access.anonRole\n * @param organization - The organization document forwarded to callbacks.\n * Only surfaces when `access.orgCollectionSlug` is configured.\n * @param resource - Subject name — a resource slug, a built-in subject\n * (e.g. `\"adminPanel\"`), or a custom resource name.\n * @param action - Action on `resource`, typed per subject.\n * @param data - Document/context forwarded to permission callbacks.\n * @param throwOnDenied - When `true`, throws `VexAccessError` instead of\n * @param scope Which question to answer when a role's check is a callback that\n * needs the document and `data` was not supplied:\n * `\"all\"` (default) asks \"may they do this to every document\" and resolves such\n * a check to `false` — fail-closed, never throws; `\"any\"` asks \"may they do this\n * to at least one document\" and resolves it to `true` (nav/sidebar/list gating —\n * row-level filtering happens separately in `find`/`get`); `\"doc\"` demands an\n * exact per-document answer and throws `VexAccessError` when `data` is missing,\n * which is what you want in edit views. Has no effect on static boolean checks,\n * and no effect when `data` is provided (the callback is always run against it).\n * @default \"all\"\n */\nexport interface HasPermissionProps<\n TSubjects extends Record<string, SubjectEntry> = Record<string, SubjectEntry>,\n TSubject extends keyof TSubjects & string = keyof TSubjects & string,\n TData extends {} = {},\n> {\n access?: VexAccessConfig<TSubjects>;\n user: Record<string, unknown> | null;\n organization?: Record<string, unknown>;\n resource: TSubject;\n action: TSubjects[TSubject][\"action\"];\n data?: TData;\n throwOnDenied?: boolean;\n scope?: PermissionScope;\n}\n\n/**\n * Resolves runtime role-based access for a single subject + action, merging\n * every role the user holds into one decision.\n *\n * This is the single runtime entry point — every server API guard, admin panel\n * gate, and custom-subject check calls it; the resolution helpers below are\n * module-private.\n *\n * Resolution, per role, first hit wins: subject boolean shorthand → explicit\n * action key → subject-level `WILDCARD_KEY` → role-level `WILDCARD_KEY`\n * (undeclared subjects only) → `defaultPermissionMode`. Roles then OR-merge:\n * any role allowing allows.\n *\n * @param props @see {@link HasPermissionProps}\n * @returns `true` when the action is permitted for the caller.\n * @throws {VexAccessError} When `throwOnDenied` is `true` and access is denied —\n * carries `resource` and `action`.\n *\n * @example\n * ```ts\n * hasPermission({ access, user, resource: \"posts\", action: \"update\" }); // boolean\n * hasPermission({ access, user, resource: \"posts\", action: \"delete\",\n * data: post, throwOnDenied: true }); // throws VexAccessError on deny\n * ```\n */\nexport function hasPermission<\n TSubjects extends Record<string, SubjectEntry>,\n TSubject extends keyof TSubjects & string,\n TData extends {},\n>(props: HasPermissionProps<TSubjects, TSubject, TData>): boolean {\n const { access } = props;\n\n if (!access || access === undefined || !access.enabled) {\n return true;\n }\n\n const rawRoles = props.user ? props.user[access.userRolesField] : [];\n const userRoles =\n typeof rawRoles === \"string\"\n ? [rawRoles]\n : Array.isArray(rawRoles)\n ? rawRoles.filter((role): role is string => typeof role === \"string\")\n : [];\n const effectiveRoles =\n userRoles.length === 0 && access.anonRole !== undefined ? [access.anonRole] : userRoles;\n const knownRoles = effectiveRoles.filter((role) => access.roles.includes(role));\n\n const defaultAllowed = access.defaultPermissionMode === PERMISSION_MODES.allow;\n let allPermissions: boolean;\n\n if (knownRoles.length === 0) {\n allPermissions = false;\n } else {\n const resolved = knownRoles.map((userRole): boolean => {\n const role = access.permissions[userRole];\n const resource = role?.[props.resource];\n\n let check: PermissionCheck;\n if (typeof resource === \"boolean\") {\n // { posts: true }\n check = resource;\n } else if (resource !== null && resource !== undefined && typeof resource === \"object\") {\n // { posts: { \"*\": true, update: () => {}, delete: false } }\n check =\n resolveActionCheck({\n resource: resource as Record<string, unknown>,\n action: props.action,\n }) ?? defaultAllowed;\n } else {\n // { posts: undefined }\n const roleWildcard = role?.[WILDCARD_KEY];\n check = typeof roleWildcard === \"boolean\" ? roleWildcard : defaultAllowed;\n }\n\n return (\n resolvePermissionCheck({\n check,\n user: props.user,\n data: props.data,\n organization: access.orgCollectionSlug !== undefined ? props.organization : undefined,\n resource: props.resource,\n action: props.action,\n scope: props.scope ?? PERMISSION_SCOPES.all,\n }) ?? defaultAllowed\n );\n });\n\n // OR across roles: holding any role that permits the action is enough.\n allPermissions = resolved.some(Boolean);\n }\n\n if (allPermissions === false) {\n if (props.throwOnDenied) {\n throw new VexAccessError({\n resource: props.resource,\n action: props.action,\n });\n }\n return false;\n }\n return true;\n}\n\nconst CAPABILITY_PROBE = Symbol(\"vex.capabilityProbe\");\n\n/**\n * True when a resolved check is the constraint object form.\n *\n * Presence of `constraints` is the discriminant. This matters here because the\n * early `typeof check !== \"function\"` return below treats every non-function as a\n * boolean or field-mode value — a constrained object has neither `mode` nor\n * `fields`, so it used to be mistaken for one.\n *\n * @param check - The resolved check for one role + action.\n * @returns `true` when `check` carries a `constraints` callback.\n * @internal\n */\nfunction isConstrainedCheck(\n check: PermissionCheck,\n): check is Extract<PermissionCheck, { constraints: unknown }> {\n return typeof check === \"object\" && check !== null && \"constraints\" in check;\n}\n\n/**\n * Evaluates a constraint-form check against one document.\n *\n * This is the second half of what makes constraints worth recording as data: the\n * SAME declaration that compiles to a `withIndex` range or a `.filter()` expression\n * on the server also interprets directly in JS here, which is what keeps\n * client-side `usePermission` working with no server round trip (P-004). A closure\n * could only ever do this half.\n *\n * `filter`, when present, is additive — the condition must hold AND the filter must\n * pass. It is resolved by recursing, so the existing boolean/callback/field-mode\n * handling owns it.\n *\n * @typeParam TData - Document type under test.\n * @typeParam TUser - User document shape.\n * @typeParam TOrg - Organization document shape.\n * @param props - Input props, as `resolvePermissionCheck` received them.\n * @param props.check - The constraint-form check.\n * @param props.user - Caller.\n * @param props.data - Document under test; absent means the quantified path.\n * @param props.organization - Active organization, when configured.\n * @param props.resource - Subject slug, for the error message.\n * @param props.action - Action name, for the error message.\n * @param props.scope - How to answer when `data` is absent.\n * @returns Whether this role permits the action on `props.data`.\n * @throws {VexAccessError} When no `data` was supplied under `scope: \"doc\"` — a\n * constraint is a per-document condition, so there is nothing to answer without\n * a document.\n * @internal\n */\nfunction resolveConstrainedCheck<TData, TUser, TOrg>(props: {\n check: Extract<PermissionCheck, { constraints: unknown }>;\n user: TUser;\n data?: TData;\n organization?: TOrg;\n resource: string;\n action: string;\n scope: PermissionScope;\n}): boolean {\n const outcome = props.check.constraints({\n user: props.user,\n q: createAccessQueryBuilder(),\n ...(props.organization !== undefined ? { organization: props.organization } : {}),\n } as unknown as Parameters<typeof props.check.constraints>[0]);\n\n // A rule may short-circuit to a flat allow/deny rather than building a condition.\n if (typeof outcome === \"boolean\") return outcome;\n\n if (props.data === undefined) {\n // A condition is inherently per-document — the same situation a data-reading\n // callback is in, so answer the same quantified question. See `PERMISSION_SCOPES`.\n if (props.scope === PERMISSION_SCOPES.any) return true;\n if (props.scope === PERMISSION_SCOPES.all) return false;\n throw new VexAccessError({\n resource: props.resource,\n action: props.action,\n message:\n `hasPermission: \"${props.resource}.${props.action}\" needs a \"data\" object (its check is a constraint). ` +\n `Pass \"data\" for an exact check, or use scope: \"any\" (nav/list gating) or scope: \"all\" (bulk actions).`,\n });\n }\n\n const condition = readAccessCondition<GenericDocument>(outcome);\n // A condition this module cannot read did not come from `q`. Deny rather than\n // widen: an unreadable condition must never resolve to unrestricted access.\n if (condition === undefined) return false;\n\n const doc = props.data as unknown as GenericDocument;\n const indexHolds =\n condition.index === undefined ||\n accessConstraintsToPredicate({ constraints: condition.index.constraints })(doc);\n const filterHolds =\n condition.filter === undefined || accessFilterTreeToPredicate({ node: condition.filter })(doc);\n if (!indexHolds || !filterHolds) return false;\n\n // `filter` augments the condition; it never replaces it.\n if (props.check.filter === undefined) return true;\n return resolvePermissionCheck<TData, TUser, TOrg>({\n ...props,\n check: props.check.filter as PermissionCheck,\n });\n}\n\n/**\n * Resolves one role's `PermissionCheck` into a concrete result: booleans and\n * mode objects pass through; callbacks are invoked with `{ user, data?,\n * organization? }`.\n *\n * Module-private. The caller resolves \"not declared\" to the configured\n * `defaultPermissionMode` BEFORE calling — `check` is always a real check\n * here. A callback returning `undefined` resolves to `false` (deny), so an\n * inconclusive callback can never be mistaken for an undeclared action.\n *\n * @throws VexAccessError when trying to call hasPermission without passing data\n * when a permission check requires the data to execture and determine the result\n * @returns The check's boolean or field-mode object; for callbacks, the\n * callback's result with `undefined` normalized to `false`.\n */\nfunction resolvePermissionCheck<TData, TUser, TOrg>(props: {\n check: PermissionCheck;\n user: TUser;\n data?: TData;\n organization?: TOrg;\n resource: string;\n action: string;\n scope: PermissionScope;\n}): boolean {\n if (isConstrainedCheck(props.check)) {\n return resolveConstrainedCheck<TData, TUser, TOrg>({\n ...props,\n check: props.check,\n });\n }\n\n if (typeof props.check !== \"function\") {\n return props.check;\n }\n\n const callbackProps = {\n user: props.user,\n data: props.data !== undefined ? props.data : undefined,\n organization: props.organization !== undefined ? props.organization : undefined,\n } as PermissionCallbackProps;\n if (props.data !== undefined) {\n const result = props.check(callbackProps);\n return result === undefined ? false : result;\n }\n\n try {\n // no data → probe whether the callback DEPENDS on data (throws the sentinel on ANY access;\n // Proxy is truthy so `?.` doesn't short-circuit, unlike a bare `undefined`)\n const probe = new Proxy(\n {},\n {\n get() {\n throw { [CAPABILITY_PROBE]: true };\n },\n has() {\n throw { [CAPABILITY_PROBE]: true };\n },\n ownKeys() {\n throw { [CAPABILITY_PROBE]: true };\n },\n },\n );\n const result = props.check({ ...callbackProps, data: probe });\n return result === undefined ? false : result;\n } catch (e) {\n const touchedData =\n typeof e === \"object\" &&\n e !== null &&\n (e as Record<symbol, unknown>)[CAPABILITY_PROBE] === true;\n if (!touchedData) throw e; // a real error from the callback body — never swallow it\n // The callback needs the document and none was supplied — answer the\n // quantified question the caller asked for. See `PERMISSION_SCOPES`.\n if (props.scope === PERMISSION_SCOPES.any) {\n // \"at least one document\" — a per-doc condition may hold for some row.\n return true;\n }\n if (props.scope === PERMISSION_SCOPES.all) {\n // \"every document\" — a per-doc condition means it cannot hold for all.\n return false;\n }\n throw new VexAccessError({\n resource: props.resource,\n action: props.action,\n message:\n `hasPermission: \"${props.resource}.${props.action}\" needs a \"data\" object (its check reads the document). ` +\n `Pass \"data\" for an exact check, or use scope: \"any\" (nav/list gating) or scope: \"all\" (bulk actions).`,\n });\n }\n}\n\n/**\n * Resolves a single action's check from a per-action map, consulting the\n * action-level wildcard when the explicit action isn't declared.\n *\n * The wildcard precedence lives in exactly one place so `hasPermission`,\n * `resolveAccessIndex`, and any future evaluator (e.g. DB-backed roles) share\n * it. Presence, not truthiness, decides: an explicit `false` still wins over\n * the wildcard.\n *\n * @param props - Input props.\n * @param props.resource - The per-action map declared for one role + resource.\n * @param props.action - The action being resolved.\n * @returns The declared check for `action`, else the wildcard's check, else\n * `undefined` (caller falls through to `defaultPermissionMode`).\n * @internal\n */\nexport function resolveActionCheck(props: {\n resource: Record<string, unknown>;\n action: string;\n}): PermissionCheck | undefined {\n if (props.action in props.resource) {\n return props.resource[props.action] as PermissionCheck;\n }\n if (WILDCARD_KEY in props.resource) {\n return props.resource[WILDCARD_KEY] as PermissionCheck;\n }\n return undefined;\n}\n","import type { GenericDocument } from \"convex/server\";\nimport { PERMISSION_MODES, WILDCARD_KEY } from \"./constants\";\nimport { resolveActionCheck } from \"./hasPermission\";\nimport type { AccessConditionResult } from \"./constraintTypes\";\nimport { createAccessQueryBuilder, readAccessCondition } from \"./createAccessQueryBuilder\";\nimport type { AccessCondition } from \"./createAccessQueryBuilder\";\nimport {\n accessConstraintsToFilter,\n accessConstraintsToIndexRange,\n accessFilterTreeToFilter,\n} from \"./compileConstraints\";\nimport type {\n AccessFilterFn,\n PermissionCheck,\n QueryIndex,\n SubjectEntry,\n VexAccessConfig,\n} from \"./types\";\n\n/**\n * How one role's rule bears on whether a query may be narrowed.\n *\n * - `deny` — the role refuses this action, statically or by its constraints\n * callback returning `false`. It contributes nothing and is skipped.\n * - `unrestricted` — the role allows the action outright, statically or by its\n * callback returning `true`. Narrowing would hide rows it permits, so nothing\n * may be applied.\n * - `opaque` — the role allows conditionally through a bare callback (or, until\n * Step 14, a field-mode object). Which rows it permits is unknowable before\n * reading them, so again nothing may be applied.\n * - `recorded` — the role's constraints callback ran and recorded something. The\n * recording says which compile targets are available; it does NOT by itself\n * mean an index was chosen.\n *\n * @internal\n */\ntype RoleContribution =\n | { kind: \"deny\" }\n | { kind: \"unrestricted\" }\n | { kind: \"opaque\" }\n | { kind: \"conditional\"; condition: AccessCondition<GenericDocument> };\n\n/**\n * True when a resolved check is the constraint object form rather than a boolean,\n * a bare callback, or (until Step 14) a field-mode object.\n *\n * Distinguished by the presence of `constraints`. Note this is the ONLY thing a\n * type predicate can settle here: whether the rule turns out to be *indexed* is a\n * RUNTIME outcome of invoking the callback, since every check now has the same\n * `{ constraints, filter? }` shape and the index is chosen inside it by calling\n * `q.withIndex(…)`. That is why classification below runs the callback rather than\n * inspecting the object further.\n *\n * @param check - The resolved check for one role + action.\n * @returns `true` when `check` carries a `constraints` callback.\n * @internal\n */\nfunction isConstrainedCheck(\n check: PermissionCheck,\n): check is Extract<PermissionCheck, { constraints: unknown }> {\n return typeof check === \"object\" && check !== null && \"constraints\" in check;\n}\n\n/**\n * Classifies one role's rule for a resource + action, running its constraints\n * callback when it has one.\n *\n * Mirrors `hasPermission`'s resolution order exactly — subject boolean shorthand,\n * then the explicit action key or action-level wildcard via\n * {@link resolveActionCheck}, then the role-level wildcard, then\n * `defaultPermissionMode` — so a role can never be read as permitting more here\n * than it does there.\n *\n * Recording requires EXECUTING the rule, which is why `user` and `organization`\n * are threaded in. `organization` is only passed when the config declares an org\n * collection, matching `hasPermission`.\n *\n * @param props - Input props.\n * @param props.access - Resolved access config.\n * @param props.role - The role being classified.\n * @param props.resource - Subject slug.\n * @param props.action - Action on that subject.\n * @param props.user - Caller, or `null` for anonymous.\n * @param props.organization - Active organization, when configured.\n * @returns How this role bears on narrowing.\n * @internal\n */\nfunction classifyRole(props: {\n access: VexAccessConfig;\n role: string;\n resource: string;\n action: string;\n user: Record<string, unknown> | null;\n organization?: Record<string, unknown>;\n}): RoleContribution {\n const defaultAllowed = props.access.defaultPermissionMode === PERMISSION_MODES.allow;\n const roleRules = props.access.permissions[props.role];\n const subject = roleRules?.[props.resource];\n\n let check: PermissionCheck;\n if (typeof subject === \"boolean\") {\n check = subject;\n } else if (subject !== null && subject !== undefined && typeof subject === \"object\") {\n check =\n resolveActionCheck({\n resource: subject as Record<string, unknown>,\n action: props.action,\n }) ?? defaultAllowed;\n } else {\n const roleWildcard = roleRules?.[WILDCARD_KEY];\n check = typeof roleWildcard === \"boolean\" ? roleWildcard : defaultAllowed;\n }\n\n if (check === false) return { kind: \"deny\" };\n if (check === true) return { kind: \"unrestricted\" };\n if (!isConstrainedCheck(check)) return { kind: \"opaque\" };\n\n const outcome: boolean | AccessConditionResult = check.constraints({\n user: props.user ?? {},\n q: createAccessQueryBuilder(),\n ...(props.access.orgCollectionSlug !== undefined\n ? { organization: props.organization }\n : {}),\n } as unknown as Parameters<typeof check.constraints>[0]);\n\n // The callback may short-circuit to a flat allow/deny instead of returning a\n // condition — a rule reading `({ user, q }) => user.isAdmin || q.filter(…)` is the\n // common case. Classifying on the OUTCOME is strictly more precise than\n // classifying on the declaration: a rule that resolves to `true` for this caller\n // is genuinely unrestricted, and one that resolves to `false` genuinely denies.\n if (outcome === true) return { kind: \"unrestricted\" };\n if (outcome === false) return { kind: \"deny\" };\n\n const condition = readAccessCondition<GenericDocument>(outcome);\n // A condition this module cannot read did not come from `q` — treat it as\n // describing nothing rather than assuming it describes everything.\n if (condition === undefined) return { kind: \"opaque\" };\n\n return { kind: \"conditional\", condition };\n}\n\n/**\n * Selects the single recording that safely narrows one query, or `undefined` when\n * no sound narrowing exists.\n *\n * Shared by {@link resolveAccessIndex} and {@link resolveAccessConstraint} — both\n * need the identical safety rule, and a divergence between them would be a silent\n * permission difference between `find`'s indexed path and its displaced/search\n * paths. Narrowing is only sound when exactly one contributing role records\n * anything and every other role either denies outright or is itself undescribable\n * (`unrestricted` or `opaque`).\n *\n * @param props - Input props.\n * @param props.access - Resolved, enabled access config.\n * @param props.user - Caller, or `null` for anonymous (resolves through `access.anonRole`).\n * @param props.organization - Active organization, when configured.\n * @param props.resource - Subject slug.\n * @param props.action - Query-shaped action.\n * @returns The one recording that safely narrows this query, or `undefined`.\n * @internal\n */\nfunction selectSingleCondition(props: {\n access: VexAccessConfig;\n user: Record<string, unknown> | null;\n organization?: Record<string, unknown>;\n resource: string;\n action: string;\n}): AccessCondition<GenericDocument> | undefined {\n const { access } = props;\n\n const rawRoles = props.user ? props.user[access.userRolesField] : [];\n const userRoles =\n typeof rawRoles === \"string\"\n ? [rawRoles]\n : Array.isArray(rawRoles)\n ? rawRoles.filter((role): role is string => typeof role === \"string\")\n : [];\n const effectiveRoles =\n userRoles.length === 0 && access.anonRole !== undefined ? [access.anonRole] : userRoles;\n const knownRoles = effectiveRoles.filter((role) => access.roles.includes(role));\n\n // No recognized role ⇒ `hasPermission` denies outright. There is no result set to\n // narrow, so narrowing is moot.\n if (knownRoles.length === 0) return undefined;\n\n const candidates: AccessCondition<GenericDocument>[] = [];\n for (const role of knownRoles) {\n const contribution = classifyRole({\n access,\n role,\n resource: props.resource,\n action: props.action,\n user: props.user,\n organization: props.organization,\n });\n if (contribution.kind === \"deny\") continue;\n // Either of these permits rows no constraint can describe.\n if (contribution.kind === \"unrestricted\" || contribution.kind === \"opaque\") return undefined;\n candidates.push(contribution.condition);\n }\n\n // Zero ⇒ every role denied; nothing to narrow. More than one ⇒ the caller's\n // permitted set is a union, and neither candidate alone is correct.\n if (candidates.length !== 1) return undefined;\n return candidates[0];\n}\n\n/**\n * Resolves the index an access rule contributes to a query, if any.\n *\n * Called once per query, before the Convex query is built. Answers a query-scoped\n * question (\"which index narrows this query?\") rather than the document-scoped one\n * `hasPermission` answers (\"may they read this row?\").\n *\n * **Never authorizes.** The per-document `hasPermission` pass still runs, so a\n * missing or broader-than-necessary index can only cost reads — it can never admit\n * a row a role does not permit. Failing to find an index is always safe; wrongly\n * applying one is not, which is why every ambiguous case resolves to `undefined`\n * (scan) rather than to a guess.\n *\n * Returns `undefined` when the single contributing rule used the FLAT algebra\n * rather than calling `q.withIndex(…)`: there is a describable constraint, but no\n * index to push it into. {@link resolveAccessConstraint} is the export that can\n * still use it.\n *\n * @param props - Input props.\n * @param props.access - Resolved access config; absent or `enabled: false` ⇒ no index.\n * @param props.user - Caller, or `null` for anonymous (resolves through `access.anonRole`).\n * @param props.organization - Active organization, forwarded to the rule's callback.\n * @param props.resource - Subject slug (a collection or global slug).\n * @param props.action - Query-shaped action (`\"read\"` | `\"readDrafts\"`).\n * @returns The index to apply, or `undefined` to scan unnarrowed.\n *\n * @typeParam TSubjects - Resolved `SubjectMap`, inferred from `access`. Generic for\n * the same reason `HasPermissionProps` is: a concrete `SubjectMap<…>`\n * instantiation is not assignable to the erased\n * `VexAccessConfig<Record<string, SubjectEntry>>` default, because callback\n * contravariance makes instantiations mutually unassignable (P-002). Pinning the\n * default would reject every real `defineAccess()` result.\n *\n * @example\n * ```ts\n * const index = resolveAccessIndex({\n * access, user, resource: \"pages\", action: CRUD_ACTIONS.read,\n * });\n * // → { name: \"by_author\", range: (q) => q.eq(\"authorId\", \"u1\") }\n * ```\n */\nexport function resolveAccessIndex<\n TSubjects extends Record<string, SubjectEntry> = Record<string, SubjectEntry>,\n>(props: {\n access?: VexAccessConfig<TSubjects>;\n user: Record<string, unknown> | null;\n organization?: Record<string, unknown>;\n resource: string;\n action: string;\n}): QueryIndex | undefined {\n const access = props.access as VexAccessConfig | undefined;\n if (!access || !access.enabled) return undefined;\n\n const condition = selectSingleCondition({\n access,\n user: props.user,\n organization: props.organization,\n resource: props.resource,\n action: props.action,\n });\n\n if (condition?.index === undefined) return undefined;\n\n return {\n name: condition.index.name,\n range: accessConstraintsToIndexRange({ constraints: condition.index.constraints }),\n };\n}\n\n/**\n * Resolves an access rule to a `.filter()` expression, if any.\n *\n * The sibling of {@link resolveAccessIndex}, for query shapes with no `withIndex`\n * slot to claim (`search`) or where the caller's own index already claimed it\n * (`find`'s displaced branch). Both share {@link selectSingleCondition}, so they\n * cannot disagree about which rule applies.\n *\n * Succeeds for BOTH halves of a condition. An indexed rule's positional\n * constraints compile to a filter expression just as well as they do to a range —\n * the whole point of recording data rather than a callback — and a flat rule's tree\n * compiles here too. When a condition carries both, they are `and`-ed.\n *\n * @param props - Input props.\n * @param props.access - Resolved access config; absent or `enabled: false` ⇒ no filter.\n * @param props.user - Caller, or `null` for anonymous (resolves through `access.anonRole`).\n * @param props.organization - Active organization, forwarded to the rule's callback.\n * @param props.resource - Subject slug.\n * @param props.action - Query-shaped action.\n * @param props.indexAlreadyApplied - Set when the caller already pushed this rule's\n * index range down via `.withIndex(name, range)`. The range is then omitted here,\n * leaving only the `filter` half — applying it twice would be redundant work on\n * every row. Leave unset (`find`'s displaced branch, `search`, `get`) to get the\n * whole condition as one expression.\n * @returns A thunk taking the query's own `FilterBuilder`, or `undefined` when\n * nothing is left to exclude.\n */\nexport function resolveAccessConstraint<\n TSubjects extends Record<string, SubjectEntry> = Record<string, SubjectEntry>,\n>(props: {\n access?: VexAccessConfig<TSubjects>;\n user: Record<string, unknown> | null;\n organization?: Record<string, unknown>;\n resource: string;\n action: string;\n indexAlreadyApplied?: boolean;\n}): AccessFilterFn | undefined {\n const access = props.access as VexAccessConfig | undefined;\n if (!access || !access.enabled) return undefined;\n\n const condition = selectSingleCondition({\n access,\n user: props.user,\n organization: props.organization,\n resource: props.resource,\n action: props.action,\n });\n if (condition === undefined) return undefined;\n\n const { index, filter } = condition;\n\n // Two reasons to omit the index half: an index with no constraints is an\n // ordering-only opt-in and excludes nothing, and a caller that already pushed the\n // range down through `.withIndex` would otherwise re-check it per row.\n const indexConstrains =\n props.indexAlreadyApplied !== true && index !== undefined && index.constraints.length > 0;\n\n // BOTH branches may be populated, and that is not a conflict — it is\n // `.withIndex(name, range).filter(expr)`, which is Convex's own shape. Compiling\n // only one would silently discard half the rule, so when both are present they are\n // `and`-ed into a single expression.\n if (indexConstrains && filter !== undefined) {\n return (q) =>\n q.and(\n accessConstraintsToFilter({ constraints: index.constraints, q }),\n accessFilterTreeToFilter({ node: filter, q }),\n );\n }\n if (indexConstrains) {\n return (q) => accessConstraintsToFilter({ constraints: index.constraints, q });\n }\n if (filter !== undefined) {\n return (q) => accessFilterTreeToFilter({ node: filter, q });\n }\n // Recorded nothing that excludes rows.\n return undefined;\n}\n","import type { IndexRangeFn, QueryIndex } from \"./types\";\n\n/**\n * Ordered `(accessIndex, callerIndex)` pairs already warned about.\n *\n * Module-level and never cleared: the key space is the set of index-name pairs\n * a project actually declares, so it is small and fixed per process. Keyed on\n * the ordered pair rather than a single flag so two distinct displacements each\n * surface once.\n *\n * @internal\n */\nconst warnedDisplacements = new Set<string>();\n\n/**\n * Warns once per displaced pair that an access index could not be applied.\n *\n * Dev-only — silent in production, where the warning has no audience and the\n * behavior is correct regardless (the access `filter` still runs per document).\n *\n * @param props - Input props.\n * @param props.accessIndexName - The access index that lost the slot.\n * @param props.callerIndexName - The caller index that won it.\n * @internal\n */\nfunction warnDisplacedAccessIndex(props: {\n accessIndexName: string;\n callerIndexName: string;\n}): void {\n if (process.env.NODE_ENV === \"production\") return;\n const key = `${props.accessIndexName}\\u0000${props.callerIndexName}`;\n if (warnedDisplacements.has(key)) return;\n warnedDisplacements.add(key);\n console.warn(\n `[vexcms] Access index \"${props.accessIndexName}\" cannot be applied: this query ` +\n `already uses \"${props.callerIndexName}\". Convex permits one index per query, so ` +\n `the access rule falls back to a per-document check and reads scale with the ` +\n `caller's range instead. Declare a compound index covering both fields to serve ` +\n `them in one query.`,\n );\n}\n\n/**\n * Composes two optional ranges onto one builder: `first` narrows, then\n * `second` continues from where it left off.\n *\n * Convex's `IndexRangeBuilder` tracks its position in the index at the type\n * level, and `.eq()` returns the builder for the *next* field — so chaining is\n * exactly how a compound constraint is expressed. Composing opaque range\n * callbacks loses that positional type, hence the cast: each callback is typed\n * as returning the terminal `IndexRange`, but at runtime it returns the builder\n * whenever fields remain. Ordering matters — the access range owns the index\n * prefix, so it must run first.\n *\n * @param props - Input props.\n * @param props.first - Range applied first; owns the index prefix.\n * @param props.second - Range continuing from `first`'s result.\n * @returns A single range applying both in order, one of them when only one is\n * supplied, or `undefined` when neither is — an absent range means \"use this\n * index unconstrained\", which `buildQuery` expresses by omitting the arg.\n * @internal\n */\nfunction composeRanges(props: {\n first?: IndexRangeFn;\n second?: IndexRangeFn;\n}): IndexRangeFn | undefined {\n const { first, second } = props;\n if (!first) return second;\n if (!second) return first;\n return (q) => second(first(q) as Parameters<IndexRangeFn>[0]);\n}\n\n/**\n * Chooses the single index a query will use, arbitrating between an\n * access-contributed index and whatever the caller explicitly requested.\n * Convex permits exactly one `withIndex` per query, so exactly one wins.\n *\n * 1. Caller's index wins the slot when it names a DIFFERENT index — usually a\n * highly selective lookup; the access `filter` still enforces the rule per\n * document (`hasPermission`), so this only ever costs reads, never\n * correctness.\n * 2. Same name ⇒ ranges merge, so the compound constraint (access's prefix AND\n * the caller's continuation) is served by one query, no degradation.\n * 3. Free slot (no caller index) ⇒ the access index claims it — the common\n * list-view case, where the view passes no index of its own.\n *\n * Never authorizes: whichever index wins, the access rule's `filter` runs per\n * document. Arbitration decides how many rows get read, not which are allowed.\n *\n * @param props - Input props.\n * @param props.accessIndex - `resolveAccessIndex`'s result for this query, if any.\n * @param props.callerIndex - The index the caller explicitly requested, if any.\n * Its `range` is optional, mirroring `find`'s `withIndex` arg — a caller may\n * name an index purely to order results.\n * @returns The index to apply, or `undefined` when neither side supplies one\n * (an un-narrowed scan, today's behavior).\n *\n * @example\n * ```ts\n * pickQueryIndex({\n * accessIndex: { name: \"by_author\", range: (q) => q.eq(\"authorId\", \"u1\") },\n * });\n * // → { name: \"by_author\", range: … } (free slot)\n * ```\n */\nexport function pickQueryIndex(props: {\n accessIndex?: QueryIndex;\n callerIndex?: { name: string; range?: IndexRangeFn };\n}): QueryIndex | undefined {\n const { accessIndex, callerIndex } = props;\n\n // Neither side narrows — identical to today's behavior for a collection or\n // action with no access index and no caller request.\n if (!accessIndex && !callerIndex) return undefined;\n\n // Free slot: the access index takes it outright.\n if (!callerIndex) return accessIndex;\n\n // No access index to arbitrate against — the caller's request stands.\n if (!accessIndex) return callerIndex;\n\n // Same index: one query serves both constraints, no degradation.\n if (accessIndex.name === callerIndex.name) {\n return {\n name: accessIndex.name,\n range: composeRanges({ first: accessIndex.range, second: callerIndex.range }),\n };\n }\n\n // Different indexes: the caller's explicit choice wins the slot. The access\n // rule's `filter` still runs per document, so this costs reads, not safety.\n warnDisplacedAccessIndex({\n accessIndexName: accessIndex.name,\n callerIndexName: callerIndex.name,\n });\n return callerIndex;\n}\n","/**\n * Convex schema value type constants for field types.\n *\n * These constants define the base Convex validator strings for each field type.\n * They're used by schema generation logic to build the Convex schema.\n *\n * @internal\n */\n\n/** Convex schema metadata for each supported field type, keyed by field type name. */\nexport const ADMIN_FIELDS = {\n text: {\n type: \"text\",\n interfaceType: \"string\",\n validator: \"v.string()\",\n defaultValue: \"\",\n },\n url: {\n type: \"url\",\n interfaceType: \"string\",\n validator: \"v.string()\",\n defaultValue: \"\",\n },\n color: {\n type: \"color\",\n interfaceType: \"string\",\n validator: \"v.string()\",\n defaultValue: \"\",\n },\n number: {\n type: \"number\",\n interfaceType: \"number\",\n validator: \"v.number()\",\n defaultValue: 0,\n },\n checkbox: {\n type: \"checkbox\",\n interfaceType: \"boolean\",\n validator: \"v.boolean()\",\n defaultValue: false,\n },\n date: {\n type: \"date\",\n interfaceType: \"number\",\n validator: \"v.number()\",\n defaultValue: undefined,\n },\n select: {\n type: \"select\",\n interfaceType: \"string[]\",\n validator: \"v.array(\\nv.string()\\n)\",\n defaultValue: [] as string[],\n },\n relationship: {\n type: \"relationship\",\n interfaceType: \"Id<CollectionSlug>[]\",\n validator: \"v.array(\\nv.string()\\n)\",\n defaultValue: [] as string[],\n },\n array: {\n type: \"array\",\n interfaceType: \"unknown[]\",\n validator: \"v.array(\\nv.any()\\n)\",\n defaultValue: [],\n },\n group: {\n type: \"group\",\n interfaceType: \"Record<string, any>\",\n validator: \"v.object({})\",\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n defaultValue: {} as Record<string, any>,\n },\n blocks: {\n type: \"blocks\",\n interfaceType: \"Record<string, any>[]\", // placeholder - blocks() overridses per instance\n validator: \"v.array(v.any())\", // placeholder - blocksFieldToValidator builds dynamically\n defaultValue: [],\n },\n upload: {\n type: \"upload\",\n interfaceType: \"Id<MediaCollectionSlug>[]\",\n validator: \"v.array(\\nv.string()\\n)\",\n defaultValue: undefined,\n },\n // richtext: {\n // type: \"richtext\",\n // validator: \"v.any()\",\n // defaultValue: {},\n // },\n} as const;\n/** Union of all supported field type name strings (e.g. `\"text\"` | `\"number\"` | `\"checkbox\"` | ...). */\nexport type AdminFieldType = (typeof ADMIN_FIELDS)[keyof typeof ADMIN_FIELDS][\"type\"];\n/** Union of all Convex validator strings for the supported field types (e.g. `\"v.string()\"` | `\"v.number()\"` | ...). */\nexport type AdminFieldValidator = (typeof ADMIN_FIELDS)[keyof typeof ADMIN_FIELDS][\"validator\"];\n/** Union of all TypeScript type strings corresponding to the supported field types (e.g. `\"string\"` | `\"number\"` | ...). */\nexport type AdminFieldTsType = (typeof ADMIN_FIELDS)[keyof typeof ADMIN_FIELDS][\"interfaceType\"];\n\n/** Literal type `\"text\"` — the discriminant value on {@link TextField}. */\nexport type TextFieldType = typeof ADMIN_FIELDS.text.type;\n\n/** Literal type `\"url\"` — the discriminant value on {@link UrlField}. */\nexport type UrlFieldType = typeof ADMIN_FIELDS.url.type;\n\n/** Literal type `\"color\"` — the discriminant value on {@link ColorField}. */\nexport type ColorFieldType = typeof ADMIN_FIELDS.color.type;\n\n/** Literal type `\"number\"` — the discriminant value on {@link NumberField}. */\nexport type NumberFieldType = typeof ADMIN_FIELDS.number.type;\n\n/** Literal type `\"checkbox\"` — the discriminant value on {@link CheckboxField}. */\nexport type CheckboxFieldType = typeof ADMIN_FIELDS.checkbox.type;\n\n/** Literal type `\"date\"` — the discriminant value on {@link DateField}. */\nexport type DateFieldType = typeof ADMIN_FIELDS.date.type;\n\n/** Literal type `\"select\"` — the discriminant value on {@link SelectField}. */\nexport type SelectFieldType = typeof ADMIN_FIELDS.select.type;\n\n/** Literal type `\"relationship\"` — the discriminant value on {@link RelationshipField}. */\nexport type RelationshipFieldType = typeof ADMIN_FIELDS.relationship.type;\n\n/** Literal type `\"array\"` — the discriminant value on {@link ArrayField}. */\nexport type ArrayFieldType = typeof ADMIN_FIELDS.array.type;\n","import { type GenericDataModel, GenericQueryCtx } from \"convex/server\";\nimport { type GenericId } from \"convex/values\";\nimport { type VexConfig } from \"../config\";\nimport { type CollectionSlug } from \"../types\";\nimport { CRUD_ACTIONS, type CustomActionsInput, DRAFT_ACTIONS, VexAccessConfig } from \"../access\";\nimport { AccessCallOptions } from \"./types\";\n\n/**\n * Resolves the access config and action for one server-function call.\n *\n * Single seam for `access.action` / `access.bypass`. Bypass returns `undefined` for\n * `access`, which is the documented \"RBAC not configured\" path — no separate enforcement\n * branch exists or should be added.\n *\n * @param props.config - Resolved Vex config; `config.access` supplies the matrix.\n * @param props.access - Per-call overrides.\n * @param props.defaultAction - The function's natural verb.\n * @param props.resource - Subject slug: the custom-action lookup key, and the name the\n * dev warnings report.\n * @returns The access config to enforce (`undefined` when bypassed) and the action to check.\n */\nexport function resolveAccessCall<A extends string>(props: {\n config?: VexConfig;\n access?: AccessCallOptions<A>;\n defaultAction: A;\n resource: string;\n}): { access: VexAccessConfig | undefined; action: A; resource: string } {\n const configured = props.config?.access;\n const bypassed = props.access?.bypass === true;\n const action = props.access?.action ?? props.defaultAction;\n\n // Dev-only diagnostics. Neither case is unsafe — the undeclared-permission posture is\n // pinned to deny, so an action nothing declares is refused, never granted. They exist\n // because that refusal is otherwise indistinguishable from a deliberate denial, and a\n // one-character typo in an action name is invisible at the call site.\n if (process.env.NODE_ENV !== \"production\") {\n if (bypassed && configured === undefined) {\n console.warn(\n `[vexcms] access.bypass was set for \"${props.resource}\" but this call has no access ` +\n `config, so RBAC was already off and the flag changed nothing. Either drop it, or ` +\n `check that the call receives the config you expect.`,\n );\n }\n\n // Skipped when bypassed: no check runs, so the action is not consulted and warning\n // about it would be noise.\n if (!bypassed && props.access?.action !== undefined && configured !== undefined) {\n // `customActions` stays generic on the resolved config so it can carry the caller's\n // literal declaration for inference, and the wide `VexConfig.access` instantiates\n // that parameter to `{}` — so a runtime lookup by slug has to restate the value\n // shape. Read-only, and `defineAccess` has already validated the entries.\n const declaredForResource = (\n configured.customActions as Record<string, CustomActionsInput> | undefined\n )?.[props.resource];\n\n const declared = new Set<string>([\n ...Object.values(CRUD_ACTIONS),\n ...Object.values(DRAFT_ACTIONS),\n ...(declaredForResource?.query ?? []),\n ...(declaredForResource?.mutation ?? []),\n ]);\n\n if (!declared.has(action)) {\n console.warn(\n `[vexcms] Action \"${action}\" is not declared for \"${props.resource}\": it is neither ` +\n `a built-in verb nor listed in customActions[\"${props.resource}\"]. No rule can ` +\n `name it, so this call resolves to a denial. Check for a typo.`,\n );\n }\n }\n }\n\n // Bypass returns `undefined` rather than short-circuiting a check: that is the documented\n // \"RBAC not configured\" path, so every downstream consumer already handles it and no\n // second enforcement branch can drift from the first.\n return { access: bypassed ? undefined : configured, action, resource: props.resource };\n}\n\n/**\n * Resolves the collection slug that owns a document `id` by probing\n * `ctx.db.normalizeId` against every registered collection.\n *\n * A Convex `Id` does not expose its table name at runtime. `get/server.ts`'s\n * D12 comment documents the same constraint for depth-populate, where\n * degrading to \"unresolvable\" is safe (populate is simply skipped). It is\n * NOT safe here — `get`, `update`, and `remove` gate a real permission\n * check — so this resolves the slug via the `ctx.db.normalizeId(tableName, id)`\n * syscall instead of string-parsing the id. Unlike the D12 trick, this works\n * identically in `convex-test` and production Convex.\n *\n * @param props.ctx - Query or mutation context — only `ctx.db.normalizeId` is used.\n * @param props.config - The resolved `VexConfig`, to enumerate candidate collections.\n * @param props.id - The document id to resolve.\n * @returns The owning collection's slug.\n * @throws {Error} When no registered collection claims the id (e.g. an id for a\n * non-collection table like `vex_globals`, or a stale id from a deleted collection).\n * Callers gate the permission check behind this, so an unresolvable id is a hard error,\n * not a silent skip.\n */\nexport function resolveCollectionSlug<DataModel extends GenericDataModel>(props: {\n ctx: GenericQueryCtx<DataModel>;\n config?: VexConfig;\n id: GenericId<CollectionSlug>;\n}): CollectionSlug {\n for (const c of props.config?.collections ?? []) {\n if (props.ctx.db.normalizeId(c.slug, props.id) !== null) {\n return c.slug;\n }\n }\n throw new Error(\"[resolveCollectionSlug]: document id does not match a collection slug\");\n // Note: `config.collections` is small and `normalizeId` is a local syscall (no DB round\n // trip), so looping it once per `get`/`update`/`remove` request is cheap.\n}\n","import type {\n ExpressionOrValue,\n FilterBuilder,\n GenericDataModel,\n IndexNames,\n IndexRange,\n IndexRangeBuilder,\n NamedIndex,\n NamedTableInfo,\n TableNamesInDataModel,\n QueryInitializer,\n} from \"convex/server\";\n\nimport type { CollectionSlug } from \"../../types/generated\";\nimport { buildDepthPopulate } from \"../depth\";\nimport { populateDocs } from \"../populate\";\nimport type {\n DocReturnItem,\n FindReturn,\n FindReturnPaginated,\n GenericQueryServerParams,\n PaginationOptions,\n PaginationResult,\n PopulateShape,\n} from \"../types\";\nimport {\n AccessFilterFn,\n CRUD_ACTIONS,\n hasPermission,\n type IndexRangeFn,\n pickQueryIndex,\n type QueryIndex,\n resolveAccessConstraint,\n resolveAccessIndex,\n} from \"../../access\";\nimport { resolveAccessCall } from \"../utils\";\n\n/**\n * Server-side args for `find`. Extends {@link GenericQueryServerParams}\n * to inherit `ctx`, `populate`, `depth`, and `config` (with their mutual-exclusion\n * constraints). All query-chain options map to `ctx.db.query()` equivalents\n * applied in order: `withIndex` → `order` → `filter` → `take`.\n *\n * @typeParam DataModel - The Convex data model (inferred from `ctx`).\n * @typeParam TCollectionSlug - Collection slug.\n * @typeParam TPopulate - Populate object.\n * @typeParam D - Depth literal (0 = no depth, default).\n */\nexport interface FindServerArgs<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n TPopulate extends PopulateShape<TCollectionSlug>,\n D extends number = 0,\n> extends GenericQueryServerParams<DataModel, TCollectionSlug, TPopulate, D> {\n /** The collection to query — must match a registered collection slug. */\n collection: TCollectionSlug;\n\n /**\n * Maximum number of documents to return. Defaults to 100.\n * Applied as the terminal `.take(n)` on the Convex query.\n */\n limit?: number;\n\n /**\n * Result ordering. Equivalent to `.order(\"asc\" | \"desc\")` on the Convex\n * query. Defaults to `\"asc\"` (insertion order).\n */\n order?: \"asc\" | \"desc\";\n\n /**\n * Filter predicate applied after index narrowing. Equivalent to\n * `.filter(q => q.eq(q.field(\"published\"), true))` on the Convex query.\n *\n * Prefer `withIndex` for performance — `filter` scans every document\n * in the range and is O(n). Use `filter` for secondary conditions that\n * can't be expressed as index equality ranges.\n *\n * @example\n * ```ts\n * find({ ctx, collection: \"posts\", filter: q => q.eq(q.field(\"published\"), true) })\n * ```\n */\n filter?: (\n q: FilterBuilder<\n NamedTableInfo<\n DataModel,\n TCollectionSlug extends TableNamesInDataModel<DataModel> ? TCollectionSlug : never\n >\n >,\n ) => ExpressionOrValue<boolean>;\n\n /**\n * Index to use for the query, with an optional equality/range constraint.\n * Equivalent to `.withIndex(name, range?)` on the Convex query.\n *\n * Using an index is strongly preferred over `filter` for performance.\n *\n * @example\n * ```ts\n * find({ ctx, collection: \"posts\", withIndex: { name: \"by_slug\", range: q => q.eq(\"slug\", \"hello\") } })\n * find({ ctx, collection: \"posts\", withIndex: { name: \"by_score\", range: q => q.gte(\"score\", 50) } })\n * find({ ctx, collection: \"posts\", withIndex: { name: \"by_publishedAt\" } })\n * ```\n */\n withIndex?: TCollectionSlug extends TableNamesInDataModel<DataModel>\n ? {\n name: IndexNames<NamedTableInfo<DataModel, TCollectionSlug>>;\n range?: (\n q: IndexRangeBuilder<\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n any,\n NamedIndex<\n NamedTableInfo<DataModel, TCollectionSlug>,\n IndexNames<NamedTableInfo<DataModel, TCollectionSlug>>\n >\n >,\n ) => IndexRange;\n }\n : never;\n\n /**\n * Pagination options. When provided, the function returns a `PaginationResult`\n * with `{ page, continueCursor, isDone }` instead of a plain array.\n *\n * Uses Convex's native `.paginate(opts)` API under the hood.\n */\n paginationOpts?: PaginationOptions;\n}\n\n// Overload 1: WITHOUT paginationOpts → returns array (most common case)\nexport async function find<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n>(\n args: FindServerArgs<DataModel, TCollectionSlug, TPopulate, D> & {\n paginationOpts?: never;\n },\n // Inlined rather than `FindReturn<…>` so hover resolves to `Doc[]` — see the\n // display/assignability note in `../types`.\n): Promise<DocReturnItem<TCollectionSlug, TPopulate, D>[]>;\n\n// Overload 2: WITH paginationOpts → returns PaginationResult\nexport async function find<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n>(\n args: FindServerArgs<DataModel, TCollectionSlug, TPopulate, D> & {\n paginationOpts: PaginationOptions;\n },\n): Promise<PaginationResult<DocReturnItem<TCollectionSlug, TPopulate, D>>>;\n\n/**\n * Find documents in a collection with optional pagination and total count.\n *\n * When `includeTotalCount=true`, runs `.collect()` on the first page to count all\n * matching documents. Returns `null` if count exceeds 32k documents.\n *\n * @param args - Find arguments including filters, sort, pagination, and count options\n * @returns Array of documents or PaginationResult with optional totalCount\n *\n * @example\n * ```ts\n * // Without pagination\n * const docs = await find({ ctx, collection: \"posts\" });\n *\n * // With pagination\n * const result = await find({\n * ctx,\n * collection: \"posts\",\n * paginationOpts: { numItems: 100, cursor: null },\n * });\n * // result: { page: [...], continueCursor: \"...\", isDone: false }\n *\n * // With pagination and count (first page only)\n * const result = await find({\n * ctx,\n * collection: \"posts\",\n * paginationOpts: { numItems: 100, cursor: null },\n * includeTotalCount: true,\n * });\n * // result: { page: [...], continueCursor: \"...\", isDone: false, totalCount: 1523 }\n * ```\n */\nexport async function find<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n>(\n args: FindServerArgs<DataModel, TCollectionSlug, TPopulate, D>,\n): Promise<\n FindReturn<TCollectionSlug, TPopulate, D> | FindReturnPaginated<TCollectionSlug, TPopulate, D>\n> {\n const { access, action, resource } = resolveAccessCall({\n config: args.config,\n access: args.access,\n defaultAction: CRUD_ACTIONS.read,\n resource: args.collection,\n });\n const accessIndex = resolveAccessIndex({\n access,\n user: args.auth?.user ?? null,\n organization: args.auth?.organization,\n resource,\n action,\n });\n // `FindServerArgs.withIndex` is a conditional type over `TCollectionSlug`,\n // which is still an unresolved type parameter here — TypeScript cannot reduce\n // it, so it stays opaque and will not assign to a concrete object type. The\n // runtime shape is exactly `{ name, range? }`, which is what `pickQueryIndex`\n // takes; the index name and range are already checked against the collection\n // at the public call site.\n const callerIndex = args.withIndex as { name: string; range?: IndexRangeFn } | undefined;\n const resolvedIndex = pickQueryIndex({ accessIndex, callerIndex });\n // Resolve the constraint UNCONDITIONALLY. Two cases were previously missed, both\n // silent because the per-document `hasPermission` pass still rejected the rows —\n // correctness held, but the query read them anyway, which is exactly the ragged\n // page this design exists to remove:\n // 1. a rule with no index at all (the flat algebra, and the only form an\n // action-level wildcard can express) never reached the query;\n // 2. a rule with an index AND a filter lost its filter half whenever the access\n // index won the slot.\n // `indexAlreadyApplied` is what keeps the range from being applied twice when the\n // access index did win.\n const accessFilter = resolveAccessConstraint({\n access,\n user: args.auth?.user ?? null,\n organization: args.auth?.organization,\n resource,\n action,\n indexAlreadyApplied: accessIndex !== undefined && resolvedIndex?.name === accessIndex.name,\n });\n\n const findQuery = buildQuery<DataModel, TCollectionSlug, TPopulate, D>({\n ...args,\n resolvedIndex,\n accessFilter,\n });\n\n // 4. paginate OR take\n let docs;\n let convexPaginationResult: Awaited<ReturnType<typeof findQuery.paginate>> | undefined;\n if (args.paginationOpts) {\n convexPaginationResult = await findQuery.paginate(args.paginationOpts);\n docs = convexPaginationResult.page.filter((d) =>\n hasPermission({\n access,\n resource,\n action,\n data: d,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n }),\n );\n } else if (args.limit) {\n docs = (await findQuery.take(args.limit)).filter((d) =>\n hasPermission({\n access,\n resource,\n action,\n data: d,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n }),\n );\n } else {\n docs = (await findQuery.collect()).filter((d) =>\n hasPermission({\n access,\n resource,\n action,\n data: d,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n }),\n );\n }\n\n // Explicit populate takes precedence over depth (D11).\n const effectivePopulate =\n args.populate ??\n (args.depth !== undefined && args.depth > 0 && args.config\n ? buildDepthPopulate<TPopulate>(args.config, args.collection, args.depth)\n : undefined);\n\n const finalDocs =\n !effectivePopulate || Object.keys(effectivePopulate).length === 0\n ? (docs as unknown as FindReturn<TCollectionSlug, TPopulate, D>)\n : ((await populateDocs(args.ctx, docs, effectivePopulate)) as unknown as FindReturn<\n TCollectionSlug,\n TPopulate,\n D\n >);\n\n if (args.paginationOpts && convexPaginationResult) {\n if (args.paginationOpts.totalDocs && !args.paginationOpts.cursor) {\n try {\n if (convexPaginationResult.isDone) {\n return {\n ...convexPaginationResult,\n page: finalDocs,\n totalDocs: finalDocs.length,\n };\n } else {\n // Build same query (with filters) but collect all to count\n const countQuery = buildQuery({\n ...args,\n resolvedIndex,\n accessFilter,\n }); // Same filters as main query\n const totalDocs = (await countQuery.collect()).filter((d) =>\n hasPermission({\n access,\n resource,\n action,\n data: d,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n }),\n );\n return {\n ...convexPaginationResult,\n page: finalDocs,\n totalDocs: totalDocs.length,\n };\n }\n } catch (error) {\n // .collect() failed (>32k docs or other limit)\n console.warn(\"Failed to collect all documents:\", error);\n return {\n ...convexPaginationResult,\n page: finalDocs,\n totalDocs: null, // Signals \"too large to count\"\n };\n }\n }\n\n // Return Convex pagination result directly with populated docs\n return {\n ...convexPaginationResult,\n page: finalDocs,\n };\n }\n\n return finalDocs;\n}\n\nfunction buildQuery<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n>(\n props: FindServerArgs<DataModel, TCollectionSlug, TPopulate, D> & {\n resolvedIndex?: QueryIndex;\n accessFilter?: AccessFilterFn;\n },\n): QueryInitializer<NamedTableInfo<DataModel, TCollectionSlug>> {\n const tableName = props.collection;\n let q = props.ctx.db.query(tableName);\n\n // 1. withIndex — narrows the scan (most efficient).\n if (props.resolvedIndex) {\n // @ts-expect-error building query piece by piece from query args\n q = props.resolvedIndex.range\n ? q.withIndex(props.resolvedIndex.name, props.resolvedIndex.range)\n : q.withIndex(props.resolvedIndex.name);\n }\n // 2. order — applied after index selection.\n // @ts-expect-error building query piece by piece from query args\n if (props.order) q = q.order(props.order);\n // 3. filter — secondary predicate, full range scan.\n if (props.accessFilter && props.filter) {\n q = q.filter((filterQ) => filterQ.and(props.accessFilter!(filterQ), props.filter!(filterQ)));\n } else if (props.accessFilter) {\n q = q.filter(props.accessFilter);\n } else if (props.filter) {\n q = q.filter(props.filter);\n }\n\n return q;\n}\n","import type { GenericDataModel } from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nimport type { CollectionSlug } from \"../../types/generated\";\nimport { buildDepthPopulate } from \"../depth\";\nimport { populateDocs } from \"../populate\";\nimport type { GenericQueryServerParams, GetReturn, PopulateShape } from \"../types\";\nimport { CRUD_ACTIONS, hasPermission } from \"../../access\";\nimport { resolveAccessCall } from \"../utils\";\n\n/**\n * Server-side args for `get`.\n *\n * Inherits `ctx`, `populate`, `depth`, and `config` (with mutual-exclusion\n * constraints) from {@link GenericQueryServerParams}.\n *\n * @typeParam DataModel - The Convex data model (inferred from `ctx`).\n * @typeParam TCollectionSlug - Collection slug.\n * @typeParam TPopulate - Populate object.\n * @typeParam D - Depth literal (0 = no depth, default).\n */\nexport interface GetServerArgs<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n TPopulate extends PopulateShape<TCollectionSlug>,\n D extends number = 0,\n> extends GenericQueryServerParams<DataModel, TCollectionSlug, TPopulate, D> {\n /** The document ID to fetch. */\n id: GenericId<TCollectionSlug>;\n /** The collection slug to patch this document. */\n collection: TCollectionSlug;\n}\n\n/**\n * Fetches a single document by its `Id<TCollectionSlug>`. Server-side only.\n *\n * Pass `populate` to explicitly name the relationship fields to resolve\n * (documented, recommended). Pass `depth` (with `config`) to automatically\n * populate all relationship fields to N levels — internal use only.\n *\n * Import from `@vexcms/core/server`. For the client-side version, import\n * `get` from `@vexcms/core/client`.\n *\n * @typeParam DataModel - Convex data model (inferred from `args.ctx`).\n * @typeParam TCollectionSlug - Collection slug, recovered from the `Id` brand.\n * @typeParam TPopulate - Populate object.\n * @typeParam D - Depth literal (0 = none).\n * @param args - `{ ctx, id, populate? }` or `{ ctx, id, depth, config }`.\n * @returns Promise resolving to the doc or `null` if not found.\n * @example\n * ```ts\n * import { get } from \"@vexcms/core/server\";\n *\n * export const post = query({\n * args: { id: v.id(\"posts\") },\n * handler: (ctx, args) => get({ ctx, id: args.id, populate: { author: true } }),\n * });\n * ```\n */\nexport async function get<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n>(\n args: GetServerArgs<DataModel, TCollectionSlug, TPopulate, D>,\n): Promise<GetReturn<TCollectionSlug, TPopulate, D>> {\n const doc = await args.ctx.db.get(args.id);\n if (doc && args.config?.access !== undefined) {\n const { access, action, resource } = resolveAccessCall({\n config: args.config,\n access: args.access,\n defaultAction: CRUD_ACTIONS.read,\n resource: args.collection,\n });\n hasPermission({\n throwOnDenied: true,\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n data: doc,\n });\n }\n\n // Resolve slug for buildDepthPopulate from the Id (D12).\n //\n // `Id<TableName>` is a TypeScript phantom type — `__tableName` is purely\n // compile-time and is NOT a runtime property on the string. To extract the\n // table name at runtime we use two strategies in priority order:\n //\n // 1. TypeScript ecosystem environments that DO materialise `__tableName`\n // as a runtime property (e.g. custom serialisers, some DX tooling).\n // 2. The `\"{random};{tableName}\"` format used by `convex-test`, which\n // mirrors the extraction in `convex-test`'s own `tableNameFromId`.\n //\n // In production Convex, IDs are opaque base32 strings without a semicolon,\n // so the split returns a single element and `tableSlug` falls back to\n // `undefined` — depth silently degrades to no-populate, which is safe.\n\n const effectivePopulate =\n args.populate ??\n (args.depth !== undefined && args.depth > 0 && args.config && args.collection\n ? buildDepthPopulate<TPopulate>(args.config, args.collection, args.depth)\n : undefined);\n\n if (!effectivePopulate || !doc) {\n return doc as unknown as GetReturn<TCollectionSlug, TPopulate, D>;\n }\n\n const [populated] = await populateDocs<DataModel, TCollectionSlug, TPopulate>(\n args.ctx,\n [doc],\n effectivePopulate,\n );\n\n return (populated ?? null) as unknown as GetReturn<TCollectionSlug, TPopulate, D>;\n}\n","import type {\n DocumentByInfo,\n ExpressionOrValue,\n FilterBuilder,\n GenericDataModel,\n NamedTableInfo,\n QueryInitializer,\n TableNamesInDataModel,\n} from \"convex/server\";\n\nimport type { CollectionSlug } from \"../../types/generated\";\nimport { buildDepthPopulate } from \"../depth\";\nimport { populateDocs } from \"../populate\";\nimport type {\n DocReturnItem,\n GenericQueryServerParams,\n PaginationOptions,\n PaginationResult,\n PopulateShape,\n SearchReturn,\n SearchReturnPaginated,\n} from \"../types\";\nimport { AccessFilterFn, CRUD_ACTIONS, hasPermission, resolveAccessConstraint } from \"../../access\";\nimport { resolveAccessCall } from \"../utils\";\n\n/**\n * Server-side args for `search`.\n *\n * Inherits `ctx`, `populate`, `depth`, and `config` (with mutual-exclusion\n * constraints) from {@link GenericQueryServerParams}.\n *\n * @typeParam DataModel - The Convex data model (inferred from `ctx`).\n * @typeParam TCollectionSlug - Collection slug.\n * @typeParam TPopulate - Populate object.\n * @typeParam D - Depth literal (0 = no depth, default).\n */\nexport interface SearchServerArgs<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n TPopulate extends PopulateShape<TCollectionSlug>,\n D extends number = 0,\n> extends GenericQueryServerParams<DataModel, TCollectionSlug, TPopulate, D> {\n /** The collection to search — must match a registered collection slug. */\n collection: TCollectionSlug;\n /** The search text. Pass `\"\"` to list recent documents (falls back to `.take()`). */\n query: string;\n /** The `.searchIndex()` name declared in the Convex schema (e.g. `\"search_name\"`). */\n searchIndexName: string;\n /** The field the search index is built on. Must match `searchField` in the index declaration. */\n searchField: string;\n /** Maximum number of results. Defaults to 20. */\n limit?: number;\n /**\n * Pagination options. When provided, the function returns a `PaginationResult`\n * with `{ page, continueCursor, isDone }` instead of a plain array.\n *\n * Uses Convex's native `.paginate(opts)` API under the hood.\n */\n paginationOpts?: PaginationOptions;\n /**\n * Filter predicate applied after index narrowing. Equivalent to\n * `.filter(q => q.eq(q.field(\"published\"), true))` on the Convex query.\n *\n * Prefer `withIndex` for performance — `filter` scans every document\n * in the range and is O(n). Use `filter` for secondary conditions that\n * can't be expressed as index equality ranges.\n *\n * @example\n * ```ts\n * find({ ctx, collection: \"posts\", filter: q => q.eq(q.field(\"published\"), true) })\n * ```\n */\n filter?: (\n q: FilterBuilder<\n NamedTableInfo<\n DataModel,\n TCollectionSlug extends TableNamesInDataModel<DataModel> ? TCollectionSlug : never\n >\n >,\n ) => ExpressionOrValue<boolean>;\n}\n\n// Overload 1: WITHOUT paginationOpts → returns array (most common case)\nexport async function search<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n>(\n args: SearchServerArgs<DataModel, TCollectionSlug, TPopulate, D> & {\n paginationOpts?: never;\n },\n // Inlined rather than `SearchReturn<…>` so hover resolves to `Doc[]` — see the\n // display/assignability note in `../types`.\n): Promise<DocReturnItem<TCollectionSlug, TPopulate, D>[]>;\n\n// Overload 2: WITH paginationOpts → returns PaginationResult\nexport async function search<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n>(\n args: SearchServerArgs<DataModel, TCollectionSlug, TPopulate, D> & {\n paginationOpts: PaginationOptions;\n },\n): Promise<PaginationResult<DocReturnItem<TCollectionSlug, TPopulate, D>>>;\n\n/**\n * Search documents in a collection with optional pagination and total count.\n *\n * When `includeTotalCount=true`, runs `.collect()` on the first page to count all\n * matching search results. Returns `null` if count exceeds 32k documents.\n *\n * @param args - Search arguments including query, filters, pagination, and count options\n * @returns Array of documents or PaginationResult with optional totalCount\n *\n * @example\n * ```ts\n * // With pagination and count (first page only)\n * const result = await search({\n * ctx,\n * collection: \"posts\",\n * query: \"react hooks\",\n * searchIndexName: \"search_posts\",\n * searchField: \"title\",\n * paginationOpts: { numItems: 100, cursor: null },\n * includeTotalCount: true,\n * });\n * // result: { page: [...], continueCursor: \"...\", isDone: false, totalCount: 42 }\n * ```\n */\nexport async function search<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n>(\n args: SearchServerArgs<DataModel, TCollectionSlug, TPopulate, D>,\n): Promise<\n SearchReturn<TCollectionSlug, TPopulate, D> | SearchReturnPaginated<TCollectionSlug, TPopulate, D>\n> {\n const { access, action, resource } = resolveAccessCall({\n config: args.config,\n access: args.access,\n defaultAction: CRUD_ACTIONS.read,\n resource: args.collection,\n });\n const accessFilter = resolveAccessConstraint({\n access,\n user: args.auth?.user ?? null,\n organization: args.auth?.organization,\n resource,\n action,\n });\n const searchQuery = buildQuery({\n ...args,\n accessFilter,\n });\n\n let docs: DocumentByInfo<NamedTableInfo<DataModel, string>>[];\n let convexPaginationResult: Awaited<ReturnType<typeof searchQuery.paginate>> | undefined;\n if (args.paginationOpts) {\n convexPaginationResult = await searchQuery.paginate(args.paginationOpts);\n docs = convexPaginationResult.page.filter((d) =>\n hasPermission({\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n data: d,\n }),\n );\n } else if (args.limit) {\n docs = (await searchQuery.take(args.limit)).filter((d) =>\n hasPermission({\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n data: d,\n }),\n );\n } else {\n docs = (await searchQuery.collect()).filter((d) =>\n hasPermission({\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n data: d,\n }),\n );\n }\n\n const effectivePopulate =\n args.populate ??\n (args.depth !== undefined && args.depth > 0 && args.config\n ? buildDepthPopulate<TPopulate>(args.config, args.collection, args.depth)\n : undefined);\n\n const finalDocs =\n !effectivePopulate || Object.keys(effectivePopulate).length === 0\n ? (docs as DocReturnItem<TCollectionSlug, TPopulate, D>[])\n : ((await populateDocs(args.ctx, docs, effectivePopulate)) as DocReturnItem<\n TCollectionSlug,\n TPopulate,\n D\n >[]);\n\n if (args.paginationOpts) {\n if (args.paginationOpts.totalDocs && !args.paginationOpts.cursor) {\n try {\n // Build same search query but collect all to count\n const countQuery = buildQuery(args); // Same search params\n const allDocs = (await countQuery.collect()).filter((d) =>\n hasPermission({\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n data: d,\n }),\n );\n\n if (convexPaginationResult) {\n return {\n ...convexPaginationResult,\n page: finalDocs,\n totalDocs: allDocs.length,\n };\n }\n } catch (error) {\n console.warn(\"Failed to count search results:\", error);\n if (convexPaginationResult) {\n return {\n ...convexPaginationResult,\n page: finalDocs,\n totalDocs: null,\n };\n }\n }\n }\n\n if (convexPaginationResult) {\n // Return Convex pagination result directly with populated docs\n return {\n ...convexPaginationResult,\n page: finalDocs as DocReturnItem<TCollectionSlug, TPopulate, D>[],\n };\n }\n }\n\n return finalDocs as DocReturnItem<TCollectionSlug, TPopulate, D>[];\n}\n\nfunction buildQuery<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n const TPopulate extends PopulateShape<TCollectionSlug> = Record<string, never>,\n const D extends number = 0,\n>(\n args: SearchServerArgs<DataModel, TCollectionSlug, TPopulate, D> & {\n accessFilter?: AccessFilterFn;\n },\n): QueryInitializer<NamedTableInfo<DataModel, TCollectionSlug>> {\n let q = args.ctx.db.query(args.collection);\n if (args.query) {\n // @ts-expect-error building query piece by piece from query args\n q = q.withSearchIndex(args.searchIndexName, (q) => q.search(args.searchField, args.query));\n }\n if (args.accessFilter && args.filter) {\n q = q.filter((fq) => fq.and(args.accessFilter!(fq), args.filter!(fq)));\n } else if (args.accessFilter) {\n q = q.filter(args.accessFilter);\n } else if (args.filter) {\n q = q.filter(args.filter);\n }\n return q;\n}\n","import type {\n BetterOmit,\n DocumentByName,\n Expand,\n GenericDataModel,\n TableNamesInDataModel,\n} from \"convex/server\";\n\nimport type { CollectionSlug } from \"../../types/generated\";\nimport type { GenericMutationServerParams } from \"../types\";\nimport { CRUD_ACTIONS, hasPermission } from \"../../access\";\nimport { resolveAccessCall } from \"../utils\";\n\n/**\n * Server-side args for `create`.\n *\n * @typeParam DataModel - The Convex data model (inferred from `ctx`).\n * @typeParam TCollectionSlug - Collection slug.\n */\nexport interface CreateServerArgs<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n> extends GenericMutationServerParams<DataModel> {\n /** The collection slug to insert into. */\n collection: TCollectionSlug;\n /**\n * Field values for the new document. `_id` and `_creationTime` are\n * excluded — Convex assigns these automatically.\n *\n * Passed through `v.any()` at the network boundary; CLI codegen validates\n * the shape against the Convex schema at build time.\n */\n data: Expand<\n BetterOmit<DocumentByName<DataModel, TableNamesInDataModel<DataModel>>, \"_creationTime\" | \"_id\">\n >;\n}\n\n/**\n * Inserts a document into a VexCMS collection and returns its ID.\n * Server-side only — call inside a Convex mutation handler.\n *\n * Import from `@vexcms/core/server`.\n *\n * @typeParam DataModel - Convex data model (inferred from `args.ctx`).\n * @typeParam TCollectionSlug - Collection slug.\n * @param args - `{ ctx, collection, data }`. `ctx` must be a mutation context.\n * @returns Promise resolving to the new document's ID as a string.\n * @example\n * ```ts\n * import { create } from \"@vexcms/core/server\";\n *\n * export const createPost = mutation({\n * args: { data: v.any() },\n * handler: (ctx, args) => create({ ctx, collection: \"posts\", data: args.data }),\n * });\n * ```\n */\nexport async function create<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n>(args: CreateServerArgs<DataModel, TCollectionSlug>): Promise<string> {\n if (args.config.access !== undefined) {\n // The PAYLOAD is the authorization subject here, unlike `update`/`remove`/`get`,\n // which authorize against the stored row. There is no stored row yet, and the\n // payload is exactly what is about to become one — so a per-document rule\n // (`({ data }) => data.status !== \"published\"`, say) has to see it. Without this\n // every payload-dependent rule on `create` denied unconditionally: the capability\n // probe detected the `data` read and, under the default `scope: \"all\"`, answered\n // \"cannot hold for every document\" — which is the wrong question for a create.\n //\n // The payload-hijack concern that makes `update` use the stored row does not\n // apply: there is nothing to protect from being misrepresented, since the\n // caller's values ARE the row being authorized.\n const { access, action, resource } = resolveAccessCall({\n config: args.config,\n access: args.access,\n defaultAction: CRUD_ACTIONS.create,\n resource: args.collection,\n });\n hasPermission({\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n data: args.data,\n throwOnDenied: true,\n });\n }\n const id = await args.ctx.db.insert(\n args.collection as TableNamesInDataModel<DataModel>,\n args.data,\n );\n return id;\n}\n","import type {\n BetterOmit,\n DocumentByName,\n Expand,\n GenericDataModel,\n TableNamesInDataModel,\n} from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nimport type { CollectionSlug } from \"../../types/generated\";\nimport type { GenericMutationServerParams } from \"../types\";\nimport { CRUD_ACTIONS, hasPermission } from \"../../access\";\nimport { resolveAccessCall } from \"../utils\";\n\n/**\n * Server-side args for `update`.\n *\n * @typeParam DataModel - The Convex data model (inferred from `ctx`).\n * @typeParam TCollectionSlug - Collection slug, recovered from the `Id` brand.\n */\nexport interface UpdateServerArgs<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n> extends GenericMutationServerParams<DataModel> {\n /** The collection slug to patch this document. */\n collection: TCollectionSlug;\n /** The document ID to patch. */\n id: GenericId<TCollectionSlug>;\n /**\n * Partial field values to merge into the document. Only the keys present\n * here are written; unspecified fields are left unchanged. `_id` and\n * `_creationTime` are excluded — Convex manages them.\n *\n * Passed through `v.any()` at the network boundary; CLI codegen validates\n * the shape against the Convex schema at build time.\n */\n data: Partial<\n Expand<\n BetterOmit<\n DocumentByName<DataModel, TableNamesInDataModel<DataModel>>,\n \"_creationTime\" | \"_id\"\n >\n >\n >;\n}\n\n/**\n * Patches a document by its `Id<TCollectionSlug>`. Only specified fields are updated;\n * unspecified fields are left unchanged. Server-side only.\n *\n * Import from `@vexcms/core/server`.\n *\n * @typeParam DataModel - Convex data model (inferred from `args.ctx`).\n * @typeParam TCollectionSlug - Collection slug.\n * @param args - `{ ctx, id, data }`. `ctx` must be a mutation context.\n * @returns Promise resolving to void.\n * @example\n * ```ts\n * import { update } from \"@vexcms/core/server\";\n *\n * export const updatePost = mutation({\n * args: { id: v.id(\"posts\"), data: v.any() },\n * handler: (ctx, args) => update({ ctx, id: args.id, data: args.data }),\n * });\n * ```\n */\nexport async function update<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n>(args: UpdateServerArgs<DataModel, TCollectionSlug>): Promise<void> {\n if (args.config.access !== undefined) {\n // Authorize against the STORED document, never `args.data`. The patch is\n // caller-controlled, so checking it would let a per-document rule be\n // satisfied by the payload rather than by the resource being protected\n // (e.g. `update: ({ data }) => !data.src.includes(\"example.com\")` would pass\n // for a protected row simply by sending a different `src`). Matches the\n // behaviour of `get`, `find`, and `remove`.\n const doc = await args.ctx.db.get(args.id);\n const { access, action, resource } = resolveAccessCall({\n config: args.config,\n access: args.access,\n defaultAction: CRUD_ACTIONS.update,\n resource: args.collection,\n });\n hasPermission({\n throwOnDenied: true,\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n data: doc ?? undefined,\n });\n }\n await args.ctx.db.patch(args.id, args.data);\n}\n","import type { GenericDataModel } from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nimport type { CollectionSlug } from \"../../types/generated\";\nimport type { GenericMutationServerParams } from \"../types\";\nimport { CRUD_ACTIONS, hasPermission } from \"../../access\";\nimport { resolveAccessCall } from \"../utils\";\n\n/**\n * Server-side args for `remove`.\n *\n * @typeParam DataModel - The Convex data model (inferred from `ctx`).\n * @typeParam TCollectionSlug - Collection slug, recovered from the `Id` brand.\n */\nexport interface RemoveServerArgs<\n DataModel extends GenericDataModel,\n TCollectionSlug extends CollectionSlug,\n> extends GenericMutationServerParams<DataModel> {\n /** The collection slug to insert into. */\n collection: TCollectionSlug;\n /**\n * Document ID(s) to delete.\n * Pass a single ID in an array for one document, or multiple IDs for bulk delete.\n */\n ids: GenericId<TCollectionSlug>[];\n /**\n * Optional soft delete field name.\n * If provided, sets this field to `true` instead of permanently deleting.\n * @example \"deleted\" — sets { deleted: true } on the document(s)\n */\n softDelete?: string;\n}\n\n/**\n * Deletes one or more documents. Server-side only.\n * Named `remove` to avoid collision with the JavaScript `delete` keyword.\n *\n * Supports both hard delete (permanent) and soft delete (mark as deleted).\n * Pass `softDelete` field name to soft delete instead of permanently removing.\n *\n * Import from `@vexcms/core/server`.\n *\n * @typeParam DataModel - Convex data model (inferred from `args.ctx`).\n * @typeParam TCollectionSlug - Collection slug.\n * @param args - `{ ctx, ids, softDelete? }`. `ctx` must be a mutation context.\n * @returns Promise resolving to void.\n * @example Single delete\n * ```ts\n * import { remove } from \"@vexcms/core/server\";\n *\n * export const deletePost = mutation({\n * args: { id: v.id(\"posts\") },\n * handler: (ctx, args) => remove({ ctx, ids: [args.id] }),\n * });\n * ```\n * @example Bulk delete\n * ```ts\n * export const bulkDeletePosts = mutation({\n * args: { ids: v.array(v.id(\"posts\")) },\n * handler: (ctx, args) => remove({ ctx, ids: args.ids }),\n * });\n * ```\n * @example Soft delete\n * ```ts\n * export const softDeletePost = mutation({\n * args: { id: v.id(\"posts\") },\n * handler: (ctx, args) =>\n * remove({ ctx, ids: [args.id], softDelete: \"deleted\" }),\n * });\n * ```\n */\nexport async function remove<\n DataModel extends GenericDataModel = GenericDataModel,\n TCollectionSlug extends CollectionSlug = CollectionSlug,\n>(args: RemoveServerArgs<DataModel, TCollectionSlug>): Promise<void> {\n async function removeById(id: GenericId<TCollectionSlug>): Promise<void> {\n if (args.config.access !== undefined) {\n const doc = await args.ctx.db.get(id);\n const { access, action, resource } = resolveAccessCall({\n config: args.config,\n access: args.access,\n defaultAction: CRUD_ACTIONS.delete,\n resource: args.collection,\n });\n hasPermission({\n throwOnDenied: true,\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n data: doc ?? undefined,\n });\n }\n if (args.softDelete) {\n return await args.ctx.db.patch(args.collection, id, { [args.softDelete as never]: true });\n }\n return await args.ctx.db.delete(args.collection, id);\n }\n\n await Promise.all(args.ids.map((id) => removeById(id)));\n}\n","import type { GenericDataModel } from \"convex/server\";\n\nimport type {\n GlobalSlug,\n GlobalDocumentBySlug,\n GlobalPopulateShape,\n GlobalPopulated,\n VexDocumentGlobal,\n} from \"../../types/generated\";\nimport { populateDocs } from \"../populate\";\nimport { buildDepthPopulate } from \"../depth\";\nimport type { Prettify } from \"../types\";\nimport { CRUD_ACTIONS, hasPermission } from \"../../access\";\nimport { GenericGlobalsQueryServerArgs } from \"./types\";\nimport { resolveAccessCall } from \"../utils\";\n\n/**\n * Flattens a raw `vex_globals` DB row into the API-facing flat document.\n * Lifts `data` fields to root, renames `slug` → `_slug`.\n * @param row the global document as returned from convex usign a single collection for globals\n * @returns the flattened global object data type including its metadata fields\n */\nfunction flattenGlobalRow(row: Record<string, unknown>): Record<string, unknown> {\n const { slug, data, _id, _creationTime } = row as {\n slug: string;\n data: Record<string, unknown>;\n _id: string;\n _creationTime: number;\n };\n return { _id, _creationTime, _slug: slug, ...(data ?? {}) };\n}\n\n/**\n * Server-side args for `getGlobal`. Populate and depth are mutually exclusive.\n *\n * @typeParam DataModel - Convex data model.\n * @typeParam TSlug - Global slug.\n * @typeParam TPopulate - Populate shape.\n * @typeParam D - Depth literal.\n */\nexport interface GetGlobalServerArgs<\n DataModel extends GenericDataModel,\n TGlobalSlug extends GlobalSlug = GlobalSlug,\n TPopulate extends GlobalPopulateShape<TGlobalSlug> = Record<string, never>,\n D extends number = 0,\n> extends GenericGlobalsQueryServerArgs<DataModel, TGlobalSlug> {\n /** Global slug to fetch. Narrowed to `GlobalSlug` after `vex generate`. */\n slug: TGlobalSlug;\n /** Relationship fields to populate. Mutually exclusive with `depth`. */\n populate?: [D] extends [0] ? TPopulate : never;\n /** Auto-populate all relationship fields to N levels. Mutually exclusive with `populate`. */\n depth?: [TPopulate] extends [Record<string, never>] ? D : never;\n}\n\n/**\n * Return type of `getGlobal` — narrows by populate/depth presence.\n */\nexport type GetGlobalReturn<\n TSlug extends GlobalSlug,\n TPopulate extends GlobalPopulateShape<TSlug>,\n D extends number,\n> = [TPopulate] extends [Record<string, never>]\n ? [D] extends [0]\n ? TSlug extends keyof GlobalDocumentBySlug\n ? GlobalDocumentBySlug[TSlug] | null\n : VexDocumentGlobal<TSlug> | null\n : (VexDocumentGlobal<TSlug> & Record<string, unknown>) | null // depth — widened, see Out of Scope\n : TSlug extends keyof GlobalDocumentBySlug\n ? Prettify<GlobalPopulated<TSlug, TPopulate>> | null\n : never;\n\n/**\n * Fetches a single global by slug and returns it as a flat document.\n * User fields (`siteName`, `activeTheme`, …) are at root level alongside\n * `_id`, `_creationTime`, and `_slug`. Server-side only.\n *\n * Import from `@vexcms/core/server`.\n *\n * @typeParam DataModel - Convex data model.\n * @typeParam TSlug - Global slug.\n * @typeParam TPopulate - Populate shape.\n * @typeParam D - Depth literal.\n * @param args - `{ ctx, slug, populate? }` or `{ ctx, slug, depth, config }`.\n * @returns Flat global document or `null` if not yet saved.\n *\n * @example\n * ```ts\n * import { getGlobal } from \"@vexcms/core/server\";\n *\n * const settings = await getGlobal({ ctx, slug: \"siteSettings\" });\n * settings?.siteName; // string | undefined\n *\n * const populated = await getGlobal({\n * ctx,\n * slug: \"siteSettings\",\n * populate: { activeTheme: true },\n * });\n * populated?.activeTheme; // Doc<\"themes\">[] | undefined (runtime correct, type widened)\n * ```\n */\nexport async function getGlobal<\n DataModel extends GenericDataModel,\n TGlobalSlug extends GlobalSlug = GlobalSlug,\n TPopulate extends GlobalPopulateShape<TGlobalSlug> = Record<string, never>,\n D extends number = 0,\n>(\n args: GetGlobalServerArgs<DataModel, TGlobalSlug, TPopulate, D>,\n): Promise<GetGlobalReturn<TGlobalSlug, TPopulate, D>> {\n const { ctx, slug, populate, depth, config } = args;\n\n const row = await ctx.db\n .query(\"vex_globals\")\n .withIndex(\"by_slug\", (q) => q.eq(\"slug\", slug as any))\n .first();\n\n if (!row) return null as GetGlobalReturn<TGlobalSlug, TPopulate, D>;\n\n let flat = flattenGlobalRow(row as Record<string, unknown>);\n\n if (args.config?.access !== undefined) {\n const { access, action, resource } = resolveAccessCall({\n config: args.config,\n access: args.access,\n defaultAction: CRUD_ACTIONS.read,\n resource: args.slug,\n });\n hasPermission({\n throwOnDenied: true,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n access,\n resource,\n action,\n data: flat,\n });\n }\n\n // Depth: auto-populate all relationship fields to N levels\n if (depth && depth > 0 && config) {\n const globalConfig = config.globals.find((g) => g.slug === slug);\n if (globalConfig) {\n const depthPopulate = buildDepthPopulate<TPopulate>(config, slug, depth);\n if (depthPopulate && Object.keys(depthPopulate).length > 0) {\n const [populated] = await populateDocs(ctx, [flat], depthPopulate);\n flat = populated as Record<string, unknown>;\n }\n }\n }\n\n // Explicit populate\n if (populate && Object.keys(populate).length > 0) {\n const [populated] = await populateDocs(ctx, [flat], populate as Record<string, unknown>);\n flat = populated as Record<string, unknown>;\n }\n\n return flat as unknown as GetGlobalReturn<TGlobalSlug, TPopulate, D>;\n}\n","import type { GenericDataModel } from \"convex/server\";\nimport type { VexDocumentGlobal } from \"../../types/generated\";\nimport { CRUD_ACTIONS, hasPermission } from \"../../access\";\nimport type { GenericGlobalsQueryServerArgs } from \"./types\";\nimport { resolveAccessCall } from \"../utils\";\n\n/**\n * Returns all rows from `vex_globals` as flat documents, ordered by\n * `_creationTime` ascending. Only includes globals that have been saved at\n * least once — globals registered in config but never saved are absent.\n *\n * The return type is `VexDocumentGlobal[]` (unnarrowed) because each element\n * may be a different slug with different user fields. Access specific fields\n * via the index signature: `doc[\"siteName\"]`. For typed single-global access,\n * use `getGlobal` instead.\n *\n * Server-side only. Import from `@vexcms/core/server`.\n *\n * @typeParam DataModel - Convex data model.\n * @param args - `{ ctx }`.\n * @returns Flat global documents, unnarrowed.\n *\n * @example\n * ```ts\n * import { findGlobals } from \"@vexcms/core/server\";\n *\n * const all = await findGlobals({ ctx });\n * all.map((g) => g._slug); // [\"siteSettings\", \"nav\"]\n * ```\n */\nexport async function findGlobals<DataModel extends GenericDataModel>(\n args: GenericGlobalsQueryServerArgs<DataModel>,\n): Promise<VexDocumentGlobal[]> {\n const { ctx } = args;\n let rows = await ctx.db.query(\"vex_globals\").collect();\n if (args.config.access !== undefined) {\n rows = rows.filter((r) => {\n const { access, action, resource } = resolveAccessCall({\n config: args.config,\n access: args.access,\n defaultAction: CRUD_ACTIONS.read,\n resource: r.slug as string,\n });\n return hasPermission({\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n data: r.data as Record<string, unknown>,\n });\n });\n }\n return rows.map((row: Record<string, unknown>) => {\n const { slug, data, _id, _creationTime } = row;\n return { _id, _creationTime, _slug: slug, ...(data ?? {}) };\n }) as VexDocumentGlobal[];\n}\n","import { ConvexError } from \"convex/values\";\nimport type { GenericDataModel } from \"convex/server\";\n\nimport type { GlobalSlug } from \"../../types/generated\";\nimport { getGlobalInputSchema } from \"../../globals/utils\";\nimport { CRUD_ACTIONS, hasPermission } from \"../../access\";\nimport { GenericGlobalsMutationServerArgs } from \"./types\";\nimport { resolveAccessCall } from \"../utils\";\n\n/** System keys stripped from flat input before writing to DB. */\nconst STRIPPED_KEYS = new Set([\"_id\", \"_creationTime\", \"_slug\"]);\n\n/**\n * Server-side args for `updateGlobal`.\n *\n * @typeParam DataModel - Convex data model.\n * @typeParam TSlug - Global slug.\n */\nexport interface UpsertGlobalServerArgs<\n DataModel extends GenericDataModel,\n TGlobalSlug extends GlobalSlug = GlobalSlug,\n> extends GenericGlobalsMutationServerArgs<DataModel, TGlobalSlug> {\n /** The global slug to upsert. Must match a registered global in config. */\n slug: TGlobalSlug;\n /**\n * User field data. May be the full flat document (system keys `_id`,\n * `_creationTime`, `_slug` are stripped server-side) or just the field\n * values. The `GlobalEditView` component sends the flat form values here.\n */\n data: Record<string, unknown>;\n}\n\n/**\n * Upserts a global document in `vex_globals`. Strips system keys from `data`,\n * validates remaining user fields against the global's Zod schema, then writes\n * `{ slug, data: userFields }` to the DB (re-nesting). Patches if a row\n * already exists for the slug; inserts if not.\n *\n * Throws `ConvexError` on Zod validation failure with structured `errors` payload.\n * Server-side only. Import from `@vexcms/core/server`.\n *\n * @typeParam DataModel - Convex data model.\n * @typeParam TSlug - Global slug.\n * @param args - `{ ctx, slug, data, globalConfig }`.\n * @returns The `_id` of the upserted document as a string.\n *\n * @example\n * ```ts\n * import { updateGlobal } from \"@vexcms/core/server\";\n *\n * const id = await updateGlobal({\n * ctx,\n * slug: \"siteSettings\",\n * data: { siteName: \"New Name\" },\n * globalConfig: config.globals.find((g) => g.slug === \"siteSettings\")!,\n * });\n * ```\n */\nexport async function upsertGlobal<\n DataModel extends GenericDataModel,\n TSlug extends GlobalSlug = GlobalSlug,\n>(args: UpsertGlobalServerArgs<DataModel, TSlug>): Promise<string> {\n const { ctx, slug, data } = args;\n\n const globalConfig = args.config.globals.find((g) => g.slug === args.slug);\n if (!globalConfig) {\n throw new ConvexError(`No global registered with slug \"${args.slug}\"`);\n }\n\n if (args.config.access !== undefined) {\n const { access, action, resource } = resolveAccessCall({\n config: args.config,\n access: args.access,\n defaultAction: CRUD_ACTIONS.read,\n resource: args.slug,\n });\n hasPermission({\n throwOnDenied: true,\n access,\n user: args.auth?.user ?? {},\n organization: args.auth?.organization,\n resource,\n action,\n });\n }\n\n // Strip any system keys that arrived in the flat payload\n const userFields: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(data)) {\n if (!STRIPPED_KEYS.has(k)) userFields[k] = v;\n }\n\n // Validate against field config's Zod schema\n const schema = getGlobalInputSchema({ global: globalConfig });\n const result = schema.safeParse(userFields);\n if (!result.success) {\n throw new ConvexError({\n message: \"Global validation failed\",\n errors: result.error.message,\n });\n }\n\n const existingGlobal = await ctx.db\n .query(\"vex_globals\")\n .withIndex(\"by_slug\", (q) => q.eq(\"slug\", slug as never))\n .first();\n\n if (existingGlobal) {\n await ctx.db.patch(existingGlobal._id as never, { data: result.data } as never);\n return existingGlobal._id as string;\n }\n\n const id = await ctx.db.insert(\"vex_globals\", {\n slug,\n data: result.data,\n } as never);\n return id as string;\n}\n","import { z, type ZodType } from \"zod\";\nimport { adminFieldToInputSchema } from \"../fields\";\nimport type { GlobalConfig } from \"./types\";\nimport type { VexDocumentGlobal } from \"../types\";\n\n/**\n * Builds `defaultValues` for a TanStack Form from a global config.\n *\n * Mirrors `getCollectionDefaultValues` — operates on `GlobalConfig` rather\n * than `CollectionConfig`. When `document` is provided (edit mode), uses its\n * field values; otherwise falls back to each field's `defaultValue`.\n *\n * @param props - Input props.\n * @param props.global - The global config whose fields define the form shape.\n * @param props.document - Optional existing flat global document (edit mode).\n * `null` means \"no document persisted yet\" — create mode with defaults.\n * @returns `Record<fieldKey, value>` ready for `useForm({ defaultValues })`.\n */\nexport function getGlobalDefaultValues(props: {\n global: GlobalConfig;\n document?: VexDocumentGlobal | null;\n}): Record<string, unknown> {\n const res: Record<string, unknown> = {};\n for (const [fieldKey, fieldDef] of Object.entries(props.global.fields)) {\n if (fieldDef.admin.hidden) continue;\n if (props.document && Boolean(props.document[fieldKey])) {\n res[fieldKey] = props.document[fieldKey];\n } else {\n res[fieldKey] = fieldDef.defaultValue;\n }\n }\n return res;\n}\n\n/**\n * Builds a Zod object schema for validating all fields in a global.\n *\n * Mirrors `getCollectionInputSchema` — same field-level logic, operates on\n * `GlobalConfig`. Used by `updateGlobal` server function to validate user\n * data before writing to `vex_globals`.\n *\n * @param props - Input props.\n * @param props.global - The global config whose fields define the schema.\n * @returns A `z.object` schema with one key per field.\n */\nexport function getGlobalInputSchema(props: { global: GlobalConfig }) {\n const res: Record<string, ZodType> = {};\n for (const [fieldKey, fieldDef] of Object.entries(props.global.fields)) {\n if (fieldDef.admin.hidden) continue;\n res[fieldKey] = adminFieldToInputSchema({ field: fieldDef });\n }\n return z.object({ ...res });\n}\n","import { z, ZodType } from \"zod\";\nimport { TextField } from \"./types\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\n\n/**\n * Builds a Zod schema for validating a text field value in the admin form.\n *\n * Applies `min`/`max` character-length constraints when configured, then\n * wraps in `.optional()` for non-required fields via `applyBaseInputSchemaMeta`.\n *\n * @param props - Input props.\n * @param props.field - The resolved text field definition\n * @returns A Zod string schema with length constraints, a baked-in `.default(field.defaultValue)`, and optionality applied\n *\n * @example\n * ```ts\n * const field = text({ required: true, min: { value: 3 }, max: { value: 100 } })\n * textFieldToInputSchema({ field })\n * // → z.string().min(3).max(100).default(\"\")\n * // (a configured `min` supersedes the bare `.min(1)` required check)\n * ```\n */\nexport function textFieldToInputSchema(props: { field: TextField }): ZodType {\n const { field } = props;\n\n const fieldMinError = field.min?.error ?? \"This field is too short.\";\n const fieldMaxError = field.max?.error ?? \"This field is too long.\";\n\n let inputSchema = z.string().default(field.defaultValue);\n if (field.required) {\n inputSchema = z\n .string()\n .min(1, \"This field is required.\")\n .default(field.defaultValue);\n }\n if (field.min) {\n if (field.max) {\n inputSchema = z\n .string()\n .min(field.min.value, fieldMinError)\n .max(field.max.value, fieldMaxError)\n .default(field.defaultValue);\n } else {\n inputSchema = z\n .string()\n .min(field.min.value, fieldMinError)\n .default(field.defaultValue);\n }\n } else if (field.max) {\n inputSchema = z\n .string()\n .max(field.max.value, fieldMaxError)\n .default(field.defaultValue);\n }\n\n return applyBaseInputSchemaMeta({ field, inputSchema });\n}\n","import type { ZodType } from \"zod\";\nimport type { AdminField } from \"../types\";\n\n/**\n * Attaches field metadata and wraps in `.optional()` for non-required fields.\n *\n * Used during form schema generation to apply the correct Zod validator based on\n * whether the field is marked as required in its config.\n *\n * @param props - Input props.\n * @param props.field - The resolved field definition, used to check `required` and `defaultValue`\n * @param props.inputSchema - The base Zod schema for the field (e.g. `z.string()`)\n * @returns The schema with `.meta(fieldMeta)` for required fields, or `.meta(fieldMeta).optional()` for non-required fields. No `.default()` is applied — each field's own schema is responsible for baking in a default value.\n *\n * @example\n * ```ts\n * applyBaseInputSchemaMeta({ field: textField, inputSchema: z.string() })\n * // required field → z.string().meta({ label, description })\n * // optional field → z.string().meta({ label, description }).optional()\n * ```\n */\nexport function applyBaseInputSchemaMeta(props: {\n field: AdminField;\n inputSchema: ZodType;\n}): ZodType {\n const { field, inputSchema } = props;\n const fieldMeta = {\n fieldType: field.type,\n label: field.label,\n description: field.description ?? \"\",\n };\n if (!field.required) {\n return inputSchema.meta(fieldMeta).optional();\n }\n return inputSchema.meta(fieldMeta);\n}\n","import { z, ZodType } from \"zod\";\nimport { NumberField } from \"./types\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\n\n/**\n * Builds a Zod schema for validating a number field value in the admin form.\n *\n * Applies `min` and `max` constraints when configured, then wraps in\n * `.optional()` for non-required fields via `applyBaseInputSchemaMeta`.\n *\n * @param props - Input props.\n * @param props.field - The resolved number field definition\n * @returns A Zod number schema with range constraints, a baked-in `.default(field.defaultValue)`, and optionality applied\n *\n * @example\n * ```ts\n * const field = number({ required: true, min: { value: 0 }, max: { value: 100 } })\n * numberFieldToInputSchema({ field })\n * // → z.number().min(0).max(100).default(0)\n * ```\n */\nexport function numberFieldToInputSchema(props: {\n field: NumberField;\n}): ZodType {\n const { field } = props;\n\n const fieldMinError = field.min?.error ?? \"This field is too small.\";\n const fieldMaxError = field.max?.error ?? \"This field is too large.\";\n\n let inputSchema = z.number().default(field.defaultValue);\n if (field.min) {\n let defaultValue =\n field.defaultValue < field.min.value\n ? field.min.value\n : field.defaultValue;\n if (field.max) {\n if (field.defaultValue > field.max.value) {\n defaultValue = field.min.value;\n }\n inputSchema = z\n .number()\n .min(field.min.value, fieldMinError)\n .max(field.max.value, fieldMaxError)\n .default(defaultValue);\n } else {\n inputSchema = z\n .number()\n .min(field.min.value, fieldMinError)\n .default(defaultValue);\n }\n } else if (field.max) {\n let defaultValue =\n field.defaultValue > field.max.value\n ? field.max.value\n : field.defaultValue;\n inputSchema = z\n .number()\n .max(field.max.value, fieldMaxError)\n .default(defaultValue);\n }\n\n return applyBaseInputSchemaMeta({ field, inputSchema });\n}\n","import { z, ZodType } from \"zod\";\nimport { CheckboxField } from \"./types\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\n\n/**\n * Builds a Zod schema for validating a checkbox field value in the admin form.\n *\n * Checkbox fields are always boolean — there are no required/optional semantics\n * beyond wrapping in `.optional()` for non-required fields. No min/max constraints.\n *\n * @param props - Input props.\n * @param props.field - The resolved checkbox field definition\n * @returns A Zod boolean schema with optionality and default applied\n *\n * @example\n * ```ts\n * const field = checkbox({ required: true })\n * checkboxFieldToInputSchema({ field })\n * // → z.boolean()\n *\n * const optionalField = checkbox()\n * checkboxFieldToInputSchema({ field: optionalField })\n * // → z.boolean().optional().default(false)\n * ```\n */\nexport function checkboxFieldToInputSchema(props: {\n field: CheckboxField;\n}): ZodType {\n const { field } = props;\n\n const inputSchema = z.boolean().default(field.defaultValue);\n\n return applyBaseInputSchemaMeta({ field, inputSchema });\n}\n","import { z, ZodType, ZodNumber, ZodDefault } from \"zod\";\nimport { DateField } from \"./types\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\n\n/**\n * Builds a Zod schema for validating a date field value in the admin form.\n *\n * Date values are Unix timestamps in milliseconds, so the base schema is\n * `z.number()`. For required fields a `.default(Date.now())` is applied so\n * the picker always has an initial value. Non-required fields are wrapped\n * in `.optional()` via `applyBaseInputSchemaMeta`.\n *\n * @param props - Input props.\n * @param props.field - The resolved date field definition\n * @returns A Zod number schema with optionality applied\n *\n * @example\n * ```ts\n * const field = date({ required: true })\n * dateFieldToInputSchema({ field })\n * // → z.number().default(<now>)\n *\n * const optField = date({ required: false })\n * dateFieldToInputSchema({ field: optField })\n * // → z.number().optional()\n * ```\n */\nexport function dateFieldToInputSchema(props: { field: DateField }): ZodType {\n const { field } = props;\n\n let inputSchema: ZodNumber | ZodDefault<ZodNumber> = z.number();\n if (field.required) {\n inputSchema = z.number().default(new Date().getTime());\n }\n\n return applyBaseInputSchemaMeta({ field, inputSchema });\n}\n","import { z, ZodType } from \"zod\";\nimport { SelectField } from \"./types\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\n\n/**\n * Builds a Zod schema for validating a select field value in the admin form.\n *\n * Validates that submitted values are arrays containing only defined option values.\n * When `hasMany` is false, limits the array to a maximum of one item.\n * Wraps in `.optional()` for non-required fields via `applyBaseInputSchemaMeta`.\n *\n * @param props - Input props.\n * @param props.field - The resolved select field definition\n * @returns A Zod array schema constrained to valid option values, with a baked-in `.default(field.defaultValue)` and optionality applied\n *\n * @example\n * ```ts\n * const field = select({ required: true, options: [{ label: \"Draft\", value: \"draft\" }, { label: \"Published\", value: \"published\" }] })\n * selectFieldToInputSchema({ field })\n * // → z.array(z.enum([\"draft\", \"published\"])).default([])\n *\n * const singleField = select({ hasMany: false, options: [{ label: \"Draft\", value: \"draft\" }] })\n * selectFieldToInputSchema({ field: singleField })\n * // → z.array(z.enum([\"draft\"])).max(1, \"Only one value may be selected.\").default([])\n * ```\n */\nexport function selectFieldToInputSchema(props: {\n field: SelectField;\n}): ZodType {\n const { field } = props;\n\n const optionValues = field.options.map((o) => o.value);\n // z.enum requires at least one element; fall back to z.string() when no options are configured yet\n const itemSchema =\n optionValues.length > 0\n ? z.enum(optionValues as [string, ...string[]])\n : z.string();\n\n let inputSchema = z.array(itemSchema).default(field.defaultValue);\n\n if (!field.hasMany) {\n inputSchema = z\n .array(itemSchema)\n .max(1, \"Only one value may be selected.\")\n .default(field.defaultValue);\n }\n\n return applyBaseInputSchemaMeta({ field, inputSchema });\n}\n","import { z, type ZodDefault, type ZodLiteral, type ZodUnion, type ZodURL, type ZodType } from \"zod\";\nimport { UrlField } from \"./types\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\n\n/**\n * Builds a Zod schema for validating a URL field value in the admin form.\n *\n * Enforces URL format via `z.url()`. Required fields add a `.min(1)` check.\n * `.default(field.defaultValue)` is applied only when `defaultValue` is explicitly\n * set on the field — unlike `text()`, the url field has no implicit empty-string\n * default. Wraps in `.optional()` for non-required fields via\n * `applyBaseInputSchemaMeta`.\n *\n * @param props - Input props.\n * @param props.field - The resolved URL field definition.\n * @returns A Zod URL schema. Optional fields are wrapped in `.optional()`. A\n * `.default()` is only added when `field.defaultValue` is not `undefined`.\n *\n * @example\n * ```ts\n * // Required — rejects empty string and non-URLs\n * urlFieldToInputSchema({ field: url({ required: true }) })\n * // → z.url().min(1, \"This field is required.\").optional() — no default\n *\n * // Optional with explicit default\n * urlFieldToInputSchema({ field: url({ required: false, defaultValue: \"https://example.com\" }) })\n * // → z.url().default(\"https://example.com\").optional()\n * ```\n */\nexport function urlFieldToInputSchema(props: { field: UrlField }): ZodType {\n const { field } = props;\n\n let inputSchema:\n | ZodURL\n | ZodDefault<ZodURL>\n | ZodDefault<ZodUnion<readonly [ZodURL, ZodLiteral<\"\">]>> = z.url();\n if (field.required) {\n inputSchema = z.url().min(1, \"This field is required.\");\n if (field.defaultValue) {\n inputSchema = z.url().min(1, \"This field is required.\").default(field.defaultValue);\n }\n } else if (field.defaultValue !== undefined) {\n inputSchema = z.union([z.url(), z.literal(\"\")]).default(field.defaultValue);\n }\n\n return applyBaseInputSchemaMeta({ field, inputSchema });\n}\n","/**\n * Supported storage notations for a `color()` field, keyed by format name.\n *\n * Each entry carries the anchored pattern that recognises values written in\n * that notation. The patterns accept exactly what `serializeColor` in\n * `@vexcms/react` emits, plus the equivalent hand-typed spellings.\n *\n * @internal\n */\nexport const COLOR_FORMATS = {\n hex: {\n format: \"hex\",\n /** `#E8622A`, `#E8622A80`. Shorthand is rejected — the picker never emits it. */\n pattern: /^#[0-9a-fA-F]{6}(?:[0-9a-fA-F]{2})?$/,\n },\n rgb: {\n format: \"rgb\",\n /** `rgb(232, 98, 42)`, `rgba(232, 98, 42, 0.5)`. */\n pattern:\n /^rgba?\\(\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*,\\s*\\d{1,3}\\s*(?:,\\s*(?:0|1|0?\\.\\d+)\\s*)?\\)$/,\n },\n hsl: {\n format: \"hsl\",\n /** `hsl(17.7, 81%, 54%)`, `hsla(17.7, 81%, 54%, 0.5)`. */\n pattern:\n /^hsla?\\(\\s*-?\\d+(?:\\.\\d+)?\\s*,\\s*\\d+(?:\\.\\d+)?%\\s*,\\s*\\d+(?:\\.\\d+)?%\\s*(?:,\\s*(?:0|1|0?\\.\\d+)\\s*)?\\)$/,\n },\n oklch: {\n format: \"oklch\",\n /** `oklch(65.7% 0.179 40.9)`, `oklch(65.7% 0.179 40.9 / 0.5)`. */\n pattern:\n /^oklch\\(\\s*\\d+(?:\\.\\d+)?%\\s+\\d+(?:\\.\\d+)?\\s+-?\\d+(?:\\.\\d+)?\\s*(?:\\/\\s*(?:0|1|0?\\.\\d+)\\s*)?\\)$/,\n },\n} as const;\n\n/** Union of the notations a `color()` field can store: `\"hex\" | \"rgb\" | \"hsl\" | \"oklch\"`. */\nexport type ColorFormat = (typeof COLOR_FORMATS)[keyof typeof COLOR_FORMATS][\"format\"];\n\n/** Every supported notation's pattern, in declaration order. */\nexport const COLOR_FORMAT_PATTERNS = Object.values(COLOR_FORMATS).map((f) => f.pattern);\n\n/** A value that is entirely one CSS custom-property reference: `var(--primary)`. */\nexport const CSS_VAR_REFERENCE = /^var\\(--[A-Za-z0-9_-]+\\)$/;\n","import { z, type ZodType } from \"zod\";\nimport { ColorField } from \"./types\";\nimport {\n COLOR_FORMAT_PATTERNS,\n CSS_VAR_REFERENCE,\n type ColorFormat,\n} from \"./formats\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\n\n/** One worked example per notation, used to make the form error actionable. */\nconst FORMAT_EXAMPLES: Record<ColorFormat, string> = {\n hex: \"#E8622A\",\n rgb: \"rgb(232, 98, 42)\",\n hsl: \"hsl(17.7, 81%, 54%)\",\n oklch: \"oklch(65.7% 0.179 40.9)\",\n};\n\n/**\n * Builds the accepted-value pattern for a colour field.\n *\n * Deliberately accepts **every** supported notation rather than only\n * `field.format`: `format` decides what the picker writes, and narrowing\n * validation to it would invalidate every existing document the moment a field\n * switched notation. Each notation's pattern is already anchored, so the\n * alternation stays anchored.\n *\n * @param props - Input props.\n * @param props.themeColors - Whether `var(--token)` references are also accepted.\n * @returns A regex matching any supported colour notation.\n */\nfunction colorPattern(props: { themeColors: boolean }): RegExp {\n const sources = COLOR_FORMAT_PATTERNS.map((pattern) => pattern.source);\n if (props.themeColors) sources.push(CSS_VAR_REFERENCE.source);\n return new RegExp(sources.join(\"|\"));\n}\n\n/**\n * Builds the form error shown when a value matches no accepted notation.\n *\n * @param props - Input props.\n * @param props.field - The resolved colour field definition.\n * @returns A message leading with the field's own notation, since that is what\n * the picker produces and therefore what the author most likely wants.\n */\nfunction colorMessage(props: { field: ColorField }): string {\n const example = FORMAT_EXAMPLES[props.field.format];\n const base = `Enter a colour, e.g. ${example}.`;\n if (!props.field.themeColors) return base;\n return `${base} A theme token such as var(--primary) is also accepted.`;\n}\n\n/**\n * Builds a Zod schema for validating a colour field value in the admin form.\n *\n * Accepts hex, `rgb()`, `hsl()` and `oklch()` notation — plus `var(--token)`\n * when the field enables `themeColors`. Required fields add a `.min(1)` check so\n * an empty field reports \"required\" rather than a notation complaint. Optional\n * fields accept the empty string, since `color()` defaults `defaultValue` to\n * `\"\"` and a cleared picker must round-trip.\n *\n * @param props - Input props.\n * @param props.field - The resolved colour field definition.\n * @returns A Zod string schema. Optional fields are wrapped in `.optional()` by\n * `applyBaseInputSchemaMeta` and carry `.default(field.defaultValue)`.\n *\n * @example\n * ```ts\n * // Required — rejects \"\", \"#fff\" and \"var(--primary)\"\n * colorFieldToInputSchema({ field: color({ required: true }) })\n *\n * // themeColors — additionally accepts \"var(--primary)\"\n * colorFieldToInputSchema({ field: color({ required: true, themeColors: true }) })\n * ```\n */\nexport function colorFieldToInputSchema(props: { field: ColorField }): ZodType {\n const { field } = props;\n const pattern = colorPattern({ themeColors: field.themeColors });\n const message = colorMessage({ field });\n\n let inputSchema: ZodType = z.string().regex(pattern, message);\n if (field.required) {\n inputSchema = z.string().min(1, \"This field is required.\").regex(pattern, message);\n if (field.defaultValue) {\n inputSchema = inputSchema.default(field.defaultValue);\n }\n } else if (field.defaultValue !== undefined) {\n inputSchema = z\n .union([z.string().regex(pattern, message), z.literal(\"\")])\n .default(field.defaultValue);\n }\n\n return applyBaseInputSchemaMeta({ field, inputSchema });\n}\n","import { z, type ZodType } from \"zod\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\nimport type { RelationshipField } from \"./types\";\nimport { ADMIN_FIELDS } from \"../constants\";\n\n/**\n * Builds a Zod schema for validating a relationship field value in the admin form.\n *\n * Convex document IDs are strings at the form boundary. Single references\n * validate as `z.string()`. Multi-references (`hasMany: true`) validate as\n * `z.array(z.string())` with a default of `[]`. Wraps in `.optional()` for\n * non-required fields via `applyBaseInputSchemaMeta`.\n *\n * @param props - Input props.\n * @param props.field - The resolved relationship field definition.\n * @returns A Zod schema for the relationship value.\n *\n * @example\n * ```ts\n * // Single, optional (default)\n * relationshipFieldToInputSchema({ field: relationship({ collection: { slug: \"authors\" } }) })\n * // → z.array(z.string()).optional().default([])\n *\n * // Multi, required\n * relationshipFieldToInputSchema({ field: relationship({ collection: { slug: \"tags\" }, hasMany: true, required: true }) })\n * // → z.array(z.string()).default([])\n * ```\n *\n * @internal\n */\nexport function relationshipFieldToInputSchema(props: {\n field: RelationshipField;\n}): ZodType {\n const { field } = props;\n const inputSchema = z\n .array(z.string())\n .default(ADMIN_FIELDS.relationship.defaultValue);\n return applyBaseInputSchemaMeta({ field, inputSchema });\n}\n","import { z, ZodType } from \"zod\";\nimport { ArrayField, ArrayType } from \"./types\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\nimport { adminFieldToInputSchema } from \"../inputSchemas\";\n\n/**\n * Builds a Zod schema for validating an array field value in the admin form.\n *\n * Wraps the nested item schema in `z.array(…)`, applies `min`/`max` item count\n * constraints when configured, then wraps in `.optional()` for non-required\n * fields via `applyBaseInputSchemaMeta`. The item schema is built\n * recursively by delegating to `adminFieldToInputSchema` for the nested field.\n *\n * @param props - Input props.\n * @param props.field - The resolved array field definition\n * @returns A Zod array schema with item count constraints, a baked-in `.default(field.defaultValue)`, and optionality applied\n *\n * @example\n * ```ts\n * const field = array({ items: text(), required: true })\n * arrayFieldToInputSchema({ field })\n * // → z.array(z.string()).superRefine(...).default([])\n * ```\n *\n * @example\n * ```ts\n * const field = array({ items: number(), min: { value: 1 }, max: { value: 5 } })\n * arrayFieldToInputSchema({ field })\n * // → z.array(z.number()).min(1).max(5).default([])\n * ```\n */\nexport function arrayFieldToInputSchema<\n TArrayType extends ArrayType = string,\n TFieldMeta extends {} = {},\n>(props: { field: ArrayField<TArrayType, TFieldMeta> }): ZodType {\n const { field } = props;\n\n const fieldMinError = field.min?.error ?? \"This field is too short.\";\n const fieldMaxError = field.max?.error ?? \"This field is too long.\";\n\n const itemsInputSchema = adminFieldToInputSchema({ field: field.items });\n let inputSchema = z.array(itemsInputSchema).default(field.defaultValue ?? []);\n if (field.required) {\n // Use superRefine which has more control over issues\n inputSchema = z\n .array(itemsInputSchema)\n .superRefine((val, ctx) => {\n if (val === undefined || val === null) {\n ctx.addIssue({\n code: \"custom\",\n message: \"This field is required.\",\n });\n }\n })\n .default(field.defaultValue ?? []);\n }\n if (field.min) {\n if (field.max) {\n inputSchema = z\n .array(itemsInputSchema)\n .min(field.min.value, fieldMinError)\n .max(field.max.value, fieldMaxError)\n .default(field.defaultValue ?? []);\n } else {\n inputSchema = z\n .array(itemsInputSchema)\n .min(field.min.value, fieldMinError)\n .default(field.defaultValue ?? []);\n }\n } else if (field.max) {\n inputSchema = z\n .array(itemsInputSchema)\n .max(field.max.value, fieldMaxError)\n .default(field.defaultValue ?? []);\n }\n\n let finalSchema = inputSchema;\n\n // Apply default value for non-required fields\n if (!field.required && field.defaultValue !== undefined) {\n // @ts-expect-error matching default types here\n finalSchema = inputSchema.default(field.defaultValue);\n }\n\n // @ts-expect-error matching inputSchema types\n finalSchema = applyBaseInputSchemaMeta({\n field,\n inputSchema: finalSchema,\n });\n\n return finalSchema;\n}\n","import { z, ZodType } from \"zod\";\nimport { adminFieldToInputSchema } from \"../inputSchemas\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\nimport type { GroupField } from \"./types\";\n\n/**\n * Builds a Zod schema for validating a group field value in the admin form.\n *\n * Constructs a `z.object({...})` where each key maps to the sub-field's own\n * Zod schema via `adminFieldToInputSchema`. Sub-field defaults and optionality\n * are handled recursively. The outer object receives `.optional()` when\n * `field.required` is `false` via `applyBaseInputSchemaMeta`.\n *\n * @param props - Input props.\n * @param props.field - The resolved group field definition.\n * @returns A Zod object schema with a `.default({})` and optionality applied.\n *\n * @example\n * ```ts\n * const field = group({ fields: { title: text({ required: true }), body: text() } })\n * groupFieldToInputSchema({ field })\n * // → z.object({ title: z.string(), body: z.string().optional() }).optional().default({})\n * ```\n *\n * @internal — Used by admin form schema construction via `adminFieldToInputSchema`.\n */\nexport function groupFieldToInputSchema<TFieldMeta extends {} = {}>(props: {\n field: GroupField<TFieldMeta>;\n}): ZodType {\n const { field } = props;\n\n const subSchemas = Object.fromEntries(\n Object.entries(field.fields).map(([key, subField]) => [\n key,\n adminFieldToInputSchema({ field: subField }),\n ]),\n );\n\n const schema = z.object(subSchemas).default(field.defaultValue ?? {});\n\n return applyBaseInputSchemaMeta({ field, inputSchema: schema });\n}\n","import { z, ZodType } from \"zod\";\nimport { adminFieldToInputSchema } from \"../inputSchemas\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\nimport type { BlocksField } from \"./types\";\n\n/**\n * Builds a Zod schema for validating a blocks field value in the admin form.\n *\n * Each block type becomes a `z.object()` with `blockType: z.literal(slug)`,\n * `blockName: z.string().optional()`, and `id: z.string()` as framework\n * keys, plus the block's own sub-field schemas from `adminFieldToInputSchema`.\n * Multiple block types use `z.discriminatedUnion(\"blockType\", [...])`. A single\n * block type uses a plain `z.array(z.object(...))`. `min`/`max` are enforced on\n * the outer array when set.\n *\n * @param props - Input props.\n * @param props.field - The resolved blocks field definition.\n * @returns A Zod array schema with discriminated-union items.\n *\n * @internal — Used by admin form schema construction via `adminFieldToInputSchema`.\n */\nexport function blocksFieldToInputSchema<TFieldMeta extends {} = {}>(props: {\n field: BlocksField<TFieldMeta>;\n}): ZodType {\n const { field } = props;\n\n const blockSchemas = field.blocks.map((block) => {\n const userSubSchemas = Object.fromEntries(\n Object.entries(block.fields).map(([key, subField]) => [\n key,\n adminFieldToInputSchema({ field: subField }),\n ]),\n );\n return z.object({\n blockType: z.literal(block.blockType),\n blockName: z.string().optional(),\n id: z.string(),\n ...userSubSchemas,\n });\n });\n\n const itemSchema =\n blockSchemas.length <= 1\n ? (blockSchemas[0] ?? z.object({ blockType: z.string(), id: z.string() }))\n : // @ts-expect-error mismatched zod types, works in practice\n z.discriminatedUnion(\"blockType\", blockSchemas);\n\n let schema = z.array(itemSchema);\n\n if (field.min) {\n schema = schema.min(field.min, `At least ${field.min} ${field.labels.plural} required.`);\n }\n if (field.max) {\n schema = schema.max(field.max, `No more than ${field.max} ${field.labels.plural} allowed.`);\n }\n\n // @ts-expect-error mismatched zod types, works in practice\n schema = schema.default(field.defaultValue ?? []);\n\n return applyBaseInputSchemaMeta({ field, inputSchema: schema });\n}\n","import { z, type ZodType } from \"zod\";\nimport { applyBaseInputSchemaMeta } from \"../inputSchemas/utils\";\nimport type { UploadField } from \"./types\";\n\n/**\n * Generates the Zod input schema for an upload field.\n *\n * The form stores a media document ID string. The upload component validates\n * that the ID points to an existing media document at the UI level (by querying\n * the media collection), but the Zod schema is a simple string check.\n *\n * @param props — Input schema generation options.\n * @param props.field — The resolved upload field definition.\n * @returns Zod schema for the form field value.\n */\nexport function uploadFieldToInputSchema(props: { field: UploadField }): ZodType {\n const inputSchema = z.array(z.string());\n return applyBaseInputSchemaMeta({ field: props.field, inputSchema });\n}\n","import { ADMIN_FIELDS, type AdminFieldType } from \"../constants\";\nimport { AdminField } from \"../types\";\nimport { textFieldToInputSchema } from \"../text\";\nimport { numberFieldToInputSchema } from \"../number\";\nimport { checkboxFieldToInputSchema } from \"../checkbox\";\nimport { dateFieldToInputSchema } from \"../date\";\nimport { selectFieldToInputSchema } from \"../select\";\nimport { urlFieldToInputSchema } from \"../url\";\nimport { colorFieldToInputSchema } from \"../color\";\nimport { relationshipFieldToInputSchema } from \"../relationship\";\nimport { arrayFieldToInputSchema } from \"../array\";\nimport { groupFieldToInputSchema } from \"../group\";\nimport { blocksFieldToInputSchema } from \"../blocks\";\nimport { uploadFieldToInputSchema } from \"../upload\";\n\n/**\n * Converts any field definition to its form input schema using zod.\n *\n * Dispatches to the field-type-specific input schema function based on `field.type`.\n * Used by the admin panel while form rendering to build the form input for individual fields, collections, blocks, and globals.\n *\n * @param props Input props\n * @param props.field - The resolved field definition to convert\n * @returns A ZodType (e.g. `z.string()`, `z.boolean().optional().default(false)`)\n * @throws An Error if an unrecognized field type is given. Reaching this is a\n * compile error: the default arm binds the exhausted union to `never`, so a new\n * member of `AdminField` fails typecheck here until a case is added\n *\n * @see {@link textFieldToInputSchema} for the text field implementation\n * @see {@link numberFieldToInputSchema} for the number field implementation\n * @see {@link checkboxFieldToInputSchema} for the checkbox field implementation\n * @internal\n */\nexport function adminFieldToInputSchema(props: { field: AdminField }) {\n // Captured before the switch narrows `props.field`: inside the default arm the\n // union is exhausted to `never`, and `never.type` is not a usable string.\n const fieldType: AdminFieldType = props.field.type;\n\n switch (props.field.type) {\n case ADMIN_FIELDS.text.type:\n return textFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.number.type:\n return numberFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.checkbox.type:\n return checkboxFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.date.type:\n return dateFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.select.type:\n return selectFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.url.type:\n return urlFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.color.type:\n return colorFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.relationship.type:\n return relationshipFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.array.type:\n return arrayFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.group.type:\n return groupFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.blocks.type:\n return blocksFieldToInputSchema({ field: props.field });\n case ADMIN_FIELDS.upload.type:\n return uploadFieldToInputSchema({ field: props.field });\n default: {\n const unhandled: never = props.field;\n throw new Error(\n `unrecognized field type: ${fieldType} — ${JSON.stringify(unhandled)}`,\n );\n }\n }\n}\n"],"mappings":"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","names":["left","right","isConstrainedCheck","buildQuery","q","ConvexError","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","ConvexError","v"]}
|