cvx-kit 0.1.19 → 0.1.21
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/README.md +50 -2
- package/dist/errors.d.mts +3 -2
- package/dist/errors.mjs +1 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/filters.d.mts +4 -0
- package/dist/filters.mjs +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/modules/contracts/errors.d.mts +218 -0
- package/dist/modules/contracts/errors.mjs +1 -1
- package/dist/modules/contracts/errors.mjs.map +1 -1
- package/dist/modules/effect/crud.d.mts +28 -0
- package/dist/querying/cursors.d.mts +30 -4
- package/dist/querying/cursors.mjs +1 -1
- package/dist/querying/cursors.mjs.map +1 -1
- package/dist/querying/execution.d.mts +14 -3
- package/dist/querying/execution.mjs +1 -1
- package/dist/querying/execution.mjs.map +1 -1
- package/dist/querying/failures.d.mts +13 -0
- package/dist/querying/failures.mjs +2 -0
- package/dist/querying/failures.mjs.map +1 -0
- package/dist/querying/filters.d.mts +43 -44
- package/dist/querying/filters.mjs +1 -1
- package/dist/querying/filters.mjs.map +1 -1
- package/dist/querying/planner.d.mts +13 -16
- package/dist/querying/planner.mjs +1 -1
- package/dist/querying/planner.mjs.map +1 -1
- package/dist/querying/relationships.d.mts +24 -7
- package/dist/querying/relationships.mjs +1 -1
- package/dist/querying/relationships.mjs.map +1 -1
- package/dist/querying/schema-snapshot.d.mts +29 -0
- package/dist/querying/schema-snapshot.mjs +2 -0
- package/dist/querying/schema-snapshot.mjs.map +1 -0
- package/dist/querying/search.d.mts +69 -0
- package/dist/querying/search.mjs +2 -0
- package/dist/querying/search.mjs.map +1 -0
- package/dist/querying/security.d.mts +19 -2
- package/dist/querying/security.mjs +1 -1
- package/dist/querying/security.mjs.map +1 -1
- package/dist/querying/shared/ast.d.mts +28 -0
- package/dist/querying/shared/ast.mjs +2 -0
- package/dist/querying/shared/ast.mjs.map +1 -0
- package/dist/querying/shared/contract.d.mts +104 -0
- package/dist/querying/shared/contract.mjs +2 -0
- package/dist/querying/shared/contract.mjs.map +1 -0
- package/dist/querying/shared/evaluate.d.mts +7 -0
- package/dist/querying/shared/evaluate.mjs +2 -0
- package/dist/querying/shared/evaluate.mjs.map +1 -0
- package/dist/querying/shared/filters.d.mts +31 -0
- package/dist/querying/shared/filters.mjs +2 -0
- package/dist/querying/shared/filters.mjs.map +1 -0
- package/dist/querying.d.mts +47 -16
- package/dist/querying.mjs +1 -1
- package/dist/querying.mjs.map +1 -1
- package/dist/react/failures.d.mts +23 -0
- package/dist/react/failures.mjs +2 -0
- package/dist/react/failures.mjs.map +1 -0
- package/dist/react/optimistic.d.mts +47 -0
- package/dist/react/optimistic.mjs +2 -0
- package/dist/react/optimistic.mjs.map +1 -0
- package/dist/react/pagination-state.d.mts +40 -0
- package/dist/react/pagination-state.mjs +2 -0
- package/dist/react/pagination-state.mjs.map +1 -0
- package/dist/react/use-command.d.mts +23 -0
- package/dist/react/use-command.mjs +2 -0
- package/dist/react/use-command.mjs.map +1 -0
- package/dist/react/use-paginated-list.d.mts +32 -0
- package/dist/react/use-paginated-list.mjs +2 -0
- package/dist/react/use-paginated-list.mjs.map +1 -0
- package/dist/react.d.mts +6 -0
- package/dist/react.mjs +1 -0
- package/dist/relations-public.d.mts +2 -2
- package/dist/relations.d.mts +65 -22
- package/dist/relations.mjs +1 -1
- package/dist/relations.mjs.map +1 -1
- package/dist/zod-convex.mjs +1 -1
- package/dist/zod-convex.mjs.map +1 -1
- package/dist/zod-table.d.mts +15 -1
- package/dist/zod-table.mjs +1 -1
- package/dist/zod-table.mjs.map +1 -1
- package/package.json +21 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationships.mjs","names":["RelationshipError","code","assertRelationshipDepth","nested","selection","depth","relationshipExecutor","metadata","planner","navigation","z","base","leg","edge","declared","table","name","guarded","guard","row","memberIdSchema","memberMatches","member","id","value","request","sourceValue","parent","lookup","index","field","position","i","key","relationIndex","indexes","bound","plan","usable","length","size","condition","conditions","c","chosen","candidates","b","a","relationPrefix","prefix","get","result","targets","target","iterator","range","q","binding","next","permitted","traverse","junction","one","match","matches","scalarOnly","ast","scalarMatches","child","output","dto","selected","args","path","parents","pageRelation","prepare","guardConstraint","input","constraints","from","to","through","sourceTable","targetIndex","rootBinding","rootId","open","codec","token","CursorError","prepared","opened","lookupPlan","indexedTarget","executeStream","schema","raw","nativeSchema","fields","getIndexFields","source","reference","EmptyStream","stream","db","continuation","rootTable","declarations","declaration","final","start","end","ancestors","found","error","rethrowQueryError"],"sources":["../../src/querying/relationships.ts"],"sourcesContent":["import type { GenericSchema, SchemaDefinition, IndexRangeBuilder } from 'convex/server'\nimport type { GenericId, Value } from 'convex/values'\nimport type { RelationEdge, RelationGuard } from '../relations'\nimport type { createPlanner, QueryInput, StoredConstraint } from './planner'\nimport { scalarMatches } from './planner'\nimport type {\n\tFilterAst,\n\tFilterScalar,\n\tIndexCondition,\n\tNativePagination,\n\tQueryIndex,\n} from './filters'\nimport { executeStream, rethrowQueryError, type ExecutionRequest } from './execution'\nimport type { createCursorCodec, CursorBinding } from './cursors'\nimport { CursorError } from './cursors'\nimport { z } from 'zod'\nimport { stream, EmptyStream, getIndexFields } from 'convex-helpers/server/stream'\n\nexport interface OutputSelection {\n\t[name: string]: true | OutputSelection\n}\ntype Edge = RelationEdge<string, string, 'one' | 'many', any, any>\nexport type RelationDocument = Record<string, Value> & {\n\t_id: GenericId<string>\n\t_creationTime: number\n}\ntype Document = RelationDocument\ntype Planner = ReturnType<typeof createPlanner>\n\nexport class RelationshipError extends Error {\n\tconstructor(readonly code: 'AMBIGUOUS_RELATION' | 'INVALID_RELATION') {\n\t\tsuper(code)\n\t\tthis.name = 'RelationshipError'\n\t}\n}\n\n/** The selection has already passed the registry validator; count leaf edges too. */\nexport function assertRelationshipDepth(selection: OutputSelection, depth: number): void {\n\tfor (const nested of Object.values(selection)) {\n\t\tif (depth >= 4) throw new RelationshipError('INVALID_RELATION')\n\t\tif (nested !== true) assertRelationshipDepth(nested, depth + 1)\n\t}\n}\n\n/** One request owns all traversal and authorized ID memoization. No raw reader escapes. */\nexport function relationshipExecutor(\n\tschema: SchemaDefinition<GenericSchema, boolean>,\n\trequest: ExecutionRequest,\n\tplanner: Planner,\n\tcodec: ReturnType<typeof createCursorCodec>,\n\trootBinding: CursorBinding,\n) {\n\tconst metadata = planner.metadata\n\tconst navigation = z\n\t\t.object({ cursor: z.string(), ancestors: z.array(z.string()).max(3) })\n\t\t.strict()\n\tconst base = (leg: Edge['from']) => ('base' in leg ? leg.base : leg)\n\tfunction edge(table: string, name: string): Edge {\n\t\tplanner.assertUnchanged()\n\t\tconst declared = metadata.declarations[table]?.[name]\n\t\tif (!declared) throw new RelationshipError('INVALID_RELATION')\n\t\treturn declared\n\t}\n\tconst guarded = (row: Document | Record<string, Value>, guard?: RelationGuard) =>\n\t\t!guard || row[guard.discriminator] === guard.tag\n\tconst memberIdSchema = z.string()\n\tfunction memberMatches(value: Value | undefined, member: Edge['sourceMember']): boolean {\n\t\tif (!member) return true\n\t\tconst id = memberIdSchema.safeParse(value)\n\t\treturn id.success && request.db.normalizeId(member.table, id.data) !== null\n\t}\n\tfunction sourceValue(declared: Edge, parent: Document | null): Value | undefined {\n\t\tif (!parent || !guarded(parent, declared.sourceGuard)) return undefined\n\t\tconst value = parent[base(declared.from).field]\n\t\treturn memberMatches(value, declared.sourceMember) &&\n\t\t\tmemberMatches(value, declared.targetMember)\n\t\t\t? value\n\t\t\t: undefined\n\t}\n\tfunction lookup(table: string, field: string, guard?: RelationGuard) {\n\t\tconst index =\n\t\t\tfield === '_id'\n\t\t\t\t? { name: 'by_id', fields: ['_id'] }\n\t\t\t\t: metadata.tables[table].indexes.find((i) => {\n\t\t\t\t\t\tconst position = i.fields.indexOf(field)\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tposition >= 0 &&\n\t\t\t\t\t\t\ti.fields.slice(0, position).every((key) => key === guard?.discriminator)\n\t\t\t\t\t\t)\n\t\t\t\t\t})\n\t\tif (!index) throw new RelationshipError('INVALID_RELATION')\n\t\treturn index\n\t}\n\tfunction relationIndex(\n\t\ttable: string,\n\t\tfield: string,\n\t\tplan: ReturnType<Planner['plan']>,\n\t\tconditions: readonly IndexCondition[],\n\t\tguard?: RelationGuard,\n\t): QueryIndex {\n\t\tconst indexes =\n\t\t\tfield === '_id' ? [{ name: 'by_id', fields: ['_id'] }] : metadata.tables[table].indexes\n\t\tconst bound = (key: string) =>\n\t\t\tkey === field || key === guard?.discriminator || plan.bindings.has(key)\n\t\tconst usable = (index: QueryIndex) =>\n\t\t\tindex.fields.includes(field) &&\n\t\t\tindex.fields.slice(0, index.fields.indexOf(field)).every(bound)\n\t\tconst length = (index: QueryIndex) => {\n\t\t\tlet size = 0\n\t\t\tfor (const key of index.fields) {\n\t\t\t\tif (!bound(key)) break\n\t\t\t\tsize++\n\t\t\t}\n\t\t\treturn size\n\t\t}\n\t\tconst condition = conditions.find((c) => c.value !== null && c.value !== undefined)\n\t\tif (condition) {\n\t\t\tconst chosen = indexes.find((i) => i.name === condition.index && usable(i))\n\t\t\tif (!chosen)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Explicit index \"${condition.index}\" is incompatible with declared relationship lookup`,\n\t\t\t\t)\n\t\t\treturn chosen\n\t\t}\n\t\tconst candidates = indexes\n\t\t\t.filter(usable)\n\t\t\t.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\tlength(b) - length(a) ||\n\t\t\t\t\ta.name.localeCompare(b.name) ||\n\t\t\t\t\ta.fields.join('\\0').localeCompare(b.fields.join('\\0')),\n\t\t\t)\n\t\tif (!candidates.length) throw new RelationshipError('INVALID_RELATION')\n\t\treturn candidates[0]\n\t}\n\tfunction relationPrefix(\n\t\tindex: QueryIndex,\n\t\tfield: string,\n\t\tvalue: FilterScalar,\n\t\tplan?: ReturnType<Planner['plan']>,\n\t\tguard?: RelationGuard,\n\t) {\n\t\tconst prefix: { field: string; value: FilterScalar }[] = []\n\t\tfor (const key of index.fields) {\n\t\t\tif (key === field) prefix.push({ field: key, value })\n\t\t\telse if (key === guard?.discriminator) prefix.push({ field: key, value: guard.tag })\n\t\t\telse if (plan?.bindings.has(key)) prefix.push({ field: key, value: plan.bindings.get(key)! })\n\t\t\telse break\n\t\t}\n\t\treturn prefix\n\t}\n\n\tasync function get(table: string, id: Value | undefined): Promise<Document | null> {\n\t\tplanner.beforeLeaf(table)\n\t\t// SAFETY: native normalization rejects non-string/non-table IDs before any read.\n\t\tif (id == null || !request.db.normalizeId(table, id as string)) return null\n\t\t// SAFETY: normalizeId verifies the declared native table before the authorized metered get.\n\t\tconst result = await request.db.get(table, id as GenericId<string>)\n\t\tplanner.assertUnchanged()\n\t\tif (result) planner.validateDocument(table, result)\n\t\t// SAFETY: normalizeId checked the table; the authorized native get retains system fields.\n\t\treturn result as Document | null\n\t}\n\tasync function* targets(\n\t\ttable: string,\n\t\tfield: string,\n\t\tvalue: Value | undefined,\n\t\tdepth: number,\n\t\tguard?: RelationGuard,\n\t\tmember?: Edge['targetMember'],\n\t): AsyncGenerator<Document> {\n\t\trequest.expand(depth, 0)\n\t\tif (value == null || !memberMatches(value, member)) return\n\t\tif (field === '_id') {\n\t\t\tconst target = await get(table, value)\n\t\t\tif (target && guarded(target, guard) && memberMatches(target[field], member)) {\n\t\t\t\trequest.expand(depth)\n\t\t\t\tyield target\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tplanner.beforeLeaf(table)\n\t\tconst index = lookup(table, field, guard)\n\t\t// SAFETY: registry validates the indexed native lookup prefix and compatible values.\n\t\tconst source = request.leafQuery(table).withIndex(index.name, (q) => {\n\t\t\t// SAFETY: registration checks the mandatory join and every preceding guard equality.\n\t\t\tlet range = q as IndexRangeBuilder<any, string[], number>\n\t\t\t// SAFETY: registered relation fields are native scalars; each equality advances the consecutive checked prefix.\n\t\t\tfor (const binding of relationPrefix(index, field, value as FilterScalar, undefined, guard)) {\n\t\t\t\t// SAFETY: the prefix comes from the declared index and registry-owned guard.\n\t\t\t\trange = range.eq(binding.field, binding.value) as IndexRangeBuilder<any, string[], number>\n\t\t\t}\n\t\t\treturn range\n\t\t})\n\t\tconst iterator = source[Symbol.asyncIterator]()\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tplanner.assertUnchanged()\n\t\t\t\tplanner.beforeLeaf(table)\n\t\t\t\tconst next = await iterator.next()\n\t\t\t\tplanner.assertUnchanged()\n\t\t\t\tif (next.done) return\n\t\t\t\tconst permitted = await request.authorize(table, next.value)\n\t\t\t\t// Denials are an await boundary too; never hide them inside an authorized iterator.\n\t\t\t\tplanner.assertUnchanged()\n\t\t\t\tif (!permitted) continue\n\t\t\t\tplanner.validateDocument(table, next.value)\n\t\t\t\tif (!guarded(next.value, guard) || !memberMatches(next.value[field], member)) continue\n\t\t\t\trequest.expand(depth)\n\t\t\t\t// SAFETY: native rows include system fields and explicit authorization just completed.\n\t\t\t\tyield next.value as Document\n\t\t\t}\n\t\t} finally {\n\t\t\tawait iterator.return?.()\n\t\t}\n\t}\n\tasync function* traverse(\n\t\tdeclared: Edge,\n\t\tparent: Document,\n\t\tdepth: number,\n\t): AsyncGenerator<Document> {\n\t\trequest.expand(depth, 0)\n\t\tconst value = sourceValue(declared, parent)\n\t\tif (value == null) return\n\t\tif ('base' in declared.from && 'base' in declared.to) {\n\t\t\tfor await (const junction of targets(\n\t\t\t\tdeclared.from.junction.table,\n\t\t\t\tdeclared.from.junction.field,\n\t\t\t\tvalue,\n\t\t\t\tdepth,\n\t\t\t\tdeclared.junctionGuard,\n\t\t\t))\n\t\t\t\tyield* targets(\n\t\t\t\t\tdeclared.target,\n\t\t\t\t\tdeclared.to.base.field,\n\t\t\t\t\tjunction[declared.to.junction.field],\n\t\t\t\t\tdepth,\n\t\t\t\t\tdeclared.targetGuard,\n\t\t\t\t)\n\t\t} else\n\t\t\tyield* targets(\n\t\t\t\tdeclared.target,\n\t\t\t\tbase(declared.to).field,\n\t\t\t\tvalue,\n\t\t\t\tdepth,\n\t\t\t\tdeclared.targetGuard,\n\t\t\t\tdeclared.targetMember,\n\t\t\t)\n\t}\n\tasync function one(declared: Edge, parent: Document, depth: number) {\n\t\tlet match: Document | null = null\n\t\tfor await (const target of traverse(declared, parent, depth)) {\n\t\t\tif (match) throw new RelationshipError('AMBIGUOUS_RELATION')\n\t\t\tmatch = target\n\t\t}\n\t\treturn match\n\t}\n\tasync function matches(\n\t\ttable: string,\n\t\tparent: Document,\n\t\tast: FilterAst,\n\t\tdepth = 0,\n\t): Promise<boolean> {\n\t\trequest.expand(depth, 0)\n\t\tplanner.assertUnchanged()\n\t\t// Ordinary tables have no variant document validation. The live schema\n\t\t// guard above still covers them; variant rows retain full stored validation.\n\t\tif (metadata.tables[table].variant) planner.validateDocument(table, parent)\n\t\t// Evaluate a scalar Boolean tree synchronously under that same guard,\n\t\t// rather than rechecking every registry schema at each predicate node.\n\t\tif (scalarOnly(ast)) return scalarMatches(ast, parent)\n\t\tif (ast.kind === 'variant')\n\t\t\treturn parent[ast.discriminator] === ast.tag && matches(table, parent, ast.child, depth)\n\t\tif (ast.kind === 'scalar') return scalarMatches(ast, parent)\n\t\tif (ast.kind === 'not') return !(await matches(table, parent, ast.child, depth))\n\t\tif (ast.kind === 'and' || ast.kind === 'or') {\n\t\t\tfor (const child of ast.children) {\n\t\t\t\tconst result = await matches(table, parent, child, depth)\n\t\t\t\tif (ast.kind === 'and' && !result) return false\n\t\t\t\tif (ast.kind === 'or' && result) return true\n\t\t\t}\n\t\t\treturn ast.kind === 'and'\n\t\t}\n\t\tif (ast.kind !== 'relation') throw new RelationshipError('INVALID_RELATION')\n\t\tconst declared = edge(table, ast.name)\n\t\tif (ast.mode === 'one') {\n\t\t\tconst target = await one(declared, parent, depth + 1)\n\t\t\treturn ast.predicate === null\n\t\t\t\t? target === null\n\t\t\t\t: target !== null && (await matches(declared.target, target, ast.predicate, depth + 1))\n\t\t}\n\t\tfor await (const target of traverse(declared, parent, depth + 1))\n\t\t\tif (await matches(declared.target, target, ast.predicate!, depth + 1))\n\t\t\t\treturn ast.mode === 'some'\n\t\t// Only actual authorized traversal exhaustion proves none.\n\t\treturn ast.mode === 'none'\n\t}\n\tasync function output(\n\t\ttable: string,\n\t\tparent: Document,\n\t\tselection: OutputSelection,\n\t\targs: NativePagination & { order?: 'asc' | 'desc' },\n\t\tpath: readonly string[] = [],\n\t\tdepth = 0,\n\t\tparents: readonly string[] = [],\n\t): Promise<Record<string, Value>> {\n\t\trequest.expand(depth, 0)\n\t\t// SAFETY: the registry strictly validates this public native DTO projection.\n\t\tconst dto = planner.complete(table, parent, true, { kind: 'and', children: [] }) as Record<\n\t\t\tstring,\n\t\t\tValue\n\t\t>\n\t\tfor (const [name, selected] of Object.entries(selection)) {\n\t\t\tconst declared = edge(table, name),\n\t\t\t\tnested = selected === true ? {} : selected\n\t\t\tif (declared.cardinality === 'one') {\n\t\t\t\tconst target = await one(declared, parent, depth + 1)\n\t\t\t\tdto[name] = target\n\t\t\t\t\t? await output(declared.target, target, nested, args, [...path, name], depth + 1, [\n\t\t\t\t\t\t\t...parents,\n\t\t\t\t\t\t\tparent._id,\n\t\t\t\t\t\t])\n\t\t\t\t\t: null\n\t\t\t} else {\n\t\t\t\tconst page = await pageRelation(\n\t\t\t\t\ttable,\n\t\t\t\t\tparent,\n\t\t\t\t\tname,\n\t\t\t\t\tnested,\n\t\t\t\t\t{ numItems: args.numItems, cursor: null, order: args.order },\n\t\t\t\t\t[],\n\t\t\t\t\t[],\n\t\t\t\t\t[...path, name],\n\t\t\t\t\tdepth + 1,\n\t\t\t\t\t[...parents, parent._id],\n\t\t\t\t)\n\t\t\t\t// SAFETY: the declared page validator has parsed this nested DTO page.\n\t\t\t\tdto[name] = page as Value\n\t\t\t}\n\t\t}\n\t\treturn dto\n\t}\n\tfunction prepare(\n\t\ttable: string,\n\t\tname: string,\n\t\tselection: OutputSelection,\n\t\tinput: QueryInput,\n\t\tconditions: readonly IndexCondition[],\n\t\tconstraints: readonly StoredConstraint[],\n\t\tpath: readonly string[],\n\t\trootId: string,\n\t) {\n\t\tplanner.page(edge(table, name).target, selection)\n\t\tassertRelationshipDepth(selection, path.length)\n\t\tconst declared = edge(table, name)\n\t\tif (declared.cardinality !== 'many') throw new RelationshipError('INVALID_RELATION')\n\t\tconst guardConstraint = declared.targetGuard\n\t\t\t? { fields: { [declared.targetGuard.discriminator]: declared.targetGuard.tag } }\n\t\t\t: undefined\n\t\tconst plan = planner.plan(\n\t\t\tdeclared.target,\n\t\t\tinput,\n\t\t\t[],\n\t\t\tguardConstraint ? [...constraints, guardConstraint] : constraints,\n\t\t)\n\t\tconst from = declared.from,\n\t\t\tto = declared.to\n\t\tconst through =\n\t\t\t'base' in from && 'base' in to\n\t\t\t\t? {\n\t\t\t\t\t\ttable: from.junction.table,\n\t\t\t\t\t\tfield: from.junction.field,\n\t\t\t\t\t\ttargetField: to.base.field,\n\t\t\t\t\t\ttargetReference: to.junction.field,\n\t\t\t\t\t}\n\t\t\t\t: null\n\t\tconst sourceTable = through ? through.table : declared.target\n\t\tconst field = through ? through.field : base(to).field\n\t\tconst targetIndex = relationIndex(\n\t\t\tdeclared.target,\n\t\t\tthrough ? through.targetField : field,\n\t\t\tplan,\n\t\t\tconditions,\n\t\t\tdeclared.targetGuard,\n\t\t)\n\t\tconst index = through ? lookup(sourceTable, field, declared.junctionGuard) : targetIndex\n\t\t// SAFETY: planner.page validated the complete output mask before cursor binding.\n\t\tconst binding: CursorBinding = {\n\t\t\t...rootBinding,\n\t\t\ttable: sourceTable,\n\t\t\tindex: index.name,\n\t\t\torder: plan.args.order ?? 'asc',\n\t\t\tclientFilters: plan.clientIdentity,\n\t\t\tserverFilters: {\n\t\t\t\tpredicates: plan.serverIdentity,\n\t\t\t\tselection: selection as Value,\n\t\t\t\tscope: { ...request.scope },\n\t\t\t\trootTable: rootBinding.table,\n\t\t\t\tlookupIndex: targetIndex.name,\n\t\t\t\tguards: {\n\t\t\t\t\tsource: declared.sourceGuard ? { ...declared.sourceGuard } : null,\n\t\t\t\t\ttarget: declared.targetGuard ? { ...declared.targetGuard } : null,\n\t\t\t\t\tjunction: declared.junctionGuard ? { ...declared.junctionGuard } : null,\n\t\t\t\t},\n\t\t\t\t...((declared.sourceMember || declared.targetMember) && {\n\t\t\t\t\tmembers: {\n\t\t\t\t\t\tsource: declared.sourceMember ? { ...declared.sourceMember } : null,\n\t\t\t\t\t\ttarget: declared.targetMember ? { ...declared.targetMember } : null,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t},\n\t\t\trelationPath: path,\n\t\t\tparent: rootId,\n\t\t}\n\t\tfunction open(token: string) {\n\t\t\ttry {\n\t\t\t\treturn navigation.parse(JSON.parse(codec.open(token, binding)))\n\t\t\t} catch {\n\t\t\t\tthrow new CursorError()\n\t\t\t}\n\t\t}\n\t\treturn { declared, plan, through, sourceTable, field, index, targetIndex, binding, open }\n\t}\n\tasync function pageRelation(\n\t\ttable: string,\n\t\tparent: Document | null,\n\t\tname: string,\n\t\tselection: OutputSelection,\n\t\tinput: QueryInput,\n\t\tconditions: readonly IndexCondition[] = [],\n\t\tconstraints: readonly StoredConstraint[] = [],\n\t\tpath: readonly string[] = [name],\n\t\tdepth = 1,\n\t\tparents: readonly string[] = [],\n\t\tprepared = prepare(\n\t\t\ttable,\n\t\t\tname,\n\t\t\tselection,\n\t\t\tinput,\n\t\t\tconditions,\n\t\t\tconstraints,\n\t\t\tpath,\n\t\t\tparents[0] ?? parent!._id,\n\t\t),\n\t\topened?: { cursor: string | null; endCursor?: string | null },\n\t) {\n\t\trequest.expand(depth, 0)\n\t\tconst { declared, plan, through, sourceTable, field, index, targetIndex, binding } = prepared\n\t\tconst value = sourceValue(declared, parent)\n\t\tconst args = { ...plan.args, ...(opened ?? { cursor: null }) }\n\t\t// SAFETY: declarations accept only native scalar lookup values; nullish references are absence.\n\t\tconst lookupPlan = {\n\t\t\t...plan,\n\t\t\tindex,\n\t\t\tprefix:\n\t\t\t\tvalue == null\n\t\t\t\t\t? []\n\t\t\t\t\t: through\n\t\t\t\t\t\t? relationPrefix(index, field, value as FilterScalar, undefined, declared.junctionGuard)\n\t\t\t\t\t\t: relationPrefix(index, field, value as FilterScalar, plan, declared.targetGuard),\n\t\t\tunsatisfiable: value == null || plan.unsatisfiable,\n\t\t}\n\t\tconst indexedTarget = through && through.targetField !== '_id'\n\t\tconst result = await executeStream(\n\t\t\tschema,\n\t\t\trequest,\n\t\t\tsourceTable,\n\t\t\tlookupPlan,\n\t\t\tplanner,\n\t\t\tcodec,\n\t\t\tbinding,\n\t\t\targs,\n\t\t\tasync (row, permitted) => {\n\t\t\t\tif (permitted) planner.validateDocument(indexedTarget ? declared.target : sourceTable, row)\n\t\t\t\tif (!permitted || (through && !indexedTarget && !guarded(row, declared.junctionGuard)))\n\t\t\t\t\treturn null\n\t\t\t\trequest.expand(depth)\n\t\t\t\tlet target: Document | null\n\t\t\t\tif (through && !indexedTarget)\n\t\t\t\t\ttarget = await get(declared.target, row[through.targetReference])\n\t\t\t\telse {\n\t\t\t\t\t// SAFETY: native direct/flattened target streams retain validated system fields.\n\t\t\t\t\ttarget = row as Document\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\t!target ||\n\t\t\t\t\t!guarded(target, declared.targetGuard) ||\n\t\t\t\t\t!memberMatches(target[base(declared.to).field], declared.targetMember) ||\n\t\t\t\t\t!(await matches(declared.target, target, plan.residual, depth))\n\t\t\t\t)\n\t\t\t\t\treturn null\n\t\t\t\treturn output(declared.target, target, selection, plan.args, path, depth, parents)\n\t\t\t},\n\t\t\t(raw) => codec.seal(JSON.stringify({ cursor: raw, ancestors: parents.slice(1) }), binding),\n\t\t\tindexedTarget\n\t\t\t\t? (source, db) => {\n\t\t\t\t\t\t// SAFETY: the registry owns the native schema/index descriptors. Public\n\t\t\t\t\t\t// helper flatMap concatenates native outer/inner keys and paginates them.\n\t\t\t\t\t\tconst nativeSchema = schema as SchemaDefinition<\n\t\t\t\t\t\t\tRecord<\n\t\t\t\t\t\t\t\tstring,\n\t\t\t\t\t\t\t\timport('convex/server').TableDefinition<any, Record<string, string[]>>\n\t\t\t\t\t\t\t>,\n\t\t\t\t\t\t\tboolean\n\t\t\t\t\t\t>\n\t\t\t\t\t\tconst fields = getIndexFields(declared.target, targetIndex.name, nativeSchema)\n\t\t\t\t\t\treturn source.flatMap(async (junction) => {\n\t\t\t\t\t\t\trequest.expand(depth)\n\t\t\t\t\t\t\tconst permitted = await request.authorize(sourceTable, junction)\n\t\t\t\t\t\t\tplanner.assertUnchanged()\n\t\t\t\t\t\t\tif (permitted) planner.validateDocument(sourceTable, junction)\n\t\t\t\t\t\t\tconst reference = junction[through.targetReference]\n\t\t\t\t\t\t\tif (!permitted || !guarded(junction, declared.junctionGuard) || reference == null)\n\t\t\t\t\t\t\t\treturn new EmptyStream(plan.args.order ?? 'asc', fields)\n\t\t\t\t\t\t\treturn stream(db, nativeSchema)\n\t\t\t\t\t\t\t\t.query(declared.target)\n\t\t\t\t\t\t\t\t.withIndex(targetIndex.name, (q) => {\n\t\t\t\t\t\t\t\t\t// SAFETY: registry validates the native leading lookup; validated filters supply each consecutive trailing binding.\n\t\t\t\t\t\t\t\t\tlet range = q as IndexRangeBuilder<any, string[], number>\n\t\t\t\t\t\t\t\t\t// SAFETY: junction references are native scalar Values validated by the declared lookup.\n\t\t\t\t\t\t\t\t\tconst prefix = relationPrefix(\n\t\t\t\t\t\t\t\t\t\ttargetIndex,\n\t\t\t\t\t\t\t\t\t\tthrough.targetField,\n\t\t\t\t\t\t\t\t\t\treference as FilterScalar,\n\t\t\t\t\t\t\t\t\t\tplan,\n\t\t\t\t\t\t\t\t\t\tdeclared.targetGuard,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\tfor (const binding of prefix) {\n\t\t\t\t\t\t\t\t\t\t// SAFETY: native eq advances the validated consecutive index prefix.\n\t\t\t\t\t\t\t\t\t\trange = range.eq(binding.field, binding.value) as IndexRangeBuilder<\n\t\t\t\t\t\t\t\t\t\t\tany,\n\t\t\t\t\t\t\t\t\t\t\tstring[],\n\t\t\t\t\t\t\t\t\t\t\tnumber\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treturn range\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.order(plan.args.order ?? 'asc')\n\t\t\t\t\t\t}, fields)\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t\tindexedTarget ? declared.target : sourceTable,\n\t\t)\n\t\tplanner.assertUnchanged()\n\t\treturn planner.page(declared.target, selection).parse(result)\n\t}\n\tasync function continuation(\n\t\trootTable: string,\n\t\trootId: string,\n\t\tpath: readonly string[],\n\t\tselection: OutputSelection,\n\t\tinput: QueryInput,\n\t\tconditions: readonly IndexCondition[],\n\t\tconstraints: readonly StoredConstraint[],\n\t) {\n\t\tif (!path.length || path.length > 4 || !request.db.normalizeId(rootTable, rootId))\n\t\t\tthrow new RelationshipError('INVALID_RELATION')\n\t\tlet table = rootTable\n\t\tconst declarations = path.map((name) => {\n\t\t\tconst declaration = edge(table, name)\n\t\t\ttable = declaration.target\n\t\t\treturn declaration\n\t\t})\n\t\tconst final = declarations.at(-1)!\n\t\tconst prepared = prepare(\n\t\t\tfinal.source,\n\t\t\tpath.at(-1)!,\n\t\t\tselection,\n\t\t\tinput,\n\t\t\tconditions,\n\t\t\tconstraints,\n\t\t\tpath,\n\t\t\trootId,\n\t\t)\n\t\t// Authenticate both slots and native ancestor IDs before ANY parent/traversal read.\n\t\tconst start = input.cursor === null ? null : prepared.open(input.cursor)\n\t\tconst end = input.endCursor == null ? null : prepared.open(input.endCursor)\n\t\tconst ancestors = start?.ancestors ?? end?.ancestors ?? []\n\t\tif (\n\t\t\tancestors.length !== path.length - 1 ||\n\t\t\t(start && end && JSON.stringify(start.ancestors) !== JSON.stringify(end.ancestors)) ||\n\t\t\tancestors.some((id, i) => !request.db.normalizeId(declarations[i].target, id))\n\t\t)\n\t\t\tthrow new CursorError()\n\t\t// Declarative input/index/cursor validation above retains its pre-read errors,\n\t\t// just as on the root path. Every awaited read, ancestry proof and page is protected.\n\t\ttry {\n\t\t\tlet parent = await get(rootTable, rootId)\n\t\t\tfor (let i = 0; parent && i < ancestors.length; i++) {\n\t\t\t\tconst declared = declarations[i]\n\t\t\t\tlet found: Document | null = null\n\t\t\t\tif (declared.cardinality === 'one') {\n\t\t\t\t\tconst target = await one(declared, parent, i + 1)\n\t\t\t\t\tif (target?._id === ancestors[i]) found = target\n\t\t\t\t} else\n\t\t\t\t\tfor await (const target of traverse(declared, parent, i + 1)) {\n\t\t\t\t\t\tif (target._id === ancestors[i]) {\n\t\t\t\t\t\t\tfound = target\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tparent = found\n\t\t\t}\n\t\t\treturn await pageRelation(\n\t\t\t\tfinal.source,\n\t\t\t\tparent,\n\t\t\t\tpath.at(-1)!,\n\t\t\t\tselection,\n\t\t\t\tinput,\n\t\t\t\tconditions,\n\t\t\t\tconstraints,\n\t\t\t\tpath,\n\t\t\t\tpath.length,\n\t\t\t\t[rootId, ...ancestors],\n\t\t\t\tprepared,\n\t\t\t\t{ cursor: start?.cursor ?? null, endCursor: end?.cursor ?? input.endCursor },\n\t\t\t)\n\t\t} catch (error) {\n\t\t\trethrowQueryError(error)\n\t\t}\n\t}\n\treturn { matches, output, continuation, edge }\n}\n\n/** Scalar subtrees have no reads or await boundaries requiring another live guard. */\nfunction scalarOnly(ast: FilterAst): boolean {\n\tif (ast.kind === 'relation') return false\n\tif (ast.kind === 'and' || ast.kind === 'or') return ast.children.every(scalarOnly)\n\tif (ast.kind === 'not' || ast.kind === 'variant') return scalarOnly(ast.child)\n\treturn true\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"relationships.mjs","names":["navigation","z","RelationshipError","code","assertRelationshipDepth","nested","selection","depth","relationshipExecutor","metadata","planner","base","leg","edge","declared","table","name","guarded","guard","row","memberIdSchema","memberMatches","member","id","value","request","sourceValue","parent","lookup","index","field","position","i","key","checkTraversal","check","groups","targetPath","path","relationIndex","indexes","bound","plan","usable","length","size","condition","conditions","c","chosen","candidates","b","a","relationPrefix","prefix","get","result","targets","target","iterator","range","q","binding","next","traverse","junction","one","match","matches","scalarOnly","ast","scalarMatches","child","output","dto","selected","args","parents","pageRelation","prepare","ordering","guardConstraint","input","constraints","from","to","through","sourceTable","targetIndex","expansion","expansionForIndex","groupBinding","resolved","rootBinding","rootId","open","codec","token","CursorError","prepared","opened","lookupPlan","rangeForIndex","compareValues","withinRange","indexedTarget","executeStream","schema","permitted","raw","nativeSchema","fields","getIndexFields","source","reference","EmptyStream","stream","db","nativeIndexRange","continuation","rootTable","declarations","declaration","d","final","start","end","ancestors","found","error","rethrowQueryError","preflight","nestedPath"],"sources":["../../src/querying/relationships.ts"],"sourcesContent":["import type { GenericSchema, SchemaDefinition, IndexRangeBuilder } from 'convex/server'\nimport { compareValues, type GenericId, type Value } from 'convex/values'\nimport type { RelationEdge, RelationGuard } from '../relations'\nimport type { createPlanner, QueryInput, StoredConstraint } from './planner'\nimport { scalarMatches } from './planner'\nimport type {\n\tFilterAst,\n\tFilterScalar,\n\tIndexCondition,\n\tNativePagination,\n\tQueryIndex,\n} from './filters'\nimport { expansionForIndex, rangeForIndex, withinRange } from './filters'\nimport {\n\texecuteStream,\n\tnativeIndexRange,\n\trethrowQueryError,\n\ttype ExecutionRequest,\n} from './execution'\nimport type { createCursorCodec, CursorBinding } from './cursors'\nimport { CursorError } from './cursors'\nimport { z } from 'zod'\nimport { stream, EmptyStream, getIndexFields } from 'convex-helpers/server/stream'\n\nexport interface OutputSelection {\n\t[name: string]: true | OutputSelection\n}\nexport type ResolvedGroups = {\n\tfieldGroups: Record<string, { mode: 'required' | 'if-permitted'; allowed: boolean }>\n\trelations: Record<string, ResolvedGroups>\n}\nexport interface GroupAccess {\n\tat(path: readonly string[]): ResolvedGroups\n\tallowed(path: readonly string[]): readonly string[]\n\tcheckOrder(table: string, fields: readonly string[], path?: readonly string[]): void\n}\ntype Edge = RelationEdge<string, string, 'one' | 'many', any, any>\nexport type RelationDocument = Record<string, Value> & {\n\t_id: GenericId<string>\n\t_creationTime: number\n}\ntype Document = RelationDocument\ntype Planner = ReturnType<typeof createPlanner>\nconst navigation = z.object({ cursor: z.string(), ancestors: z.array(z.string()).max(3) }).strict()\n\nexport class RelationshipError extends Error {\n\tconstructor(readonly code: 'AMBIGUOUS_RELATION' | 'INVALID_RELATION') {\n\t\tsuper(code)\n\t\tthis.name = 'RelationshipError'\n\t}\n}\n\n/** The selection has already passed the registry validator; count leaf edges too. */\nexport function assertRelationshipDepth(selection: OutputSelection, depth: number): void {\n\tfor (const nested of Object.values(selection)) {\n\t\tif (depth >= 4) throw new RelationshipError('INVALID_RELATION')\n\t\tif (nested !== true) assertRelationshipDepth(nested, depth + 1)\n\t}\n}\n\n/** One request owns all traversal and authorized ID memoization. No raw reader escapes. */\nexport function relationshipExecutor(\n\tschema: SchemaDefinition<GenericSchema, boolean>,\n\trequest: ExecutionRequest,\n\tplanner: Planner,\n\tcodec: ReturnType<typeof createCursorCodec>,\n\trootBinding: CursorBinding,\n\tgroups?: GroupAccess,\n) {\n\tconst metadata = planner.metadata\n\tconst base = (leg: Edge['from']) => ('base' in leg ? leg.base : leg)\n\tfunction edge(table: string, name: string): Edge {\n\t\tconst declared = metadata.declarations[table]?.[name]\n\t\tif (!declared) throw new RelationshipError('INVALID_RELATION')\n\t\treturn declared\n\t}\n\tconst guarded = (row: Document | Record<string, Value>, guard?: RelationGuard) =>\n\t\t!guard || row[guard.discriminator] === guard.tag\n\tconst memberIdSchema = z.string()\n\tfunction memberMatches(value: Value | undefined, member: Edge['sourceMember']): boolean {\n\t\tif (!member) return true\n\t\tconst id = memberIdSchema.safeParse(value)\n\t\treturn id.success && request.db.normalizeId(member.table, id.data) !== null\n\t}\n\tfunction sourceValue(declared: Edge, parent: Document | null): Value | undefined {\n\t\tif (!parent || !guarded(parent, declared.sourceGuard)) return undefined\n\t\tconst value = parent[base(declared.from).field]\n\t\treturn memberMatches(value, declared.sourceMember) &&\n\t\t\tmemberMatches(value, declared.targetMember)\n\t\t\t? value\n\t\t\t: undefined\n\t}\n\tfunction lookup(table: string, field: string, guard?: RelationGuard) {\n\t\tconst index =\n\t\t\tfield === '_id'\n\t\t\t\t? { name: 'by_id', fields: ['_id'] }\n\t\t\t\t: metadata.tables[table].indexes.find((i) => {\n\t\t\t\t\t\tconst position = i.fields.indexOf(field)\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\tposition >= 0 &&\n\t\t\t\t\t\t\ti.fields.slice(0, position).every((key) => key === guard?.discriminator)\n\t\t\t\t\t\t)\n\t\t\t\t\t})\n\t\tif (!index) throw new RelationshipError('INVALID_RELATION')\n\t\treturn index\n\t}\n\tfunction checkTraversal(declared: Edge, path?: readonly string[]) {\n\t\tfunction check(\n\t\t\ttable: string,\n\t\t\tfield: string,\n\t\t\tguard?: RelationGuard,\n\t\t\ttargetPath?: readonly string[],\n\t\t) {\n\t\t\tgroups?.checkOrder(\n\t\t\t\ttable,\n\t\t\t\tlookup(table, field, guard).fields.filter(\n\t\t\t\t\t(key) => key !== field && key !== guard?.discriminator,\n\t\t\t\t),\n\t\t\t\ttargetPath,\n\t\t\t)\n\t\t}\n\t\tif ('base' in declared.from && 'base' in declared.to) {\n\t\t\tcheck(declared.from.junction.table, declared.from.junction.field, declared.junctionGuard)\n\t\t\tcheck(declared.target, declared.to.base.field, declared.targetGuard, path)\n\t\t} else check(declared.target, base(declared.to).field, declared.targetGuard, path)\n\t}\n\tfunction relationIndex(\n\t\ttable: string,\n\t\tfield: string,\n\t\tplan: ReturnType<Planner['plan']>,\n\t\tconditions: readonly IndexCondition[],\n\t\tguard?: RelationGuard,\n\t): QueryIndex {\n\t\tconst indexes =\n\t\t\tfield === '_id' ? [{ name: 'by_id', fields: ['_id'] }] : metadata.tables[table].indexes\n\t\tconst bound = (key: string) =>\n\t\t\tkey === field || key === guard?.discriminator || plan.bindings.has(key)\n\t\tconst usable = (index: QueryIndex) =>\n\t\t\tindex.fields.includes(field) &&\n\t\t\tindex.fields.slice(0, index.fields.indexOf(field)).every(bound)\n\t\tconst length = (index: QueryIndex) => {\n\t\t\tlet size = 0\n\t\t\tfor (const key of index.fields) {\n\t\t\t\tif (!bound(key)) break\n\t\t\t\tsize++\n\t\t\t}\n\t\t\treturn size\n\t\t}\n\t\tconst condition = conditions.find((c) => c.value !== null && c.value !== undefined)\n\t\tif (condition) {\n\t\t\tconst chosen = indexes.find((i) => i.name === condition.index && usable(i))\n\t\t\tif (!chosen)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Explicit index \"${condition.index}\" is incompatible with declared relationship lookup`,\n\t\t\t\t)\n\t\t\treturn chosen\n\t\t}\n\t\tconst candidates = indexes\n\t\t\t.filter(usable)\n\t\t\t.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\tlength(b) - length(a) ||\n\t\t\t\t\tNumber(plan.sets.has(b.fields[length(b)])) - Number(plan.sets.has(a.fields[length(a)])) ||\n\t\t\t\t\tNumber(plan.ranges.has(b.fields[length(b)])) -\n\t\t\t\t\t\tNumber(plan.ranges.has(a.fields[length(a)])) ||\n\t\t\t\t\ta.name.localeCompare(b.name) ||\n\t\t\t\t\ta.fields.join('\\0').localeCompare(b.fields.join('\\0')),\n\t\t\t)\n\t\tif (!candidates.length) throw new RelationshipError('INVALID_RELATION')\n\t\treturn candidates[0]\n\t}\n\tfunction relationPrefix(\n\t\tindex: QueryIndex,\n\t\tfield: string,\n\t\tvalue: FilterScalar,\n\t\tplan?: ReturnType<Planner['plan']>,\n\t\tguard?: RelationGuard,\n\t) {\n\t\tconst prefix: { field: string; value: FilterScalar }[] = []\n\t\tfor (const key of index.fields) {\n\t\t\tif (key === field) prefix.push({ field: key, value })\n\t\t\telse if (key === guard?.discriminator) prefix.push({ field: key, value: guard.tag })\n\t\t\telse if (plan?.bindings.has(key)) prefix.push({ field: key, value: plan.bindings.get(key)! })\n\t\t\telse break\n\t\t}\n\t\treturn prefix\n\t}\n\n\tasync function get(table: string, id: Value | undefined): Promise<Document | null> {\n\t\tplanner.beforeLeaf(table)\n\t\t// SAFETY: native normalization rejects non-string/non-table IDs before any read.\n\t\tif (id == null || !request.db.normalizeId(table, id as string)) return null\n\t\t// SAFETY: normalizeId verifies the declared native table before the authorized metered get.\n\t\tconst result = await request.db.get(table, id as GenericId<string>)\n\t\tif (result) planner.validateDocument(table, result)\n\t\t// SAFETY: normalizeId checked the table; the authorized native get retains system fields.\n\t\treturn result as Document | null\n\t}\n\tasync function* targets(\n\t\ttable: string,\n\t\tfield: string,\n\t\tvalue: Value | undefined,\n\t\tdepth: number,\n\t\tguard?: RelationGuard,\n\t\tmember?: Edge['targetMember'],\n\t): AsyncGenerator<Document> {\n\t\trequest.expand(depth, 0)\n\t\tif (value == null || !memberMatches(value, member)) return\n\t\tif (field === '_id') {\n\t\t\tconst target = await get(table, value)\n\t\t\tif (target && guarded(target, guard) && memberMatches(target[field], member)) {\n\t\t\t\trequest.expand(depth)\n\t\t\t\tyield target\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tplanner.beforeLeaf(table)\n\t\tconst index = lookup(table, field, guard)\n\t\t// SAFETY: registry validates the indexed native lookup prefix and compatible values.\n\t\tconst source = request.leafQuery(table).withIndex(index.name, (q) => {\n\t\t\t// SAFETY: registration checks the mandatory join and every preceding guard equality.\n\t\t\tlet range = q as IndexRangeBuilder<any, string[], number>\n\t\t\t// SAFETY: registered relation fields are native scalars; each equality advances the consecutive checked prefix.\n\t\t\tfor (const binding of relationPrefix(index, field, value as FilterScalar, undefined, guard)) {\n\t\t\t\t// SAFETY: the prefix comes from the declared index and registry-owned guard.\n\t\t\t\trange = range.eq(binding.field, binding.value) as IndexRangeBuilder<any, string[], number>\n\t\t\t}\n\t\t\treturn range\n\t\t})\n\t\tconst iterator = source[Symbol.asyncIterator]()\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tplanner.beforeLeaf(table)\n\t\t\t\tconst next = await iterator.next()\n\t\t\t\tif (next.done) return\n\t\t\t\tconst permitted = await request.authorize(table, next.value)\n\t\t\t\t// Denials are an await boundary too; never hide them inside an authorized iterator.\n\t\t\t\tif (!permitted) continue\n\t\t\t\tplanner.validateDocument(table, next.value)\n\t\t\t\tif (!guarded(next.value, guard) || !memberMatches(next.value[field], member)) continue\n\t\t\t\trequest.expand(depth)\n\t\t\t\t// SAFETY: native rows include system fields and explicit authorization just completed.\n\t\t\t\tyield next.value as Document\n\t\t\t}\n\t\t} finally {\n\t\t\tawait iterator.return?.()\n\t\t}\n\t}\n\tasync function* traverse(\n\t\tdeclared: Edge,\n\t\tparent: Document,\n\t\tdepth: number,\n\t): AsyncGenerator<Document> {\n\t\trequest.expand(depth, 0)\n\t\tconst value = sourceValue(declared, parent)\n\t\tif (value == null) return\n\t\tif ('base' in declared.from && 'base' in declared.to) {\n\t\t\tfor await (const junction of targets(\n\t\t\t\tdeclared.from.junction.table,\n\t\t\t\tdeclared.from.junction.field,\n\t\t\t\tvalue,\n\t\t\t\tdepth,\n\t\t\t\tdeclared.junctionGuard,\n\t\t\t))\n\t\t\t\tyield* targets(\n\t\t\t\t\tdeclared.target,\n\t\t\t\t\tdeclared.to.base.field,\n\t\t\t\t\tjunction[declared.to.junction.field],\n\t\t\t\t\tdepth,\n\t\t\t\t\tdeclared.targetGuard,\n\t\t\t\t)\n\t\t} else\n\t\t\tyield* targets(\n\t\t\t\tdeclared.target,\n\t\t\t\tbase(declared.to).field,\n\t\t\t\tvalue,\n\t\t\t\tdepth,\n\t\t\t\tdeclared.targetGuard,\n\t\t\t\tdeclared.targetMember,\n\t\t\t)\n\t}\n\tasync function one(declared: Edge, parent: Document, depth: number) {\n\t\tlet match: Document | null = null\n\t\tfor await (const target of traverse(declared, parent, depth)) {\n\t\t\tif (match) throw new RelationshipError('AMBIGUOUS_RELATION')\n\t\t\tmatch = target\n\t\t}\n\t\treturn match\n\t}\n\tasync function matches(\n\t\ttable: string,\n\t\tparent: Document,\n\t\tast: FilterAst,\n\t\tdepth = 0,\n\t): Promise<boolean> {\n\t\trequest.expand(depth, 0)\n\t\t// Variant rows must satisfy the captured stored branch before predicates\n\t\t// inspect branch fields. Ordinary output rows are validated by projection.\n\t\tif (metadata.tables[table].variant) planner.validateDocument(table, parent)\n\t\tif (scalarOnly(ast)) return scalarMatches(ast, parent)\n\t\tif (ast.kind === 'variant')\n\t\t\treturn parent[ast.discriminator] === ast.tag && matches(table, parent, ast.child, depth)\n\t\tif (ast.kind === 'scalar') return scalarMatches(ast, parent)\n\t\tif (ast.kind === 'not') return !(await matches(table, parent, ast.child, depth))\n\t\tif (ast.kind === 'and' || ast.kind === 'or') {\n\t\t\tfor (const child of ast.children) {\n\t\t\t\tconst result = await matches(table, parent, child, depth)\n\t\t\t\tif (ast.kind === 'and' && !result) return false\n\t\t\t\tif (ast.kind === 'or' && result) return true\n\t\t\t}\n\t\t\treturn ast.kind === 'and'\n\t\t}\n\t\tif (ast.kind !== 'relation') throw new RelationshipError('INVALID_RELATION')\n\t\tconst declared = edge(table, ast.name)\n\t\tif (ast.mode === 'one') {\n\t\t\tconst target = await one(declared, parent, depth + 1)\n\t\t\treturn ast.predicate === null\n\t\t\t\t? target === null\n\t\t\t\t: target !== null && (await matches(declared.target, target, ast.predicate, depth + 1))\n\t\t}\n\t\tfor await (const target of traverse(declared, parent, depth + 1))\n\t\t\tif (await matches(declared.target, target, ast.predicate!, depth + 1))\n\t\t\t\treturn ast.mode === 'some'\n\t\t// Only actual authorized traversal exhaustion proves none.\n\t\treturn ast.mode === 'none'\n\t}\n\tasync function output(\n\t\ttable: string,\n\t\tparent: Document,\n\t\tselection: OutputSelection,\n\t\targs: NativePagination & { order?: 'asc' | 'desc' },\n\t\tpath: readonly string[] = [],\n\t\tdepth = 0,\n\t\tparents: readonly string[] = [],\n\t): Promise<Record<string, Value>> {\n\t\trequest.expand(depth, 0)\n\t\t// SAFETY: the registry strictly validates this public native DTO projection.\n\t\tconst dto = planner.complete(\n\t\t\ttable,\n\t\t\tparent,\n\t\t\ttrue,\n\t\t\t{ kind: 'and', children: [] },\n\t\t\tgroups?.allowed(path),\n\t\t) as Record<string, Value>\n\t\tfor (const [name, selected] of Object.entries(selection)) {\n\t\t\tconst declared = edge(table, name),\n\t\t\t\tnested = selected === true ? {} : selected\n\t\t\tif (declared.cardinality === 'one') {\n\t\t\t\tconst target = await one(declared, parent, depth + 1)\n\t\t\t\tdto[name] = target\n\t\t\t\t\t? await output(declared.target, target, nested, args, [...path, name], depth + 1, [\n\t\t\t\t\t\t\t...parents,\n\t\t\t\t\t\t\tparent._id,\n\t\t\t\t\t\t])\n\t\t\t\t\t: null\n\t\t\t} else {\n\t\t\t\tconst page = await pageRelation(\n\t\t\t\t\ttable,\n\t\t\t\t\tparent,\n\t\t\t\t\tname,\n\t\t\t\t\tnested,\n\t\t\t\t\t{ numItems: args.numItems, cursor: null, order: args.order },\n\t\t\t\t\t[],\n\t\t\t\t\t[],\n\t\t\t\t\t[...path, name],\n\t\t\t\t\tdepth + 1,\n\t\t\t\t\t[...parents, parent._id],\n\t\t\t\t)\n\t\t\t\t// SAFETY: pageRelation projects and validates each nested DTO row.\n\t\t\t\tdto[name] = page as Value\n\t\t\t}\n\t\t}\n\t\treturn dto\n\t}\n\tfunction prepare(\n\t\ttable: string,\n\t\tname: string,\n\t\tselection: OutputSelection,\n\t\tinput: QueryInput,\n\t\tconditions: readonly IndexCondition[],\n\t\tconstraints: readonly StoredConstraint[],\n\t\tpath: readonly string[],\n\t\trootId: string,\n\t\tordering?: string,\n\t) {\n\t\tif (ordering !== undefined && conditions.some((c) => c.value !== null && c.value !== undefined))\n\t\t\tthrow new Error('orderBy cannot be combined with an active when selector')\n\t\tplanner.select(edge(table, name).target, selection)\n\t\tassertRelationshipDepth(selection, path.length)\n\t\tconst declared = edge(table, name)\n\t\tif (declared.cardinality !== 'many') throw new RelationshipError('INVALID_RELATION')\n\t\tconst guardConstraint = declared.targetGuard\n\t\t\t? { fields: { [declared.targetGuard.discriminator]: declared.targetGuard.tag } }\n\t\t\t: undefined\n\t\tconst plan = planner.plan(\n\t\t\tdeclared.target,\n\t\t\tinput,\n\t\t\t[],\n\t\t\tguardConstraint ? [...constraints, guardConstraint] : constraints,\n\t\t\tordering,\n\t\t\t'relation',\n\t\t)\n\t\tconst from = declared.from,\n\t\t\tto = declared.to\n\t\tconst through =\n\t\t\t'base' in from && 'base' in to\n\t\t\t\t? {\n\t\t\t\t\t\ttable: from.junction.table,\n\t\t\t\t\t\tfield: from.junction.field,\n\t\t\t\t\t\ttargetField: to.base.field,\n\t\t\t\t\t\ttargetReference: to.junction.field,\n\t\t\t\t\t}\n\t\t\t\t: null\n\t\tif (ordering !== undefined && through)\n\t\t\tthrow new Error('orderBy is unsupported for junction relationships')\n\t\tconst sourceTable = through ? through.table : declared.target\n\t\tconst field = through ? through.field : base(to).field\n\t\tconst targetIndex = relationIndex(\n\t\t\tdeclared.target,\n\t\t\tthrough ? through.targetField : field,\n\t\t\tplan,\n\t\t\tordering === undefined ? conditions : [{ value: true, index: ordering }],\n\t\t\tdeclared.targetGuard,\n\t\t)\n\t\tgroups?.checkOrder(\n\t\t\tdeclared.target,\n\t\t\ttargetIndex.fields.filter(\n\t\t\t\t(key) =>\n\t\t\t\t\tkey !== (through ? through.targetField : field) &&\n\t\t\t\t\tkey !== declared.targetGuard?.discriminator &&\n\t\t\t\t\t!plan.bindings.has(key),\n\t\t\t),\n\t\t\tpath,\n\t\t)\n\t\tconst index = through ? lookup(sourceTable, field, declared.junctionGuard) : targetIndex\n\t\tif (through)\n\t\t\tgroups?.checkOrder(\n\t\t\t\tsourceTable,\n\t\t\t\tindex.fields.filter(\n\t\t\t\t\t(key) => key !== field && key !== declared.junctionGuard?.discriminator,\n\t\t\t\t),\n\t\t\t)\n\t\t// The lookup value changes only values, not prefix positions or comparator.\n\t\tconst expansion =\n\t\t\tthrough || plan.unsatisfiable\n\t\t\t\t? null\n\t\t\t\t: expansionForIndex(\n\t\t\t\t\t\tindex,\n\t\t\t\t\t\trelationPrefix(index, field, null, plan, declared.targetGuard),\n\t\t\t\t\t\tplan.sets,\n\t\t\t\t\t\tordering !== undefined ||\n\t\t\t\t\t\t\tconditions.some((c) => c.value !== null && c.value !== undefined),\n\t\t\t\t\t)\n\t\tconst groupBinding: Record<string, Value> = {}\n\t\tconst resolved = groups?.at(path)\n\t\tif (\n\t\t\tresolved &&\n\t\t\t(Object.keys(resolved.fieldGroups).length || Object.keys(resolved.relations).length)\n\t\t)\n\t\t\tgroupBinding.fieldGroups = resolved\n\t\t// SAFETY: planner.page validated the complete output mask before cursor binding.\n\t\tconst binding: CursorBinding = {\n\t\t\t...rootBinding,\n\t\t\ttable: sourceTable,\n\t\t\tindex: index.name,\n\t\t\torder: plan.args.order ?? 'asc',\n\t\t\tclientFilters: plan.clientIdentity,\n\t\t\tserverFilters: {\n\t\t\t\tpredicates: plan.sets.size\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tpredicates: plan.serverIdentity,\n\t\t\t\t\t\t\ttraversal: { version: 2, expansion: expansion ? { ...expansion } : null },\n\t\t\t\t\t\t}\n\t\t\t\t\t: plan.serverIdentity,\n\t\t\t\tselection: selection as Value,\n\t\t\t\t...groupBinding,\n\t\t\t\tscope: { ...request.scope },\n\t\t\t\trootTable: rootBinding.table,\n\t\t\t\tlookupIndex: targetIndex.name,\n\t\t\t\tguards: {\n\t\t\t\t\tsource: declared.sourceGuard ? { ...declared.sourceGuard } : null,\n\t\t\t\t\ttarget: declared.targetGuard ? { ...declared.targetGuard } : null,\n\t\t\t\t\tjunction: declared.junctionGuard ? { ...declared.junctionGuard } : null,\n\t\t\t\t},\n\t\t\t\t...((declared.sourceMember || declared.targetMember) && {\n\t\t\t\t\tmembers: {\n\t\t\t\t\t\tsource: declared.sourceMember ? { ...declared.sourceMember } : null,\n\t\t\t\t\t\ttarget: declared.targetMember ? { ...declared.targetMember } : null,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t},\n\t\t\trelationPath: path,\n\t\t\tparent: rootId,\n\t\t}\n\t\tfunction open(token: string) {\n\t\t\ttry {\n\t\t\t\treturn navigation.parse(JSON.parse(codec.open(token, binding)))\n\t\t\t} catch {\n\t\t\t\tthrow new CursorError()\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tdeclared,\n\t\t\tplan,\n\t\t\tthrough,\n\t\t\tsourceTable,\n\t\t\tfield,\n\t\t\tindex,\n\t\t\ttargetIndex,\n\t\t\tbinding,\n\t\t\topen,\n\t\t\texpansion,\n\t\t}\n\t}\n\tasync function pageRelation(\n\t\ttable: string,\n\t\tparent: Document | null,\n\t\tname: string,\n\t\tselection: OutputSelection,\n\t\tinput: QueryInput,\n\t\tconditions: readonly IndexCondition[] = [],\n\t\tconstraints: readonly StoredConstraint[] = [],\n\t\tpath: readonly string[] = [name],\n\t\tdepth = 1,\n\t\tparents: readonly string[] = [],\n\t\tprepared = prepare(\n\t\t\ttable,\n\t\t\tname,\n\t\t\tselection,\n\t\t\tinput,\n\t\t\tconditions,\n\t\t\tconstraints,\n\t\t\tpath,\n\t\t\tparents[0] ?? parent!._id,\n\t\t),\n\t\topened?: { cursor: string | null; endCursor?: string | null },\n\t) {\n\t\trequest.expand(depth, 0)\n\t\tconst { declared, plan, through, sourceTable, field, index, targetIndex, binding, expansion } =\n\t\t\tprepared\n\t\tconst value = sourceValue(declared, parent)\n\t\tconst args = { ...plan.args, ...(opened ?? { cursor: null }) }\n\t\t// SAFETY: declarations accept only native scalar lookup values; nullish references are absence.\n\t\tconst lookupPlan = {\n\t\t\t...plan,\n\t\t\texpansion,\n\t\t\tindex,\n\t\t\tprefix:\n\t\t\t\tvalue == null\n\t\t\t\t\t? []\n\t\t\t\t\t: through\n\t\t\t\t\t\t? relationPrefix(index, field, value as FilterScalar, undefined, declared.junctionGuard)\n\t\t\t\t\t\t: relationPrefix(index, field, value as FilterScalar, plan, declared.targetGuard),\n\t\t\tunsatisfiable: value == null || plan.unsatisfiable,\n\t\t}\n\t\tlookupPlan.range = through ? null : rangeForIndex(index, lookupPlan.prefix, plan.ranges)\n\t\tif (!through)\n\t\t\tfor (const binding of lookupPlan.prefix) {\n\t\t\t\tconst range = plan.ranges.get(binding.field)\n\t\t\t\tif (\n\t\t\t\t\tplan.bindings.has(binding.field) &&\n\t\t\t\t\tcompareValues(binding.value, plan.bindings.get(binding.field)!) !== 0\n\t\t\t\t)\n\t\t\t\t\tlookupPlan.unsatisfiable = true\n\t\t\t\tif (range && !withinRange(binding.value, range)) lookupPlan.unsatisfiable = true\n\t\t\t}\n\t\tconst indexedTarget = through && through.targetField !== '_id'\n\t\tconst result = await executeStream(\n\t\t\tschema,\n\t\t\trequest,\n\t\t\tsourceTable,\n\t\t\tlookupPlan,\n\t\t\tplanner,\n\t\t\tcodec,\n\t\t\tbinding,\n\t\t\targs,\n\t\t\tasync (row, permitted) => {\n\t\t\t\tif (permitted) planner.validateDocument(indexedTarget ? declared.target : sourceTable, row)\n\t\t\t\tif (!permitted || (through && !indexedTarget && !guarded(row, declared.junctionGuard)))\n\t\t\t\t\treturn null\n\t\t\t\trequest.expand(depth)\n\t\t\t\tlet target: Document | null\n\t\t\t\tif (through && !indexedTarget)\n\t\t\t\t\ttarget = await get(declared.target, row[through.targetReference])\n\t\t\t\telse {\n\t\t\t\t\t// SAFETY: native direct/flattened target streams retain validated system fields.\n\t\t\t\t\ttarget = row as Document\n\t\t\t\t}\n\t\t\t\tif (\n\t\t\t\t\t!target ||\n\t\t\t\t\t!guarded(target, declared.targetGuard) ||\n\t\t\t\t\t!memberMatches(target[base(declared.to).field], declared.targetMember) ||\n\t\t\t\t\t!(await matches(declared.target, target, plan.residual, depth))\n\t\t\t\t)\n\t\t\t\t\treturn null\n\t\t\t\treturn output(declared.target, target, selection, plan.args, path, depth, parents)\n\t\t\t},\n\t\t\t(raw) => codec.seal(JSON.stringify({ cursor: raw, ancestors: parents.slice(1) }), binding),\n\t\t\tindexedTarget\n\t\t\t\t? (source, db) => {\n\t\t\t\t\t\t// SAFETY: the registry owns the native schema/index descriptors. Public\n\t\t\t\t\t\t// helper flatMap concatenates native outer/inner keys and paginates them.\n\t\t\t\t\t\tconst nativeSchema = schema as SchemaDefinition<\n\t\t\t\t\t\t\tRecord<\n\t\t\t\t\t\t\t\tstring,\n\t\t\t\t\t\t\t\timport('convex/server').TableDefinition<any, Record<string, string[]>>\n\t\t\t\t\t\t\t>,\n\t\t\t\t\t\t\tboolean\n\t\t\t\t\t\t>\n\t\t\t\t\t\tconst fields = getIndexFields(declared.target, targetIndex.name, nativeSchema)\n\t\t\t\t\t\treturn source.flatMap(async (junction) => {\n\t\t\t\t\t\t\trequest.expand(depth)\n\t\t\t\t\t\t\tconst permitted = await request.authorize(sourceTable, junction)\n\t\t\t\t\t\t\tif (permitted) planner.validateDocument(sourceTable, junction)\n\t\t\t\t\t\t\tconst reference = junction[through.targetReference]\n\t\t\t\t\t\t\tif (!permitted || !guarded(junction, declared.junctionGuard) || reference == null)\n\t\t\t\t\t\t\t\treturn new EmptyStream(plan.args.order ?? 'asc', fields)\n\t\t\t\t\t\t\treturn stream(db, nativeSchema)\n\t\t\t\t\t\t\t\t.query(declared.target)\n\t\t\t\t\t\t\t\t.withIndex(targetIndex.name, (q) => {\n\t\t\t\t\t\t\t\t\t// SAFETY: registry-owned lookup bindings are native scalar index values.\n\t\t\t\t\t\t\t\t\tconst prefix = relationPrefix(\n\t\t\t\t\t\t\t\t\t\ttargetIndex,\n\t\t\t\t\t\t\t\t\t\tthrough.targetField,\n\t\t\t\t\t\t\t\t\t\treference as FilterScalar,\n\t\t\t\t\t\t\t\t\t\tplan,\n\t\t\t\t\t\t\t\t\t\tdeclared.targetGuard,\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\treturn nativeIndexRange(\n\t\t\t\t\t\t\t\t\t\tq,\n\t\t\t\t\t\t\t\t\t\tprefix,\n\t\t\t\t\t\t\t\t\t\trangeForIndex(targetIndex, prefix, plan.ranges),\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.order(plan.args.order ?? 'asc')\n\t\t\t\t\t\t}, fields)\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t\tindexedTarget ? declared.target : sourceTable,\n\t\t)\n\t\treturn result\n\t}\n\tasync function continuation(\n\t\trootTable: string,\n\t\trootId: string,\n\t\tpath: readonly string[],\n\t\tselection: OutputSelection,\n\t\tinput: QueryInput,\n\t\tconditions: readonly IndexCondition[],\n\t\tconstraints: readonly StoredConstraint[],\n\t\tordering?: string,\n\t) {\n\t\tif (!path.length || path.length > 4 || !request.db.normalizeId(rootTable, rootId))\n\t\t\tthrow new RelationshipError('INVALID_RELATION')\n\t\tlet table = rootTable\n\t\tconst declarations = path.map((name) => {\n\t\t\tconst declaration = edge(table, name)\n\t\t\ttable = declaration.target\n\t\t\treturn declaration\n\t\t})\n\t\tif (ordering !== undefined && declarations.some((d) => 'base' in d.from || 'base' in d.to))\n\t\t\tthrow new Error('orderBy is unsupported for junction relationships')\n\t\tconst final = declarations.at(-1)!\n\t\tfor (const declaration of declarations.slice(0, -1))\n\t\t\tif (\n\t\t\t\tdeclaration.cardinality === 'one' ||\n\t\t\t\t'base' in declaration.from ||\n\t\t\t\t'base' in declaration.to\n\t\t\t)\n\t\t\t\tcheckTraversal(declaration)\n\t\tconst prepared = prepare(\n\t\t\tfinal.source,\n\t\t\tpath.at(-1)!,\n\t\t\tselection,\n\t\t\tinput,\n\t\t\tconditions,\n\t\t\tconstraints,\n\t\t\tpath,\n\t\t\trootId,\n\t\t\tordering,\n\t\t)\n\t\t// Authenticate both slots and native ancestor IDs before ANY parent/traversal read.\n\t\tconst start = input.cursor === null ? null : prepared.open(input.cursor)\n\t\tconst end = input.endCursor == null ? null : prepared.open(input.endCursor)\n\t\tconst ancestors = start?.ancestors ?? end?.ancestors ?? []\n\t\tif (\n\t\t\tancestors.length !== path.length - 1 ||\n\t\t\t(start && end && JSON.stringify(start.ancestors) !== JSON.stringify(end.ancestors)) ||\n\t\t\tancestors.some((id, i) => !request.db.normalizeId(declarations[i].target, id))\n\t\t)\n\t\t\tthrow new CursorError()\n\t\t// Declarative input/index/cursor validation above retains its pre-read errors,\n\t\t// just as on the root path. Every awaited read, ancestry proof and page is protected.\n\t\ttry {\n\t\t\tlet parent = await get(rootTable, rootId)\n\t\t\tfor (let i = 0; parent && i < ancestors.length; i++) {\n\t\t\t\tconst declared = declarations[i]\n\t\t\t\tlet found: Document | null = null\n\t\t\t\tif (\n\t\t\t\t\tdeclared.cardinality === 'many' &&\n\t\t\t\t\t!('base' in declared.from) &&\n\t\t\t\t\t!('base' in declared.to)\n\t\t\t\t) {\n\t\t\t\t\t// A cursor names this ancestor. Prove its current authorized membership\n\t\t\t\t\t// by ID instead of replaying an ordering that is not part of this page.\n\t\t\t\t\trequest.expand(i + 1, 0)\n\t\t\t\t\tconst value = sourceValue(declared, parent)\n\t\t\t\t\tconst target = value == null ? null : await get(declared.target, ancestors[i])\n\t\t\t\t\tconst key = declared.to.field\n\t\t\t\t\tif (\n\t\t\t\t\t\tvalue != null &&\n\t\t\t\t\t\ttarget &&\n\t\t\t\t\t\tguarded(target, declared.targetGuard) &&\n\t\t\t\t\t\tmemberMatches(target[key], declared.targetMember) &&\n\t\t\t\t\t\ttarget[key] !== undefined &&\n\t\t\t\t\t\tcompareValues(value, target[key]) === 0\n\t\t\t\t\t) {\n\t\t\t\t\t\trequest.expand(i + 1)\n\t\t\t\t\t\tfound = target\n\t\t\t\t\t}\n\t\t\t\t} else if (declared.cardinality === 'one') {\n\t\t\t\t\tconst target = await one(declared, parent, i + 1)\n\t\t\t\t\tif (target?._id === ancestors[i]) found = target\n\t\t\t\t} else\n\t\t\t\t\tfor await (const target of traverse(declared, parent, i + 1)) {\n\t\t\t\t\t\tif (target._id === ancestors[i]) {\n\t\t\t\t\t\t\tfound = target\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tparent = found\n\t\t\t}\n\t\t\treturn await pageRelation(\n\t\t\t\tfinal.source,\n\t\t\t\tparent,\n\t\t\t\tpath.at(-1)!,\n\t\t\t\tselection,\n\t\t\t\tinput,\n\t\t\t\tconditions,\n\t\t\t\tconstraints,\n\t\t\t\tpath,\n\t\t\t\tpath.length,\n\t\t\t\t[rootId, ...ancestors],\n\t\t\t\tprepared,\n\t\t\t\t{ cursor: start?.cursor ?? null, endCursor: end?.cursor ?? input.endCursor },\n\t\t\t)\n\t\t} catch (error) {\n\t\t\trethrowQueryError(error)\n\t\t}\n\t}\n\t/** Admit the complete output tree even when a parent query produces no rows. */\n\tfunction preflight(\n\t\ttable: string,\n\t\tselection: OutputSelection,\n\t\tinput: QueryInput,\n\t\tpath: readonly string[] = [],\n\t) {\n\t\tfor (const [name, selected] of Object.entries(selection)) {\n\t\t\tconst declared = edge(table, name)\n\t\t\tconst nested = selected === true ? {} : selected\n\t\t\tconst nestedPath = [...path, name]\n\t\t\tif (declared.cardinality === 'many')\n\t\t\t\tprepare(\n\t\t\t\t\ttable,\n\t\t\t\t\tname,\n\t\t\t\t\tnested,\n\t\t\t\t\t{ numItems: input.numItems, cursor: null, order: input.order },\n\t\t\t\t\t[],\n\t\t\t\t\t[],\n\t\t\t\t\tnestedPath,\n\t\t\t\t\t'',\n\t\t\t\t)\n\t\t\telse checkTraversal(declared, nestedPath)\n\t\t\tpreflight(declared.target, nested, input, nestedPath)\n\t\t}\n\t}\n\treturn { matches, output, continuation, edge, preflight }\n}\n\nfunction scalarOnly(ast: FilterAst): boolean {\n\tif (ast.kind === 'relation') return false\n\tif (ast.kind === 'and' || ast.kind === 'or') return ast.children.every(scalarOnly)\n\tif (ast.kind === 'not' || ast.kind === 'variant') return scalarOnly(ast.child)\n\treturn true\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
//#region src/querying/schema-snapshot.d.ts
|
|
3
|
+
type Recipe = {
|
|
4
|
+
readonly kind: 'value';
|
|
5
|
+
readonly value: unknown;
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: 'array';
|
|
8
|
+
readonly items: readonly Recipe[];
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: 'record';
|
|
11
|
+
readonly entries: readonly (readonly [string, Recipe])[];
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: 'regexp';
|
|
14
|
+
readonly source: string;
|
|
15
|
+
readonly flags: string;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'node';
|
|
18
|
+
readonly constructor: string;
|
|
19
|
+
readonly definition: Recipe;
|
|
20
|
+
readonly id?: string;
|
|
21
|
+
};
|
|
22
|
+
export interface DtoRecipe<Schema extends z.ZodType = z.ZodType> {
|
|
23
|
+
readonly schemaType?: Schema;
|
|
24
|
+
readonly root: Recipe;
|
|
25
|
+
}
|
|
26
|
+
export declare function captureDto<Schema extends z.ZodType>(schema: Schema): DtoRecipe<Schema>;
|
|
27
|
+
export declare function materializeDto<Schema extends z.ZodType>(recipe: DtoRecipe<Schema>): Schema;
|
|
28
|
+
//#endregion
|
|
29
|
+
//# sourceMappingURL=schema-snapshot.d.mts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{zodToConvex as e}from"../zod-convex.mjs";import{zid as t}from"convex-helpers/server/zod4";import{z as n}from"zod";const r=/* @__PURE__ */ new Map;u(`object`,n.ZodObject),u(`array`,n.ZodArray),u(`optional`,n.ZodOptional),u(`exactOptional`,n.ZodExactOptional),u(`nullable`,n.ZodNullable),u(`nonoptional`,n.ZodNonOptional),u(`union`,n.ZodUnion),u(`discriminatedUnion`,n.ZodDiscriminatedUnion),u(`tuple`,n.ZodTuple),u(`record`,n.ZodRecord),u(`string`,n.ZodString),u(`number`,n.ZodNumber),u(`boolean`,n.ZodBoolean),u(`bigint`,n.ZodBigInt),u(`null`,n.ZodNull),u(`never`,n.ZodNever),u(`literal`,n.ZodLiteral),u(`enum`,n.ZodEnum),u(`custom`,n.ZodCustom),u(`minLength`,n.core.$ZodCheckMinLength),u(`maxLength`,n.core.$ZodCheckMaxLength),u(`length`,n.core.$ZodCheckLengthEquals),u(`lessThan`,n.core.$ZodCheckLessThan),u(`greaterThan`,n.core.$ZodCheckGreaterThan),u(`multipleOf`,n.core.$ZodCheckMultipleOf),u(`checkNumberFormat`,n.core.$ZodCheckNumberFormat),u(`checkBigintFormat`,n.core.$ZodCheckBigIntFormat),u(`stringFormat`,n.core.$ZodCheckStringFormat),u(`regex`,n.core.$ZodCheckRegex),u(`lowercase`,n.core.$ZodCheckLowerCase),u(`uppercase`,n.core.$ZodCheckUpperCase),u(`includes`,n.core.$ZodCheckIncludes),u(`startsWith`,n.core.$ZodCheckStartsWith),u(`endsWith`,n.core.$ZodCheckEndsWith),u(`property`,n.core.$ZodCheckProperty),u(`properties`,n.core.$ZodCheckProperties),u(`numberFormat`,n.ZodNumberFormat),u(`bigintFormat`,n.ZodBigIntFormat),u(`coreEmail`,n.core.$ZodEmail),u(`coreGUID`,n.core.$ZodGUID),u(`coreUUID`,n.core.$ZodUUID),u(`coreURL`,n.core.$ZodURL),u(`coreEmoji`,n.core.$ZodEmoji),u(`coreNanoID`,n.core.$ZodNanoID),u(`coreCUID`,n.core.$ZodCUID),u(`coreCUID2`,n.core.$ZodCUID2),u(`coreULID`,n.core.$ZodULID),u(`coreXID`,n.core.$ZodXID),u(`coreKSUID`,n.core.$ZodKSUID),u(`coreIPv4`,n.core.$ZodIPv4),u(`coreIPv6`,n.core.$ZodIPv6),u(`coreMAC`,n.core.$ZodMAC),u(`coreCIDRv4`,n.core.$ZodCIDRv4),u(`coreCIDRv6`,n.core.$ZodCIDRv6),u(`coreBase64`,n.core.$ZodBase64),u(`coreBase64URL`,n.core.$ZodBase64URL),u(`coreE164`,n.core.$ZodE164),u(`coreJWT`,n.core.$ZodJWT),u(`coreISODateTime`,n.core.$ZodISODateTime),u(`coreISODate`,n.core.$ZodISODate),u(`coreISOTime`,n.core.$ZodISOTime),u(`coreISODuration`,n.core.$ZodISODuration),u(`coreCreditCard`,n.core.$ZodCreditCard),u(`coreIBAN`,n.core.$ZodIBAN),u(`coreCustomStringFormat`,n.core.$ZodCustomStringFormat),u(`email`,n.ZodEmail),u(`guid`,n.ZodGUID),u(`uuid`,n.ZodUUID),u(`url`,n.ZodURL),u(`emoji`,n.ZodEmoji),u(`nanoid`,n.ZodNanoID),u(`cuid`,n.ZodCUID),u(`cuid2`,n.ZodCUID2),u(`ulid`,n.ZodULID),u(`xid`,n.ZodXID),u(`ksuid`,n.ZodKSUID),u(`ipv4`,n.ZodIPv4),u(`ipv6`,n.ZodIPv6),u(`mac`,n.ZodMAC),u(`cidrv4`,n.ZodCIDRv4),u(`cidrv6`,n.ZodCIDRv6),u(`base64`,n.ZodBase64),u(`base64url`,n.ZodBase64URL),u(`e164`,n.ZodE164),u(`jwt`,n.ZodJWT),u(`isoDateTime`,n.ZodISODateTime),u(`isoDate`,n.ZodISODate),u(`isoTime`,n.ZodISOTime),u(`isoDuration`,n.ZodISODuration),u(`creditCard`,n.ZodCreditCard),u(`iban`,n.ZodIBAN),u(`customStringFormat`,n.ZodCustomStringFormat);const i=[...r.entries()].reverse();function a(e){let t=/* @__PURE__ */ new WeakMap,r=/* @__PURE__ */ new WeakSet,a=0;function o(e,s){if(e===null||Object(e)!==e||e instanceof Function)return Object.freeze({kind:`value`,value:e});let c=e;if(r.has(c))throw Error(`Unsupported cyclic DTO schema`);let u=t.get(c);if(u)return u;if(s>64||++a>8192)throw Error(`DTO schema graph budget exceeded`);r.add(c);let d;if(e instanceof RegExp)d={kind:`regexp`,source:e.source,flags:e.flags};else if(e instanceof n.core.$ZodType||e instanceof n.core.$ZodCheck){if(e instanceof n.core.$ZodCheck&&e._zod.def.check===`custom`&&!(e instanceof n.ZodCustom))throw Error(`Unsupported opaque DTO check/superRefine; use .refine() instead`);let t=i.find(([,t])=>t.matches(e));if(!t)throw Error(`Unsupported DTO schema or check`);let r;if(e instanceof n.ZodCustom){let t=l(e);t.kind===`id`&&(r=t.tableName)}let a=e instanceof n.ZodObject?{...e._zod.def,shape:e.shape}:e._zod.def;d={kind:`node`,constructor:t[0],definition:o(a,s+1),id:r}}else if(Array.isArray(e))d={kind:`array`,items:Object.freeze(e.map(e=>o(e,s+1)))};else{if(Object.hasOwn(c,`_zod`))throw Error(`Unsupported opaque DTO check/superRefine; use .refine() instead`);if(Object.getPrototypeOf(e)!==Object.prototype&&Object.getPrototypeOf(e)!==null)throw Error(`Unsupported DTO definition object`);let t=[];for(let e of Reflect.ownKeys(c)){if(String(e)!==e)throw Error(`Unsupported DTO definition symbol`);let n=Object.getOwnPropertyDescriptor(c,e);if(!(`value`in n))throw Error(`Unsupported DTO schema accessor`);t.push(Object.freeze([e,o(n.value,s+1)]))}d={kind:`record`,entries:Object.freeze(t)}}return r.delete(c),Object.freeze(d),t.set(c,d),d}return Object.freeze({root:o(e,0)})}const o=/* @__PURE__ */ new WeakMap;function s(e){let n=o.get(e);if(n)return n;let i;switch(e.kind){case`value`:i=()=>e.value;break;case`regexp`:i=()=>new RegExp(e.source,e.flags);break;case`array`:{let t=e.items.map(s);i=()=>t.map(e=>e());break}case`record`:{let t=e.entries.map(([e,t])=>[e,s(t)]);i=()=>Object.fromEntries(t.map(([e,t])=>[e,t()]));break}case`node`:{let n=s(e.definition);if(e.id!==void 0){let r=e.id;i=()=>{let e=n(),i=t(r);if(e instanceof Object&&`checks`in e){let t=e.checks;if(Array.isArray(t))return i.check(...t)}return i}}else{let t=r.get(e.constructor);i=()=>t.create(n())}break}}return o.set(e,i),i}function c(e){let t=s(e.root)();if(!(t instanceof n.ZodType))throw Error(`Expected classic DTO schema`);return t}function l(t){return e(t)}function u(e,t){t!==void 0&&r.set(e,{matches:e=>e instanceof t,create:e=>new t(e)})}export{a as captureDto,c as materializeDto};
|
|
2
|
+
//# sourceMappingURL=schema-snapshot.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-snapshot.mjs","names":["zodToConvex","zodToConvex$1","constructors","register","z","dispatch","captureDto","seen","active","nodes","capture","value","object","cached","depth","recipe","entry","constructor","id","native","nativeValidator","definition","child","entries","key","descriptor","schema","factories","compile","factory","items","item","restored","result","zid","checks","materializeDto","Constructor","name","node"],"sources":["../../src/querying/schema-snapshot.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { GenericValidator } from 'convex/values'\nimport { zid } from 'convex-helpers/server/zod4'\nimport { zodToConvex } from '../zod-convex'\n\n// Recipes contain semantic definitions only. No source parser, constructor,\n// parent, hooks or lazy caches survive registration.\ntype Recipe =\n\t| { readonly kind: 'value'; readonly value: unknown }\n\t| { readonly kind: 'array'; readonly items: readonly Recipe[] }\n\t| { readonly kind: 'record'; readonly entries: readonly (readonly [string, Recipe])[] }\n\t| { readonly kind: 'regexp'; readonly source: string; readonly flags: string }\n\t| {\n\t\t\treadonly kind: 'node'\n\t\t\treadonly constructor: string\n\t\t\treadonly definition: Recipe\n\t\t\treadonly id?: string\n\t }\n\nexport interface DtoRecipe<Schema extends z.ZodType = z.ZodType> {\n\treadonly schemaType?: Schema\n\treadonly root: Recipe\n}\n\ntype Node = z.core.$ZodType | z.core.$ZodCheck\nconst constructors = new Map<\n\tstring,\n\t{\n\t\tmatches(node: Node): boolean\n\t\t// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Only recipe-restored semantic definitions enter the selected installed constructor.\n\t\tcreate(definition: unknown): Node\n\t}\n>()\n// Base classes precede specialized classes so the most specific installed\n// constructor wins. Never consult a caller's _zod.constr or constructor property.\nregister('object', z.ZodObject)\nregister('array', z.ZodArray)\nregister('optional', z.ZodOptional)\nregister('exactOptional', z.ZodExactOptional)\nregister('nullable', z.ZodNullable)\nregister('nonoptional', z.ZodNonOptional)\nregister('union', z.ZodUnion)\nregister('discriminatedUnion', z.ZodDiscriminatedUnion)\nregister('tuple', z.ZodTuple)\nregister('record', z.ZodRecord)\nregister('string', z.ZodString)\nregister('number', z.ZodNumber)\nregister('boolean', z.ZodBoolean)\nregister('bigint', z.ZodBigInt)\nregister('null', z.ZodNull)\nregister('never', z.ZodNever)\nregister('literal', z.ZodLiteral)\nregister('enum', z.ZodEnum)\nregister('custom', z.ZodCustom)\nregister('minLength', z.core.$ZodCheckMinLength)\nregister('maxLength', z.core.$ZodCheckMaxLength)\nregister('length', z.core.$ZodCheckLengthEquals)\nregister('lessThan', z.core.$ZodCheckLessThan)\nregister('greaterThan', z.core.$ZodCheckGreaterThan)\nregister('multipleOf', z.core.$ZodCheckMultipleOf)\nregister('checkNumberFormat', z.core.$ZodCheckNumberFormat)\nregister('checkBigintFormat', z.core.$ZodCheckBigIntFormat)\nregister('stringFormat', z.core.$ZodCheckStringFormat)\nregister('regex', z.core.$ZodCheckRegex)\nregister('lowercase', z.core.$ZodCheckLowerCase)\nregister('uppercase', z.core.$ZodCheckUpperCase)\nregister('includes', z.core.$ZodCheckIncludes)\nregister('startsWith', z.core.$ZodCheckStartsWith)\nregister('endsWith', z.core.$ZodCheckEndsWith)\nregister('property', z.core.$ZodCheckProperty)\nregister('properties', z.core.$ZodCheckProperties)\nregister('numberFormat', z.ZodNumberFormat)\nregister('bigintFormat', z.ZodBigIntFormat)\n// Format schemas are also checks; preserve their implicit checks, including\n// URL/JWT validation which cannot be represented by a regex alone.\nregister('coreEmail', z.core.$ZodEmail)\nregister('coreGUID', z.core.$ZodGUID)\nregister('coreUUID', z.core.$ZodUUID)\nregister('coreURL', z.core.$ZodURL)\nregister('coreEmoji', z.core.$ZodEmoji)\nregister('coreNanoID', z.core.$ZodNanoID)\nregister('coreCUID', z.core.$ZodCUID)\nregister('coreCUID2', z.core.$ZodCUID2)\nregister('coreULID', z.core.$ZodULID)\nregister('coreXID', z.core.$ZodXID)\nregister('coreKSUID', z.core.$ZodKSUID)\nregister('coreIPv4', z.core.$ZodIPv4)\nregister('coreIPv6', z.core.$ZodIPv6)\nregister('coreMAC', z.core.$ZodMAC)\nregister('coreCIDRv4', z.core.$ZodCIDRv4)\nregister('coreCIDRv6', z.core.$ZodCIDRv6)\nregister('coreBase64', z.core.$ZodBase64)\nregister('coreBase64URL', z.core.$ZodBase64URL)\nregister('coreE164', z.core.$ZodE164)\nregister('coreJWT', z.core.$ZodJWT)\nregister('coreISODateTime', z.core.$ZodISODateTime)\nregister('coreISODate', z.core.$ZodISODate)\nregister('coreISOTime', z.core.$ZodISOTime)\nregister('coreISODuration', z.core.$ZodISODuration)\nregister('coreCreditCard', z.core.$ZodCreditCard)\nregister('coreIBAN', z.core.$ZodIBAN)\nregister('coreCustomStringFormat', z.core.$ZodCustomStringFormat)\nregister('email', z.ZodEmail)\nregister('guid', z.ZodGUID)\nregister('uuid', z.ZodUUID)\nregister('url', z.ZodURL)\nregister('emoji', z.ZodEmoji)\nregister('nanoid', z.ZodNanoID)\nregister('cuid', z.ZodCUID)\nregister('cuid2', z.ZodCUID2)\nregister('ulid', z.ZodULID)\nregister('xid', z.ZodXID)\nregister('ksuid', z.ZodKSUID)\nregister('ipv4', z.ZodIPv4)\nregister('ipv6', z.ZodIPv6)\nregister('mac', z.ZodMAC)\nregister('cidrv4', z.ZodCIDRv4)\nregister('cidrv6', z.ZodCIDRv6)\nregister('base64', z.ZodBase64)\nregister('base64url', z.ZodBase64URL)\nregister('e164', z.ZodE164)\nregister('jwt', z.ZodJWT)\nregister('isoDateTime', z.ZodISODateTime)\nregister('isoDate', z.ZodISODate)\nregister('isoTime', z.ZodISOTime)\nregister('isoDuration', z.ZodISODuration)\nregister('creditCard', z.ZodCreditCard)\nregister('iban', z.ZodIBAN)\nregister('customStringFormat', z.ZodCustomStringFormat)\nconst dispatch = [...constructors.entries()].reverse()\n\nexport function captureDto<Schema extends z.ZodType>(schema: Schema): DtoRecipe<Schema> {\n\tconst seen = new WeakMap<object, Recipe>()\n\tconst active = new WeakSet<object>()\n\tlet nodes = 0\n\t// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Recursively decode preflighted Zod definitions into owned recipes.\n\tfunction capture(value: unknown, depth: number): Recipe {\n\t\tif (value === null || Object(value) !== value || value instanceof Function)\n\t\t\treturn Object.freeze({ kind: 'value', value })\n\t\t// SAFETY: the boxing equality above excludes primitives.\n\t\tconst object = value as object\n\t\tif (active.has(object)) throw new Error('Unsupported cyclic DTO schema')\n\t\tconst cached = seen.get(object)\n\t\tif (cached) return cached\n\t\tif (depth > 64 || ++nodes > 8192) throw new Error('DTO schema graph budget exceeded')\n\t\tactive.add(object)\n\t\tlet recipe: Recipe\n\t\tif (value instanceof RegExp) {\n\t\t\trecipe = { kind: 'regexp', source: value.source, flags: value.flags }\n\t\t} else if (value instanceof z.core.$ZodType || value instanceof z.core.$ZodCheck) {\n\t\t\tif (\n\t\t\t\tvalue instanceof z.core.$ZodCheck &&\n\t\t\t\tvalue._zod.def.check === 'custom' &&\n\t\t\t\t!(value instanceof z.ZodCustom)\n\t\t\t)\n\t\t\t\tthrow new Error('Unsupported opaque DTO check/superRefine; use .refine() instead')\n\t\t\tconst entry = dispatch.find(([, constructor]) => constructor.matches(value))\n\t\t\tif (!entry) throw new Error('Unsupported DTO schema or check')\n\t\t\tlet id: string | undefined\n\t\t\tif (value instanceof z.ZodCustom) {\n\t\t\t\tconst native = nativeValidator(value)\n\t\t\t\tif (native.kind === 'id') id = native.tableName\n\t\t\t}\n\t\t\tconst definition =\n\t\t\t\tvalue instanceof z.ZodObject\n\t\t\t\t\t? // oxlint-disable-next-line anti-slop/no-shape-in-symbol-names -- The installed Zod object definition requires this exact property.\n\t\t\t\t\t\t{ ...value._zod.def, shape: value.shape }\n\t\t\t\t\t: value._zod.def\n\t\t\trecipe = {\n\t\t\t\tkind: 'node',\n\t\t\t\tconstructor: entry[0],\n\t\t\t\tdefinition: capture(definition, depth + 1),\n\t\t\t\tid,\n\t\t\t}\n\t\t} else if (Array.isArray(value)) {\n\t\t\trecipe = {\n\t\t\t\tkind: 'array',\n\t\t\t\titems: Object.freeze(value.map((child) => capture(child, depth + 1))),\n\t\t\t}\n\t\t} else {\n\t\t\tif (Object.hasOwn(object, '_zod'))\n\t\t\t\tthrow new Error('Unsupported opaque DTO check/superRefine; use .refine() instead')\n\t\t\tif (\n\t\t\t\tObject.getPrototypeOf(value) !== Object.prototype &&\n\t\t\t\tObject.getPrototypeOf(value) !== null\n\t\t\t)\n\t\t\t\tthrow new Error('Unsupported DTO definition object')\n\t\t\tconst entries: (readonly [string, Recipe])[] = []\n\t\t\tfor (const key of Reflect.ownKeys(object)) {\n\t\t\t\tif (String(key) !== key) throw new Error('Unsupported DTO definition symbol')\n\t\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(object, key)!\n\t\t\t\tif (!('value' in descriptor)) throw new Error('Unsupported DTO schema accessor')\n\t\t\t\tentries.push(Object.freeze([key, capture(descriptor.value, depth + 1)]))\n\t\t\t}\n\t\t\trecipe = { kind: 'record', entries: Object.freeze(entries) }\n\t\t}\n\t\tactive.delete(object)\n\t\tObject.freeze(recipe)\n\t\tseen.set(object, recipe)\n\t\treturn recipe\n\t}\n\treturn Object.freeze({ root: capture(schema, 0) })\n}\n\n// Factories share immutable instructions only. Every occurrence invokes its factory\n// again, so shared recipe nodes never share mutable materialized definitions.\ntype Factory = () => unknown\nconst factories = new WeakMap<Recipe, Factory>()\n\nfunction compile(recipe: Recipe): Factory {\n\tconst cached = factories.get(recipe)\n\tif (cached) return cached\n\tlet factory: Factory\n\tswitch (recipe.kind) {\n\t\tcase 'value':\n\t\t\tfactory = () => recipe.value\n\t\t\tbreak\n\t\tcase 'regexp':\n\t\t\tfactory = () => new RegExp(recipe.source, recipe.flags)\n\t\t\tbreak\n\t\tcase 'array': {\n\t\t\tconst items = recipe.items.map(compile)\n\t\t\tfactory = () => items.map((item) => item())\n\t\t\tbreak\n\t\t}\n\t\tcase 'record': {\n\t\t\tconst entries = recipe.entries.map(([key, child]) => [key, compile(child)] as const)\n\t\t\tfactory = () => Object.fromEntries(entries.map(([key, child]) => [key, child()]))\n\t\t\tbreak\n\t\t}\n\t\tcase 'node': {\n\t\t\tconst definition = compile(recipe.definition)\n\t\t\tif (recipe.id !== undefined) {\n\t\t\t\tconst id = recipe.id\n\t\t\t\tfactory = () => {\n\t\t\t\t\tconst restored = definition()\n\t\t\t\t\tconst result = zid(id)\n\t\t\t\t\t// The new zid is registered with convex-helpers. Added refinements are\n\t\t\t\t\t// reconstructed independently, never inherited via _zod.parent.\n\t\t\t\t\tif (restored instanceof Object && 'checks' in restored) {\n\t\t\t\t\t\tconst checks = restored.checks\n\t\t\t\t\t\tif (Array.isArray(checks)) return result.check(...checks)\n\t\t\t\t\t}\n\t\t\t\t\treturn result\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst constructor = constructors.get(recipe.constructor)!\n\t\t\t\tfactory = () => constructor.create(definition())\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t}\n\tfactories.set(recipe, factory)\n\treturn factory\n}\n\nexport function materializeDto<Schema extends z.ZodType>(recipe: DtoRecipe<Schema>): Schema {\n\tconst result = compile(recipe.root)()\n\tif (!(result instanceof z.ZodType)) throw new Error('Expected classic DTO schema')\n\t// SAFETY: the recipe preserves the captured schema constructor and definition.\n\treturn result as Schema\n}\n\nfunction nativeValidator(schema: z.ZodType): GenericValidator {\n\treturn zodToConvex(schema)\n}\n\nfunction register<T extends Node>(\n\tname: string,\n\tConstructor: z.core.$constructor<T> | undefined,\n): void {\n\tif (Constructor === undefined) return\n\tconstructors.set(name, {\n\t\tmatches: (node) => node instanceof Constructor,\n\t\tcreate: (definition) => {\n\t\t\t// SAFETY: capture selected this installed constructor and recursively copied\n\t\t\t// its own semantic definition; materialization restores every child first.\n\t\t\treturn new Constructor(definition as T['_zod']['def'])\n\t\t},\n\t})\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { FilterAst, FilterScalar } from "./shared/ast.mjs";
|
|
2
|
+
import { FieldRange, IndexExpansion, NativeRange, QueryIndex } from "./filters.mjs";
|
|
3
|
+
import { EnginePaginationResult } from "../zod-table.mjs";
|
|
4
|
+
import { SearchIndexMetadata } from "../relations.mjs";
|
|
5
|
+
import { ManagedReadRequest } from "./security.mjs";
|
|
6
|
+
import { QueryInput, StoredConstraint, createPlanner } from "./planner.mjs";
|
|
7
|
+
import { CursorBinding, createCursorCodec } from "./cursors.mjs";
|
|
8
|
+
import { GenericDataModel, GenericDocument } from "convex/server";
|
|
9
|
+
import { Value } from "convex/values";
|
|
10
|
+
//#region src/querying/search.d.ts
|
|
11
|
+
export type SearchErrorCode = 'SEARCH_TEXT_REQUIRED' | 'SEARCH_END_CURSOR_UNSUPPORTED' | 'SEARCH_ORDER_UNSUPPORTED' | 'SEARCH_RELATION_UNSUPPORTED' | 'SEARCH_OPTIONS_UNSUPPORTED' | 'SEARCH_PROTOCOL_UNSUPPORTED';
|
|
12
|
+
export declare class SearchError extends Error {
|
|
13
|
+
readonly code: SearchErrorCode;
|
|
14
|
+
constructor(code: SearchErrorCode);
|
|
15
|
+
}
|
|
16
|
+
export type SearchPaginationResult<Row> = Omit<EnginePaginationResult<Row>, 'splitCursor' | 'pageStatus'> & {
|
|
17
|
+
paginationMode: 'native-search';
|
|
18
|
+
splitCursor: null;
|
|
19
|
+
pageStatus: null;
|
|
20
|
+
};
|
|
21
|
+
export interface SearchInput {
|
|
22
|
+
numItems: number;
|
|
23
|
+
cursor: string | null;
|
|
24
|
+
id?: number;
|
|
25
|
+
scanLimit?: number;
|
|
26
|
+
filters?: QueryInput['filters'];
|
|
27
|
+
}
|
|
28
|
+
/** Search compilation deliberately supplies no browse indexes or expansion directives. */
|
|
29
|
+
export declare function planSearch(planner: ReturnType<typeof createPlanner>, table: string, index: string, text: string, input: SearchInput, constraints: readonly StoredConstraint[]): {
|
|
30
|
+
plan: {
|
|
31
|
+
expansion: IndexExpansion | null;
|
|
32
|
+
sets: ReadonlyMap<string, FilterScalar[]>;
|
|
33
|
+
residual: FilterAst;
|
|
34
|
+
bindings: ReadonlyMap<string, FilterScalar>;
|
|
35
|
+
ranges: ReadonlyMap<string, FieldRange>;
|
|
36
|
+
range: NativeRange | null;
|
|
37
|
+
unsatisfiable: boolean;
|
|
38
|
+
index: QueryIndex | null;
|
|
39
|
+
prefix: readonly {
|
|
40
|
+
field: string;
|
|
41
|
+
value: FilterScalar;
|
|
42
|
+
}[];
|
|
43
|
+
clientIdentity: Value;
|
|
44
|
+
serverIdentity: Value;
|
|
45
|
+
args: {
|
|
46
|
+
cursor: string | null;
|
|
47
|
+
endCursor?: string | null | undefined;
|
|
48
|
+
id?: number | undefined;
|
|
49
|
+
onEmptyBudget?: "split" | undefined;
|
|
50
|
+
maximumRowsRead?: number | undefined;
|
|
51
|
+
maximumBytesRead?: number | undefined;
|
|
52
|
+
filters?: unknown;
|
|
53
|
+
order?: "asc" | "desc" | undefined;
|
|
54
|
+
numItems: number;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
text: string;
|
|
58
|
+
metadata: SearchIndexMetadata;
|
|
59
|
+
scanLimit: number | undefined;
|
|
60
|
+
equalities: {
|
|
61
|
+
field: string;
|
|
62
|
+
value: string | number | bigint | boolean;
|
|
63
|
+
}[];
|
|
64
|
+
};
|
|
65
|
+
type SearchPlan = ReturnType<typeof planSearch>;
|
|
66
|
+
/** One complete native window. No result token is minted until all descendants finish. */
|
|
67
|
+
export declare function executeSearch(request: ManagedReadRequest<GenericDataModel>, table: string, search: SearchPlan, codec: ReturnType<typeof createCursorCodec>, binding: CursorBinding, maxPageSize: number, complete: (document: GenericDocument) => Promise<Record<string, Value> | null>): Promise<SearchPaginationResult<Record<string, Value>>>;
|
|
68
|
+
//#endregion
|
|
69
|
+
//# sourceMappingURL=search.d.mts.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import"./filters.mjs";import{CursorError as e}from"./cursors.mjs";import{rethrowQueryError as t}from"./execution.mjs";import{z as n}from"zod";var r=class extends Error{code;constructor(e){super(e),this.code=e,this.name=`SearchError`}};const i=n.strictObject({numItems:n.number().int().positive(),cursor:n.string().nullable(),id:n.number().optional(),scanLimit:n.number().int().positive().max(2**53-1).optional(),filters:n.any().optional(),endCursor:n.null().optional()}),a=n.looseObject({endCursor:n.any().optional(),order:n.any().optional(),maximumRowsRead:n.any().optional(),maximumBytesRead:n.any().optional(),onEmptyBudget:n.any().optional()}),o=n.object({page:n.array(n.any()),isDone:n.boolean(),continueCursor:n.string(),splitCursor:n.null().optional(),pageStatus:n.null().optional(),completeness:n.literal(`complete`).optional()});function s(e,t,o,s,c,l){if(!n.string().safeParse(s).success||!s.trim())throw new r(`SEARCH_TEXT_REQUIRED`);let u=a.safeParse(c);if(!u.success)throw new r(`SEARCH_OPTIONS_UNSUPPORTED`);let d=u.data;if(d.endCursor!=null)throw new r(`SEARCH_END_CURSOR_UNSUPPORTED`);if(Object.hasOwn(d,`order`))throw new r(`SEARCH_ORDER_UNSUPPORTED`);if([`maximumRowsRead`,`maximumBytesRead`,`onEmptyBudget`].some(e=>Object.hasOwn(d,e)))throw new r(`SEARCH_OPTIONS_UNSUPPORTED`);let f=i.safeParse(c);if(!f.success)throw new r(`SEARCH_OPTIONS_UNSUPPORTED`);let p=e.metadata.tables[t]?.searchIndexes.find(e=>e.name===o&&!e.staged);if(!p)throw new r(`SEARCH_OPTIONS_UNSUPPORTED`);let{scanLimit:m,endCursor:h,...g}=f.data,_=e.plan(t,{...g,numItems:Math.min(g.numItems,100)},[],l,void 0,`relation`),v={..._,args:{..._.args,numItems:g.numItems}},y=/* @__PURE__ */ new Map;function b(e){if(e.kind===`and`)for(let t of e.children)b(t);else e.kind===`scalar`&&e.operator===`eq`&&!Array.isArray(e.value)&&y.set(e.field,e.value)}return b(v.residual),{plan:v,text:s.trim(),metadata:p,scanLimit:m,equalities:p.filterFields.filter(e=>y.has(e)).map(e=>({field:e,value:y.get(e)}))}}const c=n.strictObject({v:n.literal(1),mode:n.literal(`search`),nativeCursor:n.string().nullable(),seen:n.number().int().nonnegative().max(2**53-1)});async function l(n,i,a,s,l,u,d){let f={v:1,mode:`search`,nativeCursor:null,seen:0};if(a.plan.args.cursor!==null)try{f=c.parse(JSON.parse(s.open(a.plan.args.cursor,l)))}catch{throw new e}let p=a.scanLimit,m=p!==void 0&&f.seen>=p,h=(e,t,n,r)=>({page:e,isDone:r,continueCursor:s.seal(JSON.stringify({v:1,mode:`search`,nativeCursor:t,seen:n}),l),completeness:p!==void 0&&n>=p?`result-cap`:`complete`,paginationMode:`native-search`,splitCursor:null,pageStatus:null});if(m||a.plan.unsatisfiable)return h([],f.nativeCursor,f.seen,!0);try{let e={cursor:f.nativeCursor,numItems:Math.min(a.plan.args.numItems,u,n.budgets.searchCandidates,p===void 0?1/0:p-f.seen),id:a.plan.args.id},t=await n.search(i,a.metadata,a.text,a.equalities,e);if(!o.safeParse(t).success||!t.isDone&&t.continueCursor===f.nativeCursor)throw new r(`SEARCH_PROTOCOL_UNSUPPORTED`);let s=f.seen+t.page.length;if(!Number.isSafeInteger(s))throw new r(`SEARCH_PROTOCOL_UNSUPPORTED`);let c=[];for(let e of t.page){let t=await d(e);t!==null&&c.push(t)}return h(c,t.continueCursor,s,t.isDone||p!==void 0&&s>=p)}catch(e){if(e instanceof r)throw e;t(e)}}export{r as SearchError,l as executeSearch,s as planSearch};
|
|
2
|
+
//# sourceMappingURL=search.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.mjs","names":["SearchError","code","searchInput","z","optionEnvelope","nativePage","planSearch","text","envelope","input","raw","key","parsed","metadata","planner","table","candidate","index","_endCursor","args","planned","constraints","plan","values","equalities","ast","child","field","payload","executeSearch","start","search","codec","binding","CursorError","cap","terminal","finish","seen","options","maxPageSize","request","result","page","document","projected","complete","error","rethrowQueryError"],"sources":["../../src/querying/search.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { createPlanner, QueryInput, StoredConstraint } from './planner'\nimport type { FilterAst, FilterScalar } from './filters'\nimport { MAX_PAGE_SIZE } from './filters'\n\nexport type SearchErrorCode =\n\t| 'SEARCH_TEXT_REQUIRED'\n\t| 'SEARCH_END_CURSOR_UNSUPPORTED'\n\t| 'SEARCH_ORDER_UNSUPPORTED'\n\t| 'SEARCH_RELATION_UNSUPPORTED'\n\t| 'SEARCH_OPTIONS_UNSUPPORTED'\n\t| 'SEARCH_PROTOCOL_UNSUPPORTED'\nexport class SearchError extends Error {\n\tconstructor(readonly code: SearchErrorCode) {\n\t\tsuper(code)\n\t\tthis.name = 'SearchError'\n\t}\n}\nexport type SearchPaginationResult<Row> = Omit<\n\timport('../zod-table').EnginePaginationResult<Row>,\n\t'splitCursor' | 'pageStatus'\n> & { paginationMode: 'native-search'; splitCursor: null; pageStatus: null }\nexport interface SearchInput {\n\tnumItems: number\n\tcursor: string | null\n\tid?: number\n\tscanLimit?: number\n\tfilters?: QueryInput['filters']\n}\nconst searchInput = z.strictObject({\n\tnumItems: z.number().int().positive(),\n\tcursor: z.string().nullable(),\n\tid: z.number().optional(),\n\tscanLimit: z.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),\n\tfilters: z.any().optional(),\n\tendCursor: z.null().optional(),\n})\nconst optionEnvelope = z.looseObject({\n\tendCursor: z.any().optional(),\n\torder: z.any().optional(),\n\tmaximumRowsRead: z.any().optional(),\n\tmaximumBytesRead: z.any().optional(),\n\tonEmptyBudget: z.any().optional(),\n})\nconst nativePage = z.object({\n\tpage: z.array(z.any()),\n\tisDone: z.boolean(),\n\tcontinueCursor: z.string(),\n\tsplitCursor: z.null().optional(),\n\tpageStatus: z.null().optional(),\n\tcompleteness: z.literal('complete').optional(),\n})\n\n/** Search compilation deliberately supplies no browse indexes or expansion directives. */\nexport function planSearch(\n\tplanner: ReturnType<typeof createPlanner>,\n\ttable: string,\n\tindex: string,\n\ttext: string,\n\tinput: SearchInput,\n\tconstraints: readonly StoredConstraint[],\n) {\n\tif (!z.string().safeParse(text).success || !text.trim())\n\t\tthrow new SearchError('SEARCH_TEXT_REQUIRED')\n\tconst envelope = optionEnvelope.safeParse(input)\n\tif (!envelope.success) throw new SearchError('SEARCH_OPTIONS_UNSUPPORTED')\n\tconst raw = envelope.data\n\tif (raw.endCursor != null) throw new SearchError('SEARCH_END_CURSOR_UNSUPPORTED')\n\tif (Object.hasOwn(raw, 'order')) throw new SearchError('SEARCH_ORDER_UNSUPPORTED')\n\tif (\n\t\t['maximumRowsRead', 'maximumBytesRead', 'onEmptyBudget'].some((key) => Object.hasOwn(raw, key))\n\t)\n\t\tthrow new SearchError('SEARCH_OPTIONS_UNSUPPORTED')\n\tconst parsed = searchInput.safeParse(input)\n\tif (!parsed.success) throw new SearchError('SEARCH_OPTIONS_UNSUPPORTED')\n\tconst metadata = planner.metadata.tables[table]?.searchIndexes.find(\n\t\t(candidate) => candidate.name === index && !candidate.staged,\n\t)\n\tif (!metadata) throw new SearchError('SEARCH_OPTIONS_UNSUPPORTED')\n\tconst { scanLimit, endCursor: _endCursor, ...args } = parsed.data\n\t// Relation planning with no ordering uses the existing grammar with no browse index selection.\n\t// The shared filter grammar enforces browse page limits; search owns a soft target.\n\tconst planned = planner.plan(\n\t\ttable,\n\t\t{ ...args, numItems: Math.min(args.numItems, MAX_PAGE_SIZE) },\n\t\t[],\n\t\tconstraints,\n\t\tundefined,\n\t\t'relation',\n\t)\n\tconst plan = { ...planned, args: { ...planned.args, numItems: args.numItems } }\n\tconst values = new Map<string, FilterScalar>()\n\tfunction equalities(ast: FilterAst): void {\n\t\tif (ast.kind === 'and') for (const child of ast.children) equalities(child)\n\t\telse if (ast.kind === 'scalar' && ast.operator === 'eq' && !Array.isArray(ast.value))\n\t\t\tvalues.set(ast.field, ast.value)\n\t}\n\tequalities(plan.residual)\n\treturn {\n\t\tplan,\n\t\ttext: text.trim(),\n\t\tmetadata,\n\t\tscanLimit,\n\t\tequalities: metadata.filterFields\n\t\t\t.filter((field) => values.has(field))\n\t\t\t.map((field) => ({ field, value: values.get(field)! })),\n\t}\n}\n\nimport type { GenericDataModel, GenericDocument } from 'convex/server'\nimport type { Value } from 'convex/values'\nimport { CursorError, type CursorBinding, type createCursorCodec } from './cursors'\nimport type { ManagedReadRequest } from './security'\nimport { rethrowQueryError } from './execution'\n\nconst payload = z.strictObject({\n\tv: z.literal(1),\n\tmode: z.literal('search'),\n\tnativeCursor: z.string().nullable(),\n\tseen: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),\n})\ntype SearchPlan = ReturnType<typeof planSearch>\n/** One complete native window. No result token is minted until all descendants finish. */\nexport async function executeSearch(\n\trequest: ManagedReadRequest<GenericDataModel>,\n\ttable: string,\n\tsearch: SearchPlan,\n\tcodec: ReturnType<typeof createCursorCodec>,\n\tbinding: CursorBinding,\n\tmaxPageSize: number,\n\tcomplete: (document: GenericDocument) => Promise<Record<string, Value> | null>,\n): Promise<SearchPaginationResult<Record<string, Value>>> {\n\tlet start: z.output<typeof payload> = {\n\t\tv: 1,\n\t\tmode: 'search',\n\t\tnativeCursor: null,\n\t\tseen: 0,\n\t}\n\tif (search.plan.args.cursor !== null) {\n\t\ttry {\n\t\t\tstart = payload.parse(JSON.parse(codec.open(search.plan.args.cursor, binding)))\n\t\t} catch {\n\t\t\tthrow new CursorError()\n\t\t}\n\t}\n\tconst cap = search.scanLimit\n\tconst terminal = cap !== undefined && start.seen >= cap\n\tconst finish = (\n\t\tpage: Record<string, Value>[],\n\t\tnativeCursor: string | null,\n\t\tseen: number,\n\t\tisDone: boolean,\n\t): SearchPaginationResult<Record<string, Value>> => ({\n\t\tpage,\n\t\tisDone,\n\t\tcontinueCursor: codec.seal(\n\t\t\tJSON.stringify({ v: 1, mode: 'search', nativeCursor, seen }),\n\t\t\tbinding,\n\t\t),\n\t\tcompleteness: cap !== undefined && seen >= cap ? 'result-cap' : 'complete',\n\t\tpaginationMode: 'native-search',\n\t\tsplitCursor: null,\n\t\tpageStatus: null,\n\t})\n\tif (terminal || search.plan.unsatisfiable) return finish([], start.nativeCursor, start.seen, true)\n\ttry {\n\t\tconst options = {\n\t\t\tcursor: start.nativeCursor,\n\t\t\tnumItems: Math.min(\n\t\t\t\tsearch.plan.args.numItems,\n\t\t\t\tmaxPageSize,\n\t\t\t\trequest.budgets.searchCandidates,\n\t\t\t\tcap === undefined ? Infinity : cap - start.seen,\n\t\t\t),\n\t\t\tid: search.plan.args.id,\n\t\t}\n\t\tconst result = await request.search(\n\t\t\ttable,\n\t\t\tsearch.metadata,\n\t\t\tsearch.text,\n\t\t\tsearch.equalities,\n\t\t\toptions,\n\t\t)\n\t\tif (!nativePage.safeParse(result).success) throw new SearchError('SEARCH_PROTOCOL_UNSUPPORTED')\n\t\tif (!result.isDone && result.continueCursor === start.nativeCursor)\n\t\t\tthrow new SearchError('SEARCH_PROTOCOL_UNSUPPORTED')\n\t\tconst seen = start.seen + result.page.length\n\t\tif (!Number.isSafeInteger(seen)) throw new SearchError('SEARCH_PROTOCOL_UNSUPPORTED')\n\t\tconst page: Record<string, Value>[] = []\n\t\tfor (const document of result.page) {\n\t\t\tconst projected = await complete(document)\n\t\t\tif (projected !== null) page.push(projected)\n\t\t}\n\t\treturn finish(\n\t\t\tpage,\n\t\t\tresult.continueCursor,\n\t\t\tseen,\n\t\t\tresult.isDone || (cap !== undefined && seen >= cap),\n\t\t)\n\t} catch (error) {\n\t\tif (error instanceof SearchError) throw error\n\t\trethrowQueryError(error)\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { FilterScalar } from "./shared/ast.mjs";
|
|
2
|
+
import "./filters.mjs";
|
|
3
|
+
import { SearchIndexMetadata } from "../relations.mjs";
|
|
1
4
|
import { Rules } from "../tenancy.mjs";
|
|
2
|
-
import { GenericDataModel, GenericDatabaseReader, GenericDocument, QueryInitializer } from "convex/server";
|
|
5
|
+
import { GenericDataModel, GenericDatabaseReader, GenericDocument, PaginationOptions, PaginationResult, QueryInitializer } from "convex/server";
|
|
3
6
|
//#region src/querying/security.d.ts
|
|
4
7
|
export declare const MAX_DOCUMENT_BYTES: number;
|
|
5
8
|
/** Additive context supplied to existing read/insert/modify rules on BOTH paths.
|
|
@@ -19,6 +22,7 @@ export interface ManagedScope {
|
|
|
19
22
|
readonly role: string | null;
|
|
20
23
|
}
|
|
21
24
|
export interface ManagedBudgets {
|
|
25
|
+
searchCandidates: number;
|
|
22
26
|
readOperations: number;
|
|
23
27
|
indexRanges: number;
|
|
24
28
|
rawCandidates: number;
|
|
@@ -61,8 +65,16 @@ export interface ManagedReadRequest<DM extends GenericDataModel> {
|
|
|
61
65
|
};
|
|
62
66
|
readonly budgets: Readonly<ManagedBudgets>;
|
|
63
67
|
readonly counters: ManagedCounters;
|
|
68
|
+
/** Only the private search operation enables atomic descendant completion. */
|
|
69
|
+
readonly atomicCompletion: boolean;
|
|
70
|
+
search(table: string, index: SearchIndexMetadata, text: string, equalities: readonly {
|
|
71
|
+
field: string;
|
|
72
|
+
value: FilterScalar;
|
|
73
|
+
}[], options: PaginationOptions): Promise<PaginationResult<GenericDocument>>;
|
|
64
74
|
/** Future native leaf adapter reserves BEFORE every actual next/get, not an outer stream. */
|
|
65
75
|
reserveLeaf(): LeafReservation;
|
|
76
|
+
/** Only raw leaf reservation failures from this request can be resumed by the executor. */
|
|
77
|
+
leafBudgetFailure(error: ManagedReadError): 'row-budget' | 'byte-budget' | undefined;
|
|
66
78
|
openRange(): void;
|
|
67
79
|
expand(depth: number, count?: number): void;
|
|
68
80
|
/** Raw candidate visibility for the private executor; every leaf is metered.
|
|
@@ -97,7 +109,12 @@ export type ManagedQueryContext<DM extends GenericDataModel> = {
|
|
|
97
109
|
/** Central construction, then one shared request per engine invocation.
|
|
98
110
|
* Requests may lower, never raise ceilings. Unknown/native contexts fail closed.
|
|
99
111
|
*/
|
|
100
|
-
export declare function createManagedSecurity<DM extends GenericDataModel = GenericDataModel>(config: ManagedSecurityConfig<DM>): <Ctx extends ManagedQueryContext<DM>>(ctx: Ctx, lower?: Partial<ManagedBudgets>) => ManagedReadRequest<DM
|
|
112
|
+
export declare function createManagedSecurity<DM extends GenericDataModel = GenericDataModel>(config: ManagedSecurityConfig<DM>): (<Ctx extends ManagedQueryContext<DM>>(ctx: Ctx, lower?: Partial<ManagedBudgets>) => ManagedReadRequest<DM>) & {
|
|
113
|
+
/** Scope-only admission for public cursors. Never returns the registered reader. */
|
|
114
|
+
cursorScope(ctx: ManagedQueryContext<DM>): ManagedScope & {
|
|
115
|
+
readonly policyVersion: string;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
101
118
|
/** Ordinary rule invocations share a policy-only meter. Ordinary db reads stay
|
|
102
119
|
* unmetered by engine ceilings; supplied rule dependencies never bypass policy.
|
|
103
120
|
* Mutation insert/modify receive the same supplied contract as read callbacks.
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{getDocumentSize as e}from"convex/values";const t=1048576,n=Object.freeze({readOperations:4096,indexRanges:1024,rawCandidates:8192,documentBytes:16*t,fanout:1024,depth:8,memoEntries:4096});var r=class extends Error{code;constructor(e){super(e),this.code=e,this.name=`ManagedReadError`}};const i=/* @__PURE__ */ new WeakMap;function a(e,t,n){let r=Object.freeze({get:t.get.bind(t),query:e=>c(t.query(e)),normalizeId:t.normalizeId.bind(t),system:Object.freeze({get:t.system.get.bind(t.system),query:e=>c(t.system.query(e)),normalizeId:t.system.normalizeId.bind(t.system)})});return i.set(r,{reader:e,...u(n)}),r}function o(e){if(!e.policyVersion||!e.tables.length||new Set(e.tables).size!==e.tables.length||e.tables.some(e=>!e||e.startsWith(`_`)))throw Error(`Invalid managed security configuration`);let t=new Set(e.tables);l(e.budgets);let a=Object.freeze({...n,...e.budgets}),o=e.policyVersion,s=e.trustedAdapter;
|
|
1
|
+
import{getDocumentSize as e}from"convex/values";const t=1048576,n=Object.freeze({searchCandidates:100,readOperations:4096,indexRanges:1024,rawCandidates:8192,documentBytes:16*t,fanout:1024,depth:8,memoEntries:4096});var r=class extends Error{code;constructor(e){super(e),this.code=e,this.name=`ManagedReadError`}};const i=/* @__PURE__ */ new WeakMap;function a(e,t,n){let r=Object.freeze({get:t.get.bind(t),query:e=>c(t.query(e)),normalizeId:t.normalizeId.bind(t),system:Object.freeze({get:t.system.get.bind(t.system),query:e=>c(t.system.query(e)),normalizeId:t.system.normalizeId.bind(t.system)})});return i.set(r,{reader:e,...u(n)}),r}function o(e){if(!e.policyVersion||!e.tables.length||new Set(e.tables).size!==e.tables.length||e.tables.some(e=>!e||e.startsWith(`_`)))throw Error(`Invalid managed security configuration`);let t=new Set(e.tables);l(e.budgets);let a=Object.freeze({...n,...e.budgets}),o=e.policyVersion,s=e.trustedAdapter;function c(e){let t=i.get(e.db),n=t?void 0:s?.(e);if(!t&&!n)throw new r(`UNMANAGED_QUERY_CONTEXT`);return t??{...n,...u(n)}}function f(e,n){return d(c(e),t,l(n,a),o)}return Object.assign(f,{cursorScope(e){if(!e?.db||[`insert`,`patch`,`replace`,`delete`].some(t=>t in e.db))throw new r(`UNMANAGED_QUERY_CONTEXT`);return Object.freeze({...c(e).scope,policyVersion:o})}})}function s(e,t,r){let i=d({reader:e,...u({rules:t,defaultPolicy:r,scope:{mode:`trusted`,principal:null,organization:null,tenant:null,role:null}})},null,n,`ordinary-policy`,!1),a={};for(let e of Object.keys(t)){let n=t[e],r={};n?.read&&(r.read=(t,n)=>i.authorize(e,n)),n?.insert&&(r.insert=(e,t)=>n.insert({db:i.db},t)),n?.modify&&(r.modify=(e,t)=>n.modify({db:i.db},t)),a[e]=r}return a}function c(e){return Object.freeze({fullTableScan:()=>c(e.fullTableScan()),withIndex:(...t)=>c(e.withIndex(...t)),withSearchIndex:(...t)=>c(e.withSearchIndex(...t)),filter:(...t)=>c(e.filter(...t)),order:(...t)=>c(e.order(...t)),paginate:e.paginate.bind(e),collect:e.collect.bind(e),take:e.take.bind(e),first:e.first.bind(e),unique:e.unique.bind(e),[Symbol.asyncIterator]:()=>{let t=e[Symbol.asyncIterator](),n={next:t.next.bind(t)};return t.return&&(n.return=t.return.bind(t)),t.throw&&(n.throw=t.throw.bind(t)),Object.freeze(n)}})}function l(e={},t=n){for(let[t,r]of Object.entries(e))if(!Object.hasOwn(n,t)||!Number.isSafeInteger(r)||r<=0)throw Error(`Invalid managed read budgets`);return Object.freeze({searchCandidates:Math.min(t.searchCandidates,e.searchCandidates??t.searchCandidates),readOperations:Math.min(t.readOperations,e.readOperations??t.readOperations),indexRanges:Math.min(t.indexRanges,e.indexRanges??t.indexRanges),rawCandidates:Math.min(t.rawCandidates,e.rawCandidates??t.rawCandidates),documentBytes:Math.min(t.documentBytes,e.documentBytes??t.documentBytes),fanout:Math.min(t.fanout,e.fanout??t.fanout),depth:Math.min(t.depth,e.depth??t.depth),memoEntries:Math.min(t.memoEntries,e.memoEntries??t.memoEntries)})}function u(e){let t=Object.fromEntries(Object.entries(e.rules).map(([e,t])=>[e,Object.freeze({...t})]));return Object.freeze({scope:Object.freeze({...e.scope}),rules:Object.freeze(t),defaultPolicy:e.defaultPolicy})}function d(n,i,a,o,s=!0){let c={readOperations:0,indexRanges:0,rawCandidates:0,documentBytesObserved:0,documentBytesReserved:0,fanout:0,memoEntries:0},l=Object.freeze({get readOperations(){return c.readOperations},get indexRanges(){return c.indexRanges},get rawCandidates(){return c.rawCandidates},get documentBytesObserved(){return c.documentBytesObserved},get documentBytesReserved(){return c.documentBytesReserved},get fanout(){return c.fanout},get memoEntries(){return c.memoEntries}}),u=!1,d={rawCandidates:0,documentBytes:0},f=/* @__PURE__ */ new Map,p=0,m=0,h=/* @__PURE__ */ new WeakMap,g=()=>{if(c.readOperations>=a.readOperations||c.rawCandidates+m>=a.rawCandidates)return`row-budget`;if(c.documentBytesObserved+c.documentBytesReserved+1048576>a.documentBytes)return`byte-budget`},_=()=>{throw new r(`MANAGED_READ_BUDGET`)},v=e=>{if(e.startsWith(`_`)||i&&!i.has(e))throw new r(`UNSUPPORTED_MANAGED_READ`)},y=()=>{c.indexRanges>=a.indexRanges&&_(),c.indexRanges++},b=()=>{g()&&_(),c.readOperations++,m++,c.documentBytesReserved+=t;let n=!1;return{settle(r){if(n)throw Error(`Leaf reservation already settled`);if(n=!0,m--,c.documentBytesReserved-=t,r){c.rawCandidates++;let t=e(r);c.documentBytesObserved+=t,t>1048576&&_()}}}},x=(e,t)=>JSON.stringify([e,t]),S=(e,t)=>{if(t.includes(e))throw new r(`POLICY_RECURSION`);t.length>=a.depth&&_()};async function C(e,t,i=[]){v(e);let a=t._id;if(!n.reader.normalizeId(e,a))throw new r(`UNSUPPORTED_MANAGED_READ`);let o=x(e,a);S(o,i);let s=n.rules[e]?.read;return s?s(Object.freeze({db:E([...i,o])}),t):n.defaultPolicy===`allow`}async function w(e,t,r){v(e);let i=x(e,t);if(S(i,r),f.has(i))return f.get(i);s&&c.memoEntries>=a.memoEntries&&_(),s&&(p++,c.memoEntries=f.size+p);try{y();let a=b(),o;try{o=await n.reader.get(e,t)}catch(e){throw a.settle(null),e}a.settle(o);let c=o&&await C(e,o,r)?o:null;return s&&f.set(i,c),c}finally{s&&(p--,c.memoEntries=f.size+p)}}function T(e,t,n,i){function o(){throw new r(`UNSUPPORTED_MANAGED_READ`)}async function*s(){y();let a;try{for(;;){let o=!i&&g();if(o){let e=new r(`MANAGED_READ_BUDGET`);throw h.set(e,o),e}let s=b(),c;try{a??=t[Symbol.asyncIterator](),c=await a.next()}catch(e){throw s.settle(null),e}if(s.settle(c.done?null:c.value),c.done)return;(!i||await C(e,c.value,n))&&(yield c.value)}}finally{await a?.return?.()}}async function c(e){if(!Number.isSafeInteger(e)||e<0||e>a.rawCandidates)throw new r(`MANAGED_READ_BUDGET`);let t=[];if(e===0)return t;for await(let n of s())if(t.push(n),t.length===e)break;return t}return Object.freeze({fullTableScan:()=>T(e,t.fullTableScan(),n,i),withIndex:(...r)=>T(e,t.withIndex(...r),n,i),order:(...r)=>T(e,t.order(...r),n,i),filter:o,withSearchIndex:o,paginate:o,collect:o,take:c,first:async()=>(await c(1))[0]??null,unique:async()=>{let e=await c(2);if(e.length>1)throw Error(`Query is not unique`);return e[0]??null},[Symbol.asyncIterator]:s})}function E(e){let t=()=>{throw new r(`UNSUPPORTED_MANAGED_READ`)};return Object.freeze({get:async(t,r)=>{if(r!==void 0)return w(t,r,e);let a=[...i??new Set(Object.keys(n.rules))].find(e=>n.reader.normalizeId(e,t));return a?w(a,t,e):null},query:t=>(v(t),T(t,n.reader.query(t),e,!0)),normalizeId:(e,t)=>(v(e),n.reader.normalizeId(e,t)),system:Object.freeze({get:t,query:t,normalizeId:t})})}return Object.freeze({db:E([]),scope:Object.freeze({...n.scope,policyVersion:o}),budgets:a,counters:l,get atomicCompletion(){return u},async search(t,i,o,s,l){if(v(t),u)throw new r(`UNSUPPORTED_MANAGED_READ`);u=!0,c.readOperations>=a.readOperations&&_(),y(),c.readOperations++;let f=await n.reader.query(t).withSearchIndex(i.name,e=>{let t=e.search(i.searchField,o);for(let{field:e,value:n}of s)t=t.eq(e,n);return t}).paginate(l);d.rawCandidates+=f.page.length;for(let t of f.page)d.documentBytes+=e(t);return f},reserveLeaf:b,leafBudgetFailure:e=>h.get(e),openRange:y,expand(e,t=1){(!Number.isSafeInteger(e)||e<0||e>a.depth||!Number.isSafeInteger(t)||t<0||c.fanout+t>a.fanout)&&_(),c.fanout+=t},leafQuery:e=>(v(e),T(e,n.reader.query(e),[],!1)),authorize:(e,t)=>C(e,t)})}export{t as MAX_DOCUMENT_BYTES,r as ManagedReadError,o as createManagedSecurity,a as registerQueryReader,s as suppliedPolicyRules};
|
|
2
2
|
//# sourceMappingURL=security.mjs.map
|