cvx-kit 0.1.19 → 0.1.20
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 +10 -0
- package/dist/querying/execution.mjs +1 -1
- package/dist/querying/execution.mjs.map +1 -1
- package/dist/querying/filters.d.mts +5 -0
- package/dist/querying/filters.mjs +1 -1
- package/dist/querying/filters.mjs.map +1 -1
- package/dist/querying/planner.d.mts +2 -11
- package/dist/querying/planner.mjs +1 -1
- package/dist/querying/planner.mjs.map +1 -1
- package/dist/querying/relationships.d.mts +3 -3
- 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.mjs +1 -1
- package/dist/querying.mjs.map +1 -1
- package/dist/relations.d.mts +4 -16
- package/dist/relations.mjs +1 -1
- package/dist/relations.mjs.map +1 -1
- package/dist/zod-table.mjs +1 -1
- package/dist/zod-table.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -156,6 +156,16 @@ public DTO or `null`. A many edge returns a bounded native page. Junction output
|
|
|
156
156
|
preserves duplicate edge occurrences, even when target reads are memoized.
|
|
157
157
|
Missing and unreadable targets have the same public result.
|
|
158
158
|
|
|
159
|
+
`defineRelations` captures table names, stored field semantics, public DTO
|
|
160
|
+
validation, variants, active indexes, and relation declarations. Complete schema
|
|
161
|
+
and index configuration before calling it. Later changes to source declarations
|
|
162
|
+
do not affect existing registries, helpers or engines. Create a new registry to
|
|
163
|
+
adopt changes. Caller-owned declarations remain mutable.
|
|
164
|
+
|
|
165
|
+
Relation DTOs support `.refine()`. They reject opaque `.superRefine()` and
|
|
166
|
+
`.check()` callbacks; express those validations with `.refine()` instead.
|
|
167
|
+
Captured callbacks still observe application state through their closures.
|
|
168
|
+
|
|
159
169
|
Variant tables keep their discriminated public unions. Use the relation callback's
|
|
160
170
|
second argument, `branch('payments', 'creditor_payment').creditorId`, for a
|
|
161
171
|
branch-specific reference; its tag is checked before lookup. See the
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{ManagedReadError as e}from"./security.mjs";import{CursorError as t}from"./cursors.mjs";import{RelationshipError as n}from"./relationships.mjs";import{EmptyStream as r,QueryStream as i,stream as a}from"convex-helpers/server/stream";function o(r){throw r instanceof e||r instanceof t||r instanceof n?r:Error(`Query execution failed`)}async function s(e,t,n,r,i,a,o,s){return c(e,t,n,r,i,a,o,{...r.args,cursor:r.args.cursor===null?null:a.open(r.args.cursor,o),endCursor:r.args.endCursor==null?r.args.endCursor:a.open(r.args.endCursor,o)},s)}async function c(e,t,n,i,s,c,u,d,p,m=e=>c.seal(e,u),h,g=n){let _=l(t,s);try{let o=a(_.db,e).query(n),c=(i.index?o.withIndex(i.index.name,e=>{let t=e;for(let{field:e,value:n}of i.prefix)t=t.eq(e,n);return t}):o).order(i.args.order??`asc`),l=h?h(c,_.db):c,u=i.unsatisfiable?await new r(l.getOrder(),l.getIndexFields(),l.getEqualityIndexFilter()).paginate(d):await new f(l,t,g,i,s,{completed:[],stopped:!1},p).paginate(d),v={...u,continueCursor:m(u.continueCursor)};return u.splitCursor!=null&&(v.splitCursor=m(u.splitCursor)),v}catch(e){o(e)}finally{await _.close()}}function l(e,t){let n=/* @__PURE__ */ new Set;function r(e,i){return new Proxy({},{get(a,o){let s=i;if(o===`withIndex`&&`withIndex`in s)return(...t)=>{let n=s.withIndex;return r(e,n.apply(s,t))};if(o===`order`&&`order`in s)return(...t)=>{let n=s.order;return r(e,n.apply(s,t))};if(o===Symbol.asyncIterator)return()=>{let r=s[Symbol.asyncIterator]();return n.add(r),{next:()=>(t.beforeLeaf(e),r.next())}};throw Error(`Unsupported private query operation`)}})}return{db:{query:n=>(t.beforeLeaf(n),r(n,e.leafQuery(n)))},async close(){let e=!1;for(let t of n)try{await t.return?.()}catch{e=!0}if(n.clear(),e)throw Error(`Query execution failed`)}}}function u(e){let t=e.counters,n=e.budgets;return t.readOperations<n.readOperations&&t.rawCandidates<n.rawCandidates&&t.documentBytesObserved+t.documentBytesReserved+1048576<=n.documentBytes}var d=class extends i{entry;source;constructor(e,t){super(),this.entry=e,this.source=t}getOrder(){return this.source.getOrder()}getIndexFields(){return this.source.getIndexFields()}getEqualityIndexFilter(){return this.source.getEqualityIndexFilter()}narrow(e){if(e.lowerBound.length||e.upperBound.length||!e.lowerBoundInclusive||!e.upperBoundInclusive)throw Error(`Invalid cursor encoder range`);return this}async*iterWithKeys(){yield[{},this.entry[1],this.entry[2]]}},f=class t extends i{source;request;table;plan;planner;progress;complete;constructor(e,t,n,r,i,a,o){super(),this.source=e,this.request=t,this.table=n,this.plan=r,this.planner=i,this.progress=a,this.complete=o}getOrder(){return this.source.getOrder()}getIndexFields(){return this.source.getIndexFields()}getEqualityIndexFilter(){return this.source.getEqualityIndexFilter()}narrow(e){return new t(this.source.narrow(e),this.request,this.table,this.plan,this.planner,this.progress,this.complete)}async*iterWithKeys(){let t=this.source.iterWithKeys(!0)[Symbol.asyncIterator]();try{for(;;){if(!u(this.request)){if(!this.progress.completed.length)throw new e(`MANAGED_READ_BUDGET`);this.progress.stopped=!0;return}let n,r=this.request.counters.rawCandidates;try{n=await t.next()
|
|
1
|
+
import{ManagedReadError as e}from"./security.mjs";import{CursorError as t}from"./cursors.mjs";import{RelationshipError as n}from"./relationships.mjs";import{EmptyStream as r,QueryStream as i,stream as a}from"convex-helpers/server/stream";function o(r){throw r instanceof e||r instanceof t||r instanceof n?r:Error(`Query execution failed`)}async function s(e,t,n,r,i,a,o,s){return c(e,t,n,r,i,a,o,{...r.args,cursor:r.args.cursor===null?null:a.open(r.args.cursor,o),endCursor:r.args.endCursor==null?r.args.endCursor:a.open(r.args.endCursor,o)},s)}async function c(e,t,n,i,s,c,u,d,p,m=e=>c.seal(e,u),h,g=n){let _=l(t,s);try{let o=a(_.db,e).query(n),c=(i.index?o.withIndex(i.index.name,e=>{let t=e;for(let{field:e,value:n}of i.prefix)t=t.eq(e,n);return t}):o).order(i.args.order??`asc`),l=h?h(c,_.db):c,u=i.unsatisfiable?await new r(l.getOrder(),l.getIndexFields(),l.getEqualityIndexFilter()).paginate(d):await new f(l,t,g,i,s,{completed:[],stopped:!1},p).paginate(d),v={...u,continueCursor:m(u.continueCursor)};return u.splitCursor!=null&&(v.splitCursor=m(u.splitCursor)),v}catch(e){o(e)}finally{await _.close()}}function l(e,t){let n=/* @__PURE__ */ new Set;function r(e,i){return new Proxy({},{get(a,o){let s=i;if(o===`withIndex`&&`withIndex`in s)return(...t)=>{let n=s.withIndex;return r(e,n.apply(s,t))};if(o===`order`&&`order`in s)return(...t)=>{let n=s.order;return r(e,n.apply(s,t))};if(o===Symbol.asyncIterator)return()=>{let r=s[Symbol.asyncIterator]();return n.add(r),{next:()=>(t.beforeLeaf(e),r.next())}};throw Error(`Unsupported private query operation`)}})}return{db:{query:n=>(t.beforeLeaf(n),r(n,e.leafQuery(n)))},async close(){let e=!1;for(let t of n)try{await t.return?.()}catch{e=!0}if(n.clear(),e)throw Error(`Query execution failed`)}}}function u(e){let t=e.counters,n=e.budgets;return t.readOperations<n.readOperations&&t.rawCandidates<n.rawCandidates&&t.documentBytesObserved+t.documentBytesReserved+1048576<=n.documentBytes}var d=class extends i{entry;source;constructor(e,t){super(),this.entry=e,this.source=t}getOrder(){return this.source.getOrder()}getIndexFields(){return this.source.getIndexFields()}getEqualityIndexFilter(){return this.source.getEqualityIndexFilter()}narrow(e){if(e.lowerBound.length||e.upperBound.length||!e.lowerBoundInclusive||!e.upperBoundInclusive)throw Error(`Invalid cursor encoder range`);return this}async*iterWithKeys(){yield[{},this.entry[1],this.entry[2]]}},f=class t extends i{source;request;table;plan;planner;progress;complete;constructor(e,t,n,r,i,a,o){super(),this.source=e,this.request=t,this.table=n,this.plan=r,this.planner=i,this.progress=a,this.complete=o}getOrder(){return this.source.getOrder()}getIndexFields(){return this.source.getIndexFields()}getEqualityIndexFilter(){return this.source.getEqualityIndexFilter()}narrow(e){return new t(this.source.narrow(e),this.request,this.table,this.plan,this.planner,this.progress,this.complete)}async*iterWithKeys(){let t=this.source.iterWithKeys(!0)[Symbol.asyncIterator]();try{for(;;){if(!u(this.request)){if(!this.progress.completed.length)throw new e(`MANAGED_READ_BUDGET`);this.progress.stopped=!0;return}let n,r=this.request.counters.rawCandidates;try{n=await t.next()}catch(t){if(t instanceof e&&t.code===`MANAGED_READ_BUDGET`&&this.progress.completed.length&&this.request.counters.rawCandidates===r){this.progress.stopped=!0;return}throw t}if(n.done)return;let[i,a,o]=n.value,s=null;if(i){let e=await this.request.authorize(this.table,i);s=this.complete?await this.complete(i,e):this.planner.complete(this.table,i,e,this.plan.residual)}let c=[s,a,o];this.progress.completed.push(c),u(this.request)||(this.progress.stopped=!0),yield c}}finally{await t.return?.()}}async paginate(t){let n=await super.paginate(t);if(!this.progress.stopped)return n;let r=this.progress.completed;if(!r.length)throw new e(`MANAGED_READ_BUDGET`);let i=async e=>(await new d(e,this.source).paginate({numItems:1,cursor:null})).continueCursor;return{...n,isDone:!1,pageStatus:`SplitRequired`,continueCursor:await i(r.at(-1)),splitCursor:await i(r[Math.floor((r.length-1)/2)])}}};export{s as executeQuery,c as executeStream,o as rethrowQueryError};
|
|
2
2
|
//# sourceMappingURL=execution.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution.mjs","names":["rethrowQueryError","error","ManagedReadError","CursorError","RelationshipError","executeQuery","executeStream","schema","request","table","plan","planner","codec","binding","complete","raw","leaves","leafReader","base","stream","ordered","range","q","field","value","source","transform","result","EmptyStream","args","EligibleStream","authorizationTable","protectedResult","seal","active","wrap","target","key","withIndex","order","iterator","failed","canFetch","c","b","CapturedKey","QueryStream","entry","bounds","progress","next","candidatesBefore","document","bytes","output","permitted","completed","encode"],"sources":["../../src/querying/execution.ts"],"sourcesContent":["import {\n\tEmptyStream,\n\tQueryStream,\n\tstream,\n\ttype IndexBounds,\n\ttype IndexKey,\n} from 'convex-helpers/server/stream'\nimport type {\n\tGenericDatabaseReader,\n\tGenericDataModel,\n\tGenericDocument,\n\tQueryInitializer,\n\tSchemaDefinition,\n\tGenericSchema,\n\tPaginationResult,\n\tTableDefinition,\n\tIndexRangeBuilder,\n} from 'convex/server'\nimport type { Value } from 'convex/values'\nimport type { createPlanner } from './planner'\nimport { MAX_DOCUMENT_BYTES, ManagedReadError, type ManagedReadRequest } from './security'\nimport { CursorError, type CursorBinding, type createCursorCodec } from './cursors'\nimport type { NativePagination } from './filters'\nimport { RelationshipError } from './relationships'\n\ntype Entry = [Record<string, Value> | null, IndexKey, number]\ntype Plan = ReturnType<ReturnType<typeof createPlanner>['plan']>\ntype Codec = ReturnType<typeof createCursorCodec>\nexport type ExecutionRequest = ManagedReadRequest<GenericDataModel>\ntype Request = Omit<ExecutionRequest, 'db'>\ntype ExecutionSchema = SchemaDefinition<\n\tRecord<string, TableDefinition<any, Record<string, string[]>>>,\n\tboolean\n>\n\n/** Private shared boundary: only the established fixed typed errors are public. */\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Thrown values enter this error boundary untrusted; the class checks below admit only the fixed public error contracts.\nexport function rethrowQueryError(error: unknown): never {\n\tif (\n\t\terror instanceof ManagedReadError ||\n\t\terror instanceof CursorError ||\n\t\terror instanceof RelationshipError\n\t)\n\t\tthrow error\n\t// Native/policy errors may contain traversal values or structured private data.\n\tthrow new Error('Query execution failed')\n}\n\nexport async function executeQuery(\n\tschema: SchemaDefinition<GenericSchema, boolean>,\n\trequest: Request,\n\ttable: string,\n\tplan: Plan,\n\tplanner: ReturnType<typeof createPlanner>,\n\tcodec: Codec,\n\tbinding: CursorBinding,\n\tcomplete?: (\n\t\tdocument: Record<string, Value>,\n\t\tpermitted: boolean,\n\t) => Promise<Record<string, Value> | null>,\n) {\n\t// Authenticate BOTH navigation slots before constructing or consuming native queries.\n\tconst args = {\n\t\t...plan.args,\n\t\tcursor: plan.args.cursor === null ? null : codec.open(plan.args.cursor, binding),\n\t\tendCursor:\n\t\t\tplan.args.endCursor == null ? plan.args.endCursor : codec.open(plan.args.endCursor, binding),\n\t}\n\treturn executeStream(schema, request, table, plan, planner, codec, binding, args, complete)\n}\n\nexport async function executeStream(\n\tschema: SchemaDefinition<GenericSchema, boolean>,\n\trequest: Request,\n\ttable: string,\n\tplan: Plan,\n\tplanner: ReturnType<typeof createPlanner>,\n\tcodec: Codec,\n\tbinding: CursorBinding,\n\targs: NativePagination,\n\tcomplete?: (\n\t\tdocument: Record<string, Value>,\n\t\tpermitted: boolean,\n\t) => Promise<Record<string, Value> | null>,\n\tseal: (raw: string) => string = (raw) => codec.seal(raw, binding),\n\ttransform?: (\n\t\tsource: QueryStream<Record<string, Value>>,\n\t\tdb: GenericDatabaseReader<any>,\n\t) => QueryStream<Record<string, Value>>,\n\tauthorizationTable = table,\n) {\n\tconst leaves = leafReader(request, planner)\n\ttry {\n\t\t// SAFETY: runtime ownership/index validation precedes this dynamic table\n\t\t// adapter. Public generics retain the original application's schema.\n\t\tconst base = stream(leaves.db, schema as ExecutionSchema).query(table)\n\t\tconst selected = plan.index\n\t\t\t? base.withIndex(plan.index.name, (q) => {\n\t\t\t\t\t// SAFETY: every equality is a validated consecutive native prefix.\n\t\t\t\t\tlet range = q as IndexRangeBuilder<any, string[], number>\n\t\t\t\t\tfor (const { field, value } of plan.prefix) {\n\t\t\t\t\t\t// SAFETY: native eq returns the next builder; runtime prefix length\n\t\t\t\t\t\t// is validated even though this dynamic adapter erases tuple lengths.\n\t\t\t\t\t\trange = range.eq(field, value) as IndexRangeBuilder<any, string[], number>\n\t\t\t\t\t}\n\t\t\t\t\treturn range\n\t\t\t\t})\n\t\t\t: base\n\t\tconst ordered = selected.order(plan.args.order ?? 'asc')\n\t\tconst source = transform ? transform(ordered, leaves.db) : ordered\n\t\tconst result = plan.unsatisfiable\n\t\t\t? await new EmptyStream<Record<string, Value>>(\n\t\t\t\t\tsource.getOrder(),\n\t\t\t\t\tsource.getIndexFields(),\n\t\t\t\t\tsource.getEqualityIndexFilter(),\n\t\t\t\t).paginate(args)\n\t\t\t: await new EligibleStream(\n\t\t\t\t\tsource,\n\t\t\t\t\trequest,\n\t\t\t\t\tauthorizationTable,\n\t\t\t\t\tplan,\n\t\t\t\t\tplanner,\n\t\t\t\t\t{\n\t\t\t\t\t\tcompleted: [],\n\t\t\t\t\t\tstopped: false,\n\t\t\t\t\t},\n\t\t\t\t\tcomplete,\n\t\t\t\t).paginate(args)\n\t\tconst protectedResult = {\n\t\t\t...result,\n\t\t\tcontinueCursor: seal(result.continueCursor),\n\t\t}\n\t\tif (result.splitCursor != null) protectedResult.splitCursor = seal(result.splitCursor)\n\t\treturn protectedResult\n\t} catch (error) {\n\t\trethrowQueryError(error)\n\t} finally {\n\t\tawait leaves.close()\n\t}\n}\n\n/** Retain the actual metered leaf, since the helper's public key iterator does\n * not forward return(). This private reader only constructs raw metered queries.\n */\nfunction leafReader(request: Request, planner: ReturnType<typeof createPlanner>) {\n\tconst active = new Set<AsyncIterator<GenericDocument>>()\n\tfunction wrap<Source extends AsyncIterable<GenericDocument>>(\n\t\ttable: string,\n\t\tsource: Source,\n\t): Source {\n\t\t// SAFETY: the empty proxy shell is private; supported methods forward to the real native source below.\n\t\treturn new Proxy({} as Source, {\n\t\t\tget(_target, key) {\n\t\t\t\tconst target = source\n\t\t\t\tif (key === 'withIndex' && 'withIndex' in target)\n\t\t\t\t\treturn (...args: Parameters<QueryInitializer<any>['withIndex']>) => {\n\t\t\t\t\t\t// SAFETY: the private facade forwards an existing native index constructor only.\n\t\t\t\t\t\tconst withIndex = target.withIndex as QueryInitializer<any>['withIndex']\n\t\t\t\t\t\treturn wrap(table, withIndex.apply(target, args))\n\t\t\t\t\t}\n\t\t\t\tif (key === 'order' && 'order' in target)\n\t\t\t\t\treturn (...args: Parameters<QueryInitializer<any>['order']>) => {\n\t\t\t\t\t\t// SAFETY: the private facade forwards the existing native order method only.\n\t\t\t\t\t\tconst order = target.order as QueryInitializer<any>['order']\n\t\t\t\t\t\treturn wrap(table, order.apply(target, args))\n\t\t\t\t\t}\n\t\t\t\tif (key === Symbol.asyncIterator)\n\t\t\t\t\treturn () => {\n\t\t\t\t\t\tconst iterator = target[Symbol.asyncIterator]()\n\t\t\t\t\t\tactive.add(iterator)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tnext: () => {\n\t\t\t\t\t\t\t\tplanner.beforeLeaf(table)\n\t\t\t\t\t\t\t\treturn iterator.next()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tthrow new Error('Unsupported private query operation')\n\t\t\t},\n\t\t})\n\t}\n\n\t// SAFETY: stream() uses only query to construct the indexed stream. The\n\t// request's authorized reader handles policy gets separately; never publish this facade.\n\tconst db = {\n\t\tquery: (table: string) => {\n\t\t\tplanner.beforeLeaf(table)\n\t\t\treturn wrap(table, request.leafQuery(table))\n\t\t},\n\t} as GenericDatabaseReader<any>\n\treturn {\n\t\tdb,\n\t\tasync close() {\n\t\t\tlet failed = false\n\t\t\tfor (const iterator of active) {\n\t\t\t\ttry {\n\t\t\t\t\tawait iterator.return?.()\n\t\t\t\t} catch {\n\t\t\t\t\tfailed = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tactive.clear()\n\t\t\tif (failed) throw new Error('Query execution failed')\n\t\t},\n\t}\n}\n\nfunction canFetch(request: Request): boolean {\n\tconst c = request.counters,\n\t\tb = request.budgets\n\treturn (\n\t\tc.readOperations < b.readOperations &&\n\t\tc.rawCandidates < b.rawCandidates &&\n\t\tc.documentBytesObserved + c.documentBytesReserved + MAX_DOCUMENT_BYTES <= b.documentBytes\n\t)\n}\n\n/** Encoder-only use of public native pagination; no private cursor wire format,\n * artificial bandwidth, lookahead, or database read.\n */\nclass CapturedKey extends QueryStream<object> {\n\tconstructor(\n\t\tprivate readonly entry: Entry,\n\t\tprivate readonly source: QueryStream<Record<string, Value>>,\n\t) {\n\t\tsuper()\n\t}\n\tgetOrder() {\n\t\treturn this.source.getOrder()\n\t}\n\tgetIndexFields() {\n\t\treturn this.source.getIndexFields()\n\t}\n\tgetEqualityIndexFilter() {\n\t\treturn this.source.getEqualityIndexFilter()\n\t}\n\tnarrow(bounds: IndexBounds) {\n\t\tif (\n\t\t\tbounds.lowerBound.length ||\n\t\t\tbounds.upperBound.length ||\n\t\t\t!bounds.lowerBoundInclusive ||\n\t\t\t!bounds.upperBoundInclusive\n\t\t)\n\t\t\tthrow new Error('Invalid cursor encoder range')\n\t\treturn this\n\t}\n\tasync *iterWithKeys(): AsyncGenerator<[object, IndexKey, number], undefined> {\n\t\tyield [{}, this.entry[1], this.entry[2]]\n\t}\n}\n\ninterface Progress {\n\tcompleted: Entry[]\n\tstopped: boolean\n}\nclass EligibleStream extends QueryStream<Record<string, Value>> {\n\tconstructor(\n\t\tprivate readonly source: QueryStream<Record<string, Value>>,\n\t\tprivate readonly request: Request,\n\t\tprivate readonly table: string,\n\t\tprivate readonly plan: Plan,\n\t\tprivate readonly planner: ReturnType<typeof createPlanner>,\n\t\tprivate readonly progress: Progress,\n\t\tprivate readonly complete?: (\n\t\t\tdocument: Record<string, Value>,\n\t\t\tpermitted: boolean,\n\t\t) => Promise<Record<string, Value> | null>,\n\t) {\n\t\tsuper()\n\t}\n\tgetOrder() {\n\t\treturn this.source.getOrder()\n\t}\n\tgetIndexFields() {\n\t\treturn this.source.getIndexFields()\n\t}\n\tgetEqualityIndexFilter() {\n\t\treturn this.source.getEqualityIndexFilter()\n\t}\n\tnarrow(bounds: IndexBounds): QueryStream<Record<string, Value>> {\n\t\treturn new EligibleStream(\n\t\t\tthis.source.narrow(bounds),\n\t\t\tthis.request,\n\t\t\tthis.table,\n\t\t\tthis.plan,\n\t\t\tthis.planner,\n\t\t\tthis.progress,\n\t\t\tthis.complete,\n\t\t)\n\t}\n\tasync *iterWithKeys(): AsyncGenerator<Entry, undefined> {\n\t\t// Always request actual root bandwidth, even without a native byte option.\n\t\tconst iterator = this.source.iterWithKeys(true)[Symbol.asyncIterator]()\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tif (!canFetch(this.request)) {\n\t\t\t\t\tif (!this.progress.completed.length) throw new ManagedReadError('MANAGED_READ_BUDGET')\n\t\t\t\t\tthis.progress.stopped = true\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tlet next: IteratorResult<Entry>\n\t\t\t\tconst candidatesBefore = this.request.counters.rawCandidates\n\t\t\t\ttry {\n\t\t\t\t\tnext = await iterator.next()\n\t\t\t\t\tthis.planner.assertUnchanged()\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// A reservation failure BEFORE the next root is safe to stop at\n\t\t\t\t\t// completed progress. Authorization errors below are never partialized.\n\t\t\t\t\tif (\n\t\t\t\t\t\terror instanceof ManagedReadError &&\n\t\t\t\t\t\terror.code === 'MANAGED_READ_BUDGET' &&\n\t\t\t\t\t\tthis.progress.completed.length &&\n\t\t\t\t\t\tthis.request.counters.rawCandidates === candidatesBefore\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.progress.stopped = true\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t\tif (next.done) return\n\t\t\t\tconst [document, key, bytes] = next.value\n\t\t\t\tlet output: Record<string, Value> | null = null\n\t\t\t\tif (document) {\n\t\t\t\t\t// SAFETY: metered native stream documents contain their system IDs.\n\t\t\t\t\tconst permitted = await this.request.authorize(this.table, document as GenericDocument)\n\t\t\t\t\tthis.planner.assertUnchanged()\n\t\t\t\t\t// One live whole-registry guard covers the synchronous residual\n\t\t\t\t\t// and strict projection step AFTER the asynchronous policy finishes.\n\t\t\t\t\t// SAFETY: the registry DTO validates declared public native values only.\n\t\t\t\t\toutput = this.complete\n\t\t\t\t\t\t? await this.complete(document, permitted)\n\t\t\t\t\t\t: (this.planner.complete(this.table, document, permitted, this.plan.residual) as Record<\n\t\t\t\t\t\t\t\tstring,\n\t\t\t\t\t\t\t\tValue\n\t\t\t\t\t\t\t> | null)\n\t\t\t\t}\n\t\t\t\t// Complete policy, residual, and required DTO before recording the key.\n\t\t\t\tconst entry: Entry = [output, key, bytes]\n\t\t\t\tthis.progress.completed.push(entry)\n\t\t\t\tif (!canFetch(this.request)) this.progress.stopped = true\n\t\t\t\tyield entry\n\t\t\t}\n\t\t} finally {\n\t\t\tawait iterator.return?.()\n\t\t}\n\t}\n\toverride async paginate(\n\t\targs: NativePagination,\n\t): Promise<PaginationResult<Record<string, Value>>> {\n\t\tconst result = await super.paginate(args)\n\t\tif (!this.progress.stopped) return result\n\t\tconst completed = this.progress.completed\n\t\tif (!completed.length) throw new ManagedReadError('MANAGED_READ_BUDGET')\n\t\tconst encode = async (entry: Entry) =>\n\t\t\t(await new CapturedKey(entry, this.source).paginate({ numItems: 1, cursor: null }))\n\t\t\t\t.continueCursor\n\t\treturn {\n\t\t\t...result,\n\t\t\tisDone: false,\n\t\t\tpageStatus: 'SplitRequired',\n\t\t\tcontinueCursor: await encode(completed.at(-1)!),\n\t\t\tsplitCursor: await encode(completed[Math.floor((completed.length - 1) / 2)]),\n\t\t}\n\t}\n}\n"],"mappings":"8OAqCA,SAAgBA,EAAkB,EAAuB,CAQxD,MANCC,aAAiBC,GACjBD,aAAiBE,GACjBF,aAAiBG,EAEXH,EAEG,MAAM,wBAAwB,CACzC,CAEA,eAAsBI,EACrB,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EAIC,CAQD,OAAOC,EAAcC,EAAQC,EAASC,EAAOC,EAAMC,EAASC,EAAOC,EAAS,CAL3E,GAAGH,EAAK,KACR,OAAQA,EAAK,KAAK,SAAW,KAAO,KAAOE,EAAM,KAAKF,EAAK,KAAK,OAAQG,CAAO,EAC/E,UACCH,EAAK,KAAK,WAAa,KAAOA,EAAK,KAAK,UAAYE,EAAM,KAAKF,EAAK,KAAK,UAAWG,CAAO,CAEd,EAAGC,CAAQ,CAC3F,CAEA,eAAsBR,EACrB,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EAIA,EAAiC,GAAQM,EAAM,KAAKG,EAAKF,CAAO,EAChE,EAIA,EAAqBJ,EACpB,CACD,IAAMO,EAASC,EAAWT,EAASG,CAAO,EAC1C,GAAI,CAGH,IAAMO,EAAOC,EAAOH,EAAO,GAAIT,CAAyB,CAAC,CAAC,MAAME,CAAK,EAa/DW,GAZWV,EAAK,MACnBQ,EAAK,UAAUR,EAAK,MAAM,KAAO,GAAM,CAEvC,IAAIW,EAAQC,EACZ,IAAK,GAAM,CAAE,QAAO,WAAWZ,EAAK,OAGnC,EAAQW,EAAM,GAAGE,EAAOC,CAAK,EAE9B,OAAOH,CACR,CAAC,EACAH,EAAA,CACsB,MAAMR,EAAK,KAAK,OAAS,KAAK,EACjDe,EAASC,EAAYA,EAAUN,EAASJ,EAAO,EAAE,EAAII,EACrDO,EAASjB,EAAK,cACjB,MAAM,IAAIkB,EACVH,EAAO,SAAS,EAChBA,EAAO,eAAe,EACtBA,EAAO,uBAAuB,CAC/B,CAAC,CAAC,SAASI,CAAI,EACd,MAAM,IAAIC,EACVL,EACAjB,EACAuB,EACArB,EACAC,EACA,CACC,UAAW,CAAC,EACZ,QAAS,EACV,EACAG,CACD,CAAC,CAAC,SAASe,CAAI,EACXG,EAAkB,CACvB,GAAGL,EACH,eAAgBM,EAAKN,EAAO,cAAc,CAC3C,EAEA,OADIA,EAAO,aAAe,OAAM,EAAgB,YAAcM,EAAKN,EAAO,WAAW,GAC9EK,CACR,OAAS/B,EAAO,CACfD,EAAkBC,CAAK,CACxB,QAAU,CACT,MAAMe,EAAO,MAAM,CACpB,CACD,CAKA,SAASC,EAAW,EAAkB,EAA2C,CAChF,IAAMiB,kBAAS,IAAI,IACnB,SAASC,EACR,EACA,EACS,CAET,OAAO,IAAI,MAAM,CAAC,EAAa,CAC9B,IAAI,EAAS,EAAK,CACjB,IAAMC,EAASX,EACf,GAAIY,IAAQ,aAAe,cAAeD,EACzC,OAAQ,GAAGP,IAAyD,CAEnE,IAAMS,EAAYF,EAAO,UACzB,OAAOD,EAAK1B,EAAO6B,EAAU,MAAMF,EAAQP,CAAI,CAAC,CACjD,EACD,GAAIQ,IAAQ,SAAW,UAAWD,EACjC,OAAQ,GAAGP,IAAqD,CAE/D,IAAMU,EAAQH,EAAO,MACrB,OAAOD,EAAK1B,EAAO8B,EAAM,MAAMH,EAAQP,CAAI,CAAC,CAC7C,EACD,GAAIQ,IAAQ,OAAO,cAClB,UAAa,CACZ,IAAMG,EAAWJ,EAAO,OAAO,cAAc,CAAC,EAE9C,OADAF,EAAO,IAAIM,CAAQ,EACZ,CACN,UACC7B,EAAQ,WAAWF,CAAK,EACjB+B,EAAS,KAAK,EAEvB,CACD,EACD,MAAU,MAAM,qCAAqC,CACtD,CACD,CAAC,CACF,CAUA,MAAO,CACN,GAAA,CANA,MAAQ,IACP7B,EAAQ,WAAWF,CAAK,EACjB0B,EAAK1B,EAAOD,EAAQ,UAAUC,CAAK,CAAC,EAI3C,EACD,MAAM,OAAQ,CACb,IAAIgC,EAAS,GACb,IAAK,IAAMD,KAAYN,EACtB,GAAI,CACH,MAAMM,EAAS,SAAS,CACzB,MAAQ,CACP,EAAS,EACV,CAGD,GADAN,EAAO,MAAM,EACTO,EAAQ,MAAU,MAAM,wBAAwB,CACrD,CACD,CACD,CAEA,SAASC,EAAS,EAA2B,CAC5C,IAAMC,EAAInC,EAAQ,SACjBoC,EAAIpC,EAAQ,QACb,OACCmC,EAAE,eAAiBC,EAAE,gBACrBD,EAAE,cAAgBC,EAAE,eACpBD,EAAE,sBAAwBA,EAAE,sBAAA,SAA8CC,EAAE,aAE9E,CAKA,IAAMC,EAAN,cAA0BC,CAAoB,CAE3B,MACA,OAFlB,YACC,EACA,EACC,CACD,MAAM,EAHW,KAAA,MAAAC,EACA,KAAA,OAAAtB,CAGlB,CACA,UAAW,CACV,OAAO,KAAK,OAAO,SAAS,CAC7B,CACA,gBAAiB,CAChB,OAAO,KAAK,OAAO,eAAe,CACnC,CACA,wBAAyB,CACxB,OAAO,KAAK,OAAO,uBAAuB,CAC3C,CACA,OAAO,EAAqB,CAC3B,GACCuB,EAAO,WAAW,QAClBA,EAAO,WAAW,QAClB,CAACA,EAAO,qBACR,CAACA,EAAO,oBAER,MAAU,MAAM,8BAA8B,EAC/C,OAAO,IACR,CACA,MAAO,cAAsE,CAC5E,KAAM,CAAC,CAAC,EAAG,KAAK,MAAM,GAAI,KAAK,MAAM,EAAE,CACxC,CACD,EAMMlB,EAAN,MAAMA,UAAuBgB,CAAmC,CAE7C,OACA,QACA,MACA,KACA,QACA,SACA,SAPlB,YACC,EACA,EACA,EACA,EACA,EACA,EACA,EAIC,CACD,MAAM,EAXW,KAAA,OAAArB,EACA,KAAA,QAAAjB,EACA,KAAA,MAAAC,EACA,KAAA,KAAAC,EACA,KAAA,QAAAC,EACA,KAAA,SAAAsC,EACA,KAAA,SAAAnC,CAMlB,CACA,UAAW,CACV,OAAO,KAAK,OAAO,SAAS,CAC7B,CACA,gBAAiB,CAChB,OAAO,KAAK,OAAO,eAAe,CACnC,CACA,wBAAyB,CACxB,OAAO,KAAK,OAAO,uBAAuB,CAC3C,CACA,OAAO,EAAyD,CAC/D,OAAO,IAAIgB,EACV,KAAK,OAAO,OAAOkB,CAAM,EACzB,KAAK,QACL,KAAK,MACL,KAAK,KACL,KAAK,QACL,KAAK,SACL,KAAK,QACN,CACD,CACA,MAAO,cAAiD,CAEvD,IAAMR,EAAW,KAAK,OAAO,aAAa,EAAI,CAAC,CAAC,OAAO,cAAc,CAAC,EACtE,GAAI,CACH,OAAa,CACZ,GAAI,CAACE,EAAS,KAAK,OAAO,EAAG,CAC5B,GAAI,CAAC,KAAK,SAAS,UAAU,OAAQ,MAAM,IAAIxC,EAAiB,qBAAqB,EACrF,KAAK,SAAS,QAAU,GACxB,MACD,CACA,IAAIgD,EACEC,EAAmB,KAAK,QAAQ,SAAS,cAC/C,GAAI,CACH,EAAO,MAAMX,EAAS,KAAK,EAC3B,KAAK,QAAQ,gBAAgB,CAC9B,OAASvC,EAAO,CAGf,GACCA,aAAiBC,GACjBD,EAAM,OAAS,uBACf,KAAK,SAAS,UAAU,QACxB,KAAK,QAAQ,SAAS,gBAAkBkD,EACvC,CACD,KAAK,SAAS,QAAU,GACxB,MACD,CACA,MAAMlD,CACP,CACA,GAAIiD,EAAK,KAAM,OACf,GAAM,CAACE,EAAUf,EAAKgB,GAASH,EAAK,MAChCI,EAAuC,KAC3C,GAAIF,EAAU,CAEb,IAAMG,EAAY,MAAM,KAAK,QAAQ,UAAU,KAAK,MAAOH,CAA2B,EACtF,KAAK,QAAQ,gBAAgB,EAI7B,EAAS,KAAK,SACX,MAAM,KAAK,SAASA,EAAUG,CAAS,EACtC,KAAK,QAAQ,SAAS,KAAK,MAAOH,EAAUG,EAAW,KAAK,KAAK,QAAQ,CAI9E,CAEA,IAAMR,EAAe,CAACO,EAAQjB,EAAKgB,CAAK,EACxC,KAAK,SAAS,UAAU,KAAKN,CAAK,EAC7BL,EAAS,KAAK,OAAO,IAAG,KAAK,SAAS,QAAU,IACrD,MAAMK,CACP,CACD,QAAU,CACT,MAAMP,EAAS,SAAS,CACzB,CACD,CACA,MAAe,SACd,EACmD,CACnD,IAAMb,EAAS,MAAM,MAAM,SAASE,CAAI,EACxC,GAAI,CAAC,KAAK,SAAS,QAAS,OAAOF,EACnC,IAAM6B,EAAY,KAAK,SAAS,UAChC,GAAI,CAACA,EAAU,OAAQ,MAAM,IAAItD,EAAiB,qBAAqB,EACvE,IAAMuD,EAAS,KAAO,KACpB,MAAM,IAAIZ,EAAYE,EAAO,KAAK,MAAM,CAAC,CAAC,SAAS,CAAE,SAAU,EAAG,OAAQ,IAAK,CAAC,EAAA,CAC/E,eACH,MAAO,CACN,GAAGpB,EACH,OAAQ,GACR,WAAY,gBACZ,eAAgB,MAAM8B,EAAOD,EAAU,GAAG,EAAE,CAAE,EAC9C,YAAa,MAAMC,EAAOD,EAAU,KAAK,OAAOA,EAAU,OAAS,GAAK,CAAC,EAAE,CAC5E,CACD,CACD"}
|
|
1
|
+
{"version":3,"file":"execution.mjs","names":["rethrowQueryError","error","ManagedReadError","CursorError","RelationshipError","executeQuery","executeStream","schema","request","table","plan","planner","codec","binding","complete","raw","leaves","leafReader","base","stream","ordered","range","q","field","value","source","transform","result","EmptyStream","args","EligibleStream","authorizationTable","protectedResult","seal","active","wrap","target","key","withIndex","order","iterator","failed","canFetch","c","b","CapturedKey","QueryStream","entry","bounds","progress","next","candidatesBefore","document","bytes","output","permitted","completed","encode"],"sources":["../../src/querying/execution.ts"],"sourcesContent":["import {\n\tEmptyStream,\n\tQueryStream,\n\tstream,\n\ttype IndexBounds,\n\ttype IndexKey,\n} from 'convex-helpers/server/stream'\nimport type {\n\tGenericDatabaseReader,\n\tGenericDataModel,\n\tGenericDocument,\n\tQueryInitializer,\n\tSchemaDefinition,\n\tGenericSchema,\n\tPaginationResult,\n\tTableDefinition,\n\tIndexRangeBuilder,\n} from 'convex/server'\nimport type { Value } from 'convex/values'\nimport type { createPlanner } from './planner'\nimport { MAX_DOCUMENT_BYTES, ManagedReadError, type ManagedReadRequest } from './security'\nimport { CursorError, type CursorBinding, type createCursorCodec } from './cursors'\nimport type { NativePagination } from './filters'\nimport { RelationshipError } from './relationships'\n\ntype Entry = [Record<string, Value> | null, IndexKey, number]\ntype Plan = ReturnType<ReturnType<typeof createPlanner>['plan']>\ntype Codec = ReturnType<typeof createCursorCodec>\nexport type ExecutionRequest = ManagedReadRequest<GenericDataModel>\ntype Request = Omit<ExecutionRequest, 'db'>\ntype ExecutionSchema = SchemaDefinition<\n\tRecord<string, TableDefinition<any, Record<string, string[]>>>,\n\tboolean\n>\n\n/** Private shared boundary: only the established fixed typed errors are public. */\n// oxlint-disable-next-line anti-slop/no-unknown-parameters -- Thrown values enter this error boundary untrusted; the class checks below admit only the fixed public error contracts.\nexport function rethrowQueryError(error: unknown): never {\n\tif (\n\t\terror instanceof ManagedReadError ||\n\t\terror instanceof CursorError ||\n\t\terror instanceof RelationshipError\n\t)\n\t\tthrow error\n\t// Native/policy errors may contain traversal values or structured private data.\n\tthrow new Error('Query execution failed')\n}\n\nexport async function executeQuery(\n\tschema: SchemaDefinition<GenericSchema, boolean>,\n\trequest: Request,\n\ttable: string,\n\tplan: Plan,\n\tplanner: ReturnType<typeof createPlanner>,\n\tcodec: Codec,\n\tbinding: CursorBinding,\n\tcomplete?: (\n\t\tdocument: Record<string, Value>,\n\t\tpermitted: boolean,\n\t) => Promise<Record<string, Value> | null>,\n) {\n\t// Authenticate BOTH navigation slots before constructing or consuming native queries.\n\tconst args = {\n\t\t...plan.args,\n\t\tcursor: plan.args.cursor === null ? null : codec.open(plan.args.cursor, binding),\n\t\tendCursor:\n\t\t\tplan.args.endCursor == null ? plan.args.endCursor : codec.open(plan.args.endCursor, binding),\n\t}\n\treturn executeStream(schema, request, table, plan, planner, codec, binding, args, complete)\n}\n\nexport async function executeStream(\n\tschema: SchemaDefinition<GenericSchema, boolean>,\n\trequest: Request,\n\ttable: string,\n\tplan: Plan,\n\tplanner: ReturnType<typeof createPlanner>,\n\tcodec: Codec,\n\tbinding: CursorBinding,\n\targs: NativePagination,\n\tcomplete?: (\n\t\tdocument: Record<string, Value>,\n\t\tpermitted: boolean,\n\t) => Promise<Record<string, Value> | null>,\n\tseal: (raw: string) => string = (raw) => codec.seal(raw, binding),\n\ttransform?: (\n\t\tsource: QueryStream<Record<string, Value>>,\n\t\tdb: GenericDatabaseReader<any>,\n\t) => QueryStream<Record<string, Value>>,\n\tauthorizationTable = table,\n) {\n\tconst leaves = leafReader(request, planner)\n\ttry {\n\t\t// SAFETY: runtime ownership/index validation precedes this dynamic table\n\t\t// adapter. Public generics retain the original application's schema.\n\t\tconst base = stream(leaves.db, schema as ExecutionSchema).query(table)\n\t\tconst selected = plan.index\n\t\t\t? base.withIndex(plan.index.name, (q) => {\n\t\t\t\t\t// SAFETY: every equality is a validated consecutive native prefix.\n\t\t\t\t\tlet range = q as IndexRangeBuilder<any, string[], number>\n\t\t\t\t\tfor (const { field, value } of plan.prefix) {\n\t\t\t\t\t\t// SAFETY: native eq returns the next builder; runtime prefix length\n\t\t\t\t\t\t// is validated even though this dynamic adapter erases tuple lengths.\n\t\t\t\t\t\trange = range.eq(field, value) as IndexRangeBuilder<any, string[], number>\n\t\t\t\t\t}\n\t\t\t\t\treturn range\n\t\t\t\t})\n\t\t\t: base\n\t\tconst ordered = selected.order(plan.args.order ?? 'asc')\n\t\tconst source = transform ? transform(ordered, leaves.db) : ordered\n\t\tconst result = plan.unsatisfiable\n\t\t\t? await new EmptyStream<Record<string, Value>>(\n\t\t\t\t\tsource.getOrder(),\n\t\t\t\t\tsource.getIndexFields(),\n\t\t\t\t\tsource.getEqualityIndexFilter(),\n\t\t\t\t).paginate(args)\n\t\t\t: await new EligibleStream(\n\t\t\t\t\tsource,\n\t\t\t\t\trequest,\n\t\t\t\t\tauthorizationTable,\n\t\t\t\t\tplan,\n\t\t\t\t\tplanner,\n\t\t\t\t\t{\n\t\t\t\t\t\tcompleted: [],\n\t\t\t\t\t\tstopped: false,\n\t\t\t\t\t},\n\t\t\t\t\tcomplete,\n\t\t\t\t).paginate(args)\n\t\tconst protectedResult = {\n\t\t\t...result,\n\t\t\tcontinueCursor: seal(result.continueCursor),\n\t\t}\n\t\tif (result.splitCursor != null) protectedResult.splitCursor = seal(result.splitCursor)\n\t\treturn protectedResult\n\t} catch (error) {\n\t\trethrowQueryError(error)\n\t} finally {\n\t\tawait leaves.close()\n\t}\n}\n\n/** Retain the actual metered leaf, since the helper's public key iterator does\n * not forward return(). This private reader only constructs raw metered queries.\n */\nfunction leafReader(request: Request, planner: ReturnType<typeof createPlanner>) {\n\tconst active = new Set<AsyncIterator<GenericDocument>>()\n\tfunction wrap<Source extends AsyncIterable<GenericDocument>>(\n\t\ttable: string,\n\t\tsource: Source,\n\t): Source {\n\t\t// SAFETY: the empty proxy shell is private; supported methods forward to the real native source below.\n\t\treturn new Proxy({} as Source, {\n\t\t\tget(_target, key) {\n\t\t\t\tconst target = source\n\t\t\t\tif (key === 'withIndex' && 'withIndex' in target)\n\t\t\t\t\treturn (...args: Parameters<QueryInitializer<any>['withIndex']>) => {\n\t\t\t\t\t\t// SAFETY: the private facade forwards an existing native index constructor only.\n\t\t\t\t\t\tconst withIndex = target.withIndex as QueryInitializer<any>['withIndex']\n\t\t\t\t\t\treturn wrap(table, withIndex.apply(target, args))\n\t\t\t\t\t}\n\t\t\t\tif (key === 'order' && 'order' in target)\n\t\t\t\t\treturn (...args: Parameters<QueryInitializer<any>['order']>) => {\n\t\t\t\t\t\t// SAFETY: the private facade forwards the existing native order method only.\n\t\t\t\t\t\tconst order = target.order as QueryInitializer<any>['order']\n\t\t\t\t\t\treturn wrap(table, order.apply(target, args))\n\t\t\t\t\t}\n\t\t\t\tif (key === Symbol.asyncIterator)\n\t\t\t\t\treturn () => {\n\t\t\t\t\t\tconst iterator = target[Symbol.asyncIterator]()\n\t\t\t\t\t\tactive.add(iterator)\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tnext: () => {\n\t\t\t\t\t\t\t\tplanner.beforeLeaf(table)\n\t\t\t\t\t\t\t\treturn iterator.next()\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\tthrow new Error('Unsupported private query operation')\n\t\t\t},\n\t\t})\n\t}\n\n\t// SAFETY: stream() uses only query to construct the indexed stream. The\n\t// request's authorized reader handles policy gets separately; never publish this facade.\n\tconst db = {\n\t\tquery: (table: string) => {\n\t\t\tplanner.beforeLeaf(table)\n\t\t\treturn wrap(table, request.leafQuery(table))\n\t\t},\n\t} as GenericDatabaseReader<any>\n\treturn {\n\t\tdb,\n\t\tasync close() {\n\t\t\tlet failed = false\n\t\t\tfor (const iterator of active) {\n\t\t\t\ttry {\n\t\t\t\t\tawait iterator.return?.()\n\t\t\t\t} catch {\n\t\t\t\t\tfailed = true\n\t\t\t\t}\n\t\t\t}\n\t\t\tactive.clear()\n\t\t\tif (failed) throw new Error('Query execution failed')\n\t\t},\n\t}\n}\n\nfunction canFetch(request: Request): boolean {\n\tconst c = request.counters,\n\t\tb = request.budgets\n\treturn (\n\t\tc.readOperations < b.readOperations &&\n\t\tc.rawCandidates < b.rawCandidates &&\n\t\tc.documentBytesObserved + c.documentBytesReserved + MAX_DOCUMENT_BYTES <= b.documentBytes\n\t)\n}\n\n/** Encoder-only use of public native pagination; no private cursor wire format,\n * artificial bandwidth, lookahead, or database read.\n */\nclass CapturedKey extends QueryStream<object> {\n\tconstructor(\n\t\tprivate readonly entry: Entry,\n\t\tprivate readonly source: QueryStream<Record<string, Value>>,\n\t) {\n\t\tsuper()\n\t}\n\tgetOrder() {\n\t\treturn this.source.getOrder()\n\t}\n\tgetIndexFields() {\n\t\treturn this.source.getIndexFields()\n\t}\n\tgetEqualityIndexFilter() {\n\t\treturn this.source.getEqualityIndexFilter()\n\t}\n\tnarrow(bounds: IndexBounds) {\n\t\tif (\n\t\t\tbounds.lowerBound.length ||\n\t\t\tbounds.upperBound.length ||\n\t\t\t!bounds.lowerBoundInclusive ||\n\t\t\t!bounds.upperBoundInclusive\n\t\t)\n\t\t\tthrow new Error('Invalid cursor encoder range')\n\t\treturn this\n\t}\n\tasync *iterWithKeys(): AsyncGenerator<[object, IndexKey, number], undefined> {\n\t\tyield [{}, this.entry[1], this.entry[2]]\n\t}\n}\n\ninterface Progress {\n\tcompleted: Entry[]\n\tstopped: boolean\n}\nclass EligibleStream extends QueryStream<Record<string, Value>> {\n\tconstructor(\n\t\tprivate readonly source: QueryStream<Record<string, Value>>,\n\t\tprivate readonly request: Request,\n\t\tprivate readonly table: string,\n\t\tprivate readonly plan: Plan,\n\t\tprivate readonly planner: ReturnType<typeof createPlanner>,\n\t\tprivate readonly progress: Progress,\n\t\tprivate readonly complete?: (\n\t\t\tdocument: Record<string, Value>,\n\t\t\tpermitted: boolean,\n\t\t) => Promise<Record<string, Value> | null>,\n\t) {\n\t\tsuper()\n\t}\n\tgetOrder() {\n\t\treturn this.source.getOrder()\n\t}\n\tgetIndexFields() {\n\t\treturn this.source.getIndexFields()\n\t}\n\tgetEqualityIndexFilter() {\n\t\treturn this.source.getEqualityIndexFilter()\n\t}\n\tnarrow(bounds: IndexBounds): QueryStream<Record<string, Value>> {\n\t\treturn new EligibleStream(\n\t\t\tthis.source.narrow(bounds),\n\t\t\tthis.request,\n\t\t\tthis.table,\n\t\t\tthis.plan,\n\t\t\tthis.planner,\n\t\t\tthis.progress,\n\t\t\tthis.complete,\n\t\t)\n\t}\n\tasync *iterWithKeys(): AsyncGenerator<Entry, undefined> {\n\t\t// Always request actual root bandwidth, even without a native byte option.\n\t\tconst iterator = this.source.iterWithKeys(true)[Symbol.asyncIterator]()\n\t\ttry {\n\t\t\twhile (true) {\n\t\t\t\tif (!canFetch(this.request)) {\n\t\t\t\t\tif (!this.progress.completed.length) throw new ManagedReadError('MANAGED_READ_BUDGET')\n\t\t\t\t\tthis.progress.stopped = true\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tlet next: IteratorResult<Entry>\n\t\t\t\tconst candidatesBefore = this.request.counters.rawCandidates\n\t\t\t\ttry {\n\t\t\t\t\tnext = await iterator.next()\n\t\t\t\t} catch (error) {\n\t\t\t\t\t// A reservation failure BEFORE the next root is safe to stop at\n\t\t\t\t\t// completed progress. Authorization errors below are never partialized.\n\t\t\t\t\tif (\n\t\t\t\t\t\terror instanceof ManagedReadError &&\n\t\t\t\t\t\terror.code === 'MANAGED_READ_BUDGET' &&\n\t\t\t\t\t\tthis.progress.completed.length &&\n\t\t\t\t\t\tthis.request.counters.rawCandidates === candidatesBefore\n\t\t\t\t\t) {\n\t\t\t\t\t\tthis.progress.stopped = true\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t\tif (next.done) return\n\t\t\t\tconst [document, key, bytes] = next.value\n\t\t\t\tlet output: Record<string, Value> | null = null\n\t\t\t\tif (document) {\n\t\t\t\t\t// SAFETY: metered native stream documents contain their system IDs.\n\t\t\t\t\tconst permitted = await this.request.authorize(this.table, document as GenericDocument)\n\t\t\t\t\t// SAFETY: the registry DTO validates declared public native values only.\n\t\t\t\t\toutput = this.complete\n\t\t\t\t\t\t? await this.complete(document, permitted)\n\t\t\t\t\t\t: (this.planner.complete(this.table, document, permitted, this.plan.residual) as Record<\n\t\t\t\t\t\t\t\tstring,\n\t\t\t\t\t\t\t\tValue\n\t\t\t\t\t\t\t> | null)\n\t\t\t\t}\n\t\t\t\t// Complete policy, residual, and required DTO before recording the key.\n\t\t\t\tconst entry: Entry = [output, key, bytes]\n\t\t\t\tthis.progress.completed.push(entry)\n\t\t\t\tif (!canFetch(this.request)) this.progress.stopped = true\n\t\t\t\tyield entry\n\t\t\t}\n\t\t} finally {\n\t\t\tawait iterator.return?.()\n\t\t}\n\t}\n\toverride async paginate(\n\t\targs: NativePagination,\n\t): Promise<PaginationResult<Record<string, Value>>> {\n\t\tconst result = await super.paginate(args)\n\t\tif (!this.progress.stopped) return result\n\t\tconst completed = this.progress.completed\n\t\tif (!completed.length) throw new ManagedReadError('MANAGED_READ_BUDGET')\n\t\tconst encode = async (entry: Entry) =>\n\t\t\t(await new CapturedKey(entry, this.source).paginate({ numItems: 1, cursor: null }))\n\t\t\t\t.continueCursor\n\t\treturn {\n\t\t\t...result,\n\t\t\tisDone: false,\n\t\t\tpageStatus: 'SplitRequired',\n\t\t\tcontinueCursor: await encode(completed.at(-1)!),\n\t\t\tsplitCursor: await encode(completed[Math.floor((completed.length - 1) / 2)]),\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -141,5 +141,10 @@ export type ServerFilter<Fields extends FieldSchemas> = ScalarFilters<Fields, Sc
|
|
|
141
141
|
* OR remains a residual expression over one traversal, never multiple streams.
|
|
142
142
|
*/
|
|
143
143
|
export declare function compileFilters<Schema extends z.ZodType>(schema: Schema, input: z.input<Schema>, indexes: readonly QueryIndex[], conditions?: readonly IndexCondition[], where?: FilterSpec<Schema>): CompiledFilters;
|
|
144
|
+
/** Internal planner boundary: retain the parsed arguments without parsing them again. */
|
|
145
|
+
export declare function parseFilterPlan<Schema extends z.ZodType>(schema: Schema, input: z.input<Schema>, indexes: readonly QueryIndex[], conditions?: readonly IndexCondition[], where?: FilterSpec<Schema>): {
|
|
146
|
+
args: z.infer<Schema>;
|
|
147
|
+
compiled: CompiledFilters;
|
|
148
|
+
};
|
|
144
149
|
//#endregion
|
|
145
150
|
//# sourceMappingURL=filters.d.mts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{convexToZod as e}from"convex-helpers/server/zod4";import{paginationOptsValidator as t}from"convex/server";import{z as n}from"zod";import{compareValues as r}from"convex/values";const i=100,a=4,o=256,s=100,c=e(t),l=c.strict().extend({numItems:c.shape.numItems.int().positive().max(100),maximumRowsRead:c.shape.maximumRowsRead.unwrap().int().positive().optional(),maximumBytesRead:c.shape.maximumBytesRead.unwrap().int().positive().optional()});function u(e,t){let r={};for(let[n,i]of Object.entries(t)){if(i!==!0||!Object.hasOwn(e,n))throw Error(`Invalid scalar filter selection "${n}"`);r[n]=d(e[n]).optional()}let i=
|
|
1
|
+
import{convexToZod as e}from"convex-helpers/server/zod4";import{paginationOptsValidator as t}from"convex/server";import{z as n}from"zod";import{compareValues as r}from"convex/values";const i=100,a=4,o=256,s=100,c=e(t),l=c.strict().extend({numItems:c.shape.numItems.int().positive().max(100),maximumRowsRead:c.shape.maximumRowsRead.unwrap().int().positive().optional(),maximumBytesRead:c.shape.maximumBytesRead.unwrap().int().positive().optional()});function u(e,t){let r={};for(let[n,i]of Object.entries(t)){if(i!==!0||!Object.hasOwn(e,n))throw Error(`Invalid scalar filter selection "${n}"`);r[n]=d(e[n]).optional()}let i=D(r);return l.extend({filters:i.optional(),order:n.enum([`asc`,`desc`]).optional()})}function d(t){T(t);let r=e(t).nonoptional(),i=n.object({eq:r.optional(),in:n.array(r).min(1).max(100).optional()}),a=E(t)?i.extend({gt:r.optional(),gte:r.optional(),lt:r.optional(),lte:r.optional()}):i,o=(t.isOptional===`optional`?a.extend({exists:n.boolean().optional()}):a).strict().refine(e=>Object.values(e).some(e=>e!==void 0),`At least one operator is required`);return n.union([r,o])}const f=/* @__PURE__ */ new WeakMap,p=/* @__PURE__ */ new WeakMap,m=/* @__PURE__ */ new WeakMap,h=n.core.$constructor(`BoundedFilterObject`,(e,t)=>{n.ZodObject.init(e,t);let r=e._zod.parse,i=t.shape.fields,a=i instanceof n.ZodOptional?i.unwrap():void 0,o=a instanceof n.ZodObject?Object.keys(a.shape):Object.keys(t.shape),s={variants:p.get(t.shape.variant),fields:Object.fromEntries(o.map(e=>[e,t.shape[e]])),relations:Object.fromEntries(Object.entries(t.shape).flatMap(([e,t])=>{let n=f.get(t);return n?[[e,n]]:[]}))};e._zod.parse=(e,t)=>k(e.value,s)?r(e,t):(e.issues.push({code:`custom`,input:e.value,message:`Unsupported filter layout or depth/node budget exceeded`}),e)});function g(e,t=/* @__PURE__ */ new WeakMap){let n=new h(O(e,0,t)._zod.def);return m.set(n,e),n}function _(e,t,n,r=[],i){return v(e,t,n,r,i).compiled}function v(e,t,n,r=[],i){let a=e.parse(t),o=a,s=i===void 0?void 0:e.parse({...o,filters:i}),c=y(e),l={kind:`and`,children:[b(o.filters,c),b(s?.filters,c)]},u=C(l),d=[...n].sort((e,t)=>e.name.localeCompare(t.name)||e.fields.join(`\0`).localeCompare(t.fields.join(`\0`))),f=r.find(e=>e.value!==null&&e.value!==void 0),p=null;if(f){if(p=d.find(e=>e.name===f.index)??null,!p||w(p,u.values).length===0)throw Error(`Explicit index "${f.index}" is incompatible with guaranteed filters`)}else{let e=0;for(let t of d){let n=w(t,u.values).length;n>e&&(p=t,e=n)}}return{args:a,compiled:{residual:l,bindings:u.values,unsatisfiable:u.impossible,index:p,prefix:p?w(p,u.values):[]}}}function y(e){if(!(e instanceof n.ZodObject))return;let t=e.shape.filters;if(!(t instanceof n.ZodOptional))return;let r=t.unwrap(),i=m.get(r);if(i)return i;if(r instanceof n.ZodObject)return{fields:r.shape,variants:p.get(r.shape.variant),relations:Object.fromEntries(Object.entries(r.shape).flatMap(([e,t])=>{let n=f.get(t);return n?[[e,n]]:[]}))}}function b(e,t){let n=[];if(!e)return{kind:`and`,children:n};let r=e;for(let[e,i]of Object.entries(r))if(i!==void 0){if(e===`and`||e===`or`)n.push({kind:e,children:i.map(e=>b(e,t))});else if(e===`variant`&&t?.variants){let e=i;n.push({kind:`variant`,discriminator:t.variants.discriminator,tag:e.tag,child:b(e.filters,t.variants.options[e.tag])})}else if(e===`not`)n.push({kind:`not`,child:b(i,t)});else if(e===`fields`)for(let[e,t]of Object.entries(i))n.push(...x(e,t));else if(t?.relations[e]){let r=t.relations[e];if(r.cardinality===`one`)n.push({kind:`relation`,name:e,mode:`one`,predicate:i===null?null:b(i,r.target)});else{let t=i;n.push({kind:`relation`,name:e,mode:t.some===void 0?`none`:`some`,predicate:b(t.some??t.none,r.target)})}}else n.push(...x(e,i))}return{kind:`and`,children:n}}function x(e,t){return t===void 0?[]:Object(t)===t?Object.entries(t).filter(([,e])=>e!==void 0).map(([t,n])=>({kind:`scalar`,field:e,operator:t,value:n})):[{kind:`scalar`,field:e,operator:`eq`,value:t}]}function S(e,t){return r(e,t)===0}function C(e){let t=/* @__PURE__ */ new Map;if(e.kind===`variant`)return C({kind:`and`,children:[{kind:`scalar`,field:e.discriminator,operator:`eq`,value:e.tag},e.child]});if(e.kind===`scalar`){e.operator===`eq`&&!Array.isArray(e.value)&&t.set(e.field,e.value);let n=Array.isArray(e.value)?e.value:void 0;return e.operator===`in`&&n&&n.every(e=>S(e,n[0]))&&t.set(e.field,n[0]),{values:t,impossible:!1}}if(e.kind===`not`||e.kind===`relation`)return{values:t,impossible:!1};let n=e.children.map(C);if(e.kind===`or`){let e=n.filter(e=>!e.impossible);for(let[n,r]of e[0]?.values??[])e.every(e=>e.values.has(n)&&S(e.values.get(n),r))&&t.set(n,r);return{values:t,impossible:e.every(e=>e.impossible)}}let r=n.some(e=>e.impossible);for(let e of n)for(let[n,i]of e.values)t.has(n)&&!S(t.get(n),i)&&(r=!0),t.set(n,i);return{values:t,impossible:r}}function w(e,t){let n=[];for(let r of e.fields){if(!t.has(r))break;n.push({field:r,value:t.get(r)})}return n}function T(e){switch(e.kind){case`id`:case`string`:case`float64`:case`int64`:case`boolean`:case`null`:case`literal`:return;case`union`:for(let t of e.members)T(t);return;default:throw Error(`Unsupported scalar filter kind "${e.kind}"`)}}function E(e){switch(e.kind){case`string`:case`float64`:case`int64`:return!0;case`literal`:return n.union([n.string(),n.number(),n.bigint()]).safeParse(e.value).success;case`union`:return e.members.every(E);default:return!1}}function D(e){return g({fields:e,relations:{}})}function O(e,t,r=/* @__PURE__ */ new WeakMap){let i=r.get(e)?.get(t);if(i)return i;let a=e.fields,o=Object.fromEntries(Object.entries(a).filter(([t])=>![`and`,`or`,`not`,`fields`,...e.variants?[`variant`]:[]].includes(t))),s={};for(let[i,a]of Object.entries(e.relations)){if(t===4)continue;let e=O(a.target,t+1,r);s[i]=(a.cardinality===`one`?e.nullable():n.union([n.object({some:e}).strict(),n.object({none:e}).strict()])).optional(),f.set(s[i],a)}if(e.variants&&t<4){let i=Object.entries(e.variants.options).map(([e,i])=>n.strictObject({tag:n.literal(e),filters:O(i,t+1,r)}));i.length&&(s.variant=n.union(i).optional(),p.set(s.variant,e.variants))}let c=n.object({...o,...s,fields:n.object(a).strict().optional()}).strict(),l=t===4?void 0:O(e,t+1,r),u=l?c.extend({and:n.array(l).min(1).max(256).optional(),or:n.array(l).min(1).max(256).optional(),not:l.optional()}):c,d=r.get(e);return d||(d=/* @__PURE__ */ new Map,r.set(e,d)),d.set(t,u),u}function k(e,t){let r=[{value:e,depth:0,context:`expression`,grammar:t}],i=0;for(;r.length;){let e=r.pop();if(++i>256||e.depth>4)return!1;if(Object(e.value)!==e.value)continue;let t=e.value,a=Array.isArray(t)?t:void 0,o=Object.getPrototypeOf(t);if((a?o!==Array.prototype:o!==Object.prototype&&o!==null)||a&&a.length+i+r.length>256)return!1;let s=Reflect.ownKeys(t);if(s.length-+!!a+i+r.length>256||a&&s.length!==a.length+1)return!1;let c;switch(e.context){case`expression`:c=[...Object.keys(e.grammar.fields),...Object.keys(e.grammar.relations),`and`,`or`,`not`,`fields`,...e.grammar.variants?[`variant`]:[]];break;case`variant`:c=[`tag`,`filters`];break;case`fields`:c=Object.keys(e.grammar.fields);break;case`predicate`:c=[`eq`,`in`,`gt`,`gte`,`lt`,`lte`,`exists`];break;case`many`:c=[`some`,`none`];break;default:c=[]}for(let e of c)if(!Object.hasOwn(t,e)&&e in t)return!1;for(let i of s){if(a&&i===`length`)continue;let o=Object.getOwnPropertyDescriptor(t,i),s=String(i);if(!o.enumerable||!(`value`in o)||s!==i||a&&(!/^(0|[1-9]\d*)$/.test(s)||Number(s)>=a.length))return!1;let c=`value`,l=e.depth,u=e.grammar;if(e.context===`expression`){if(c=`predicate`,s===`variant`&&e.grammar.variants)c=`variant`,l++;else if(s===`fields`)c=`fields`;else if(s===`and`||s===`or`)c=`composition`,l++;else if(s===`not`)c=`expression`,l++;else if(e.grammar.relations[s]){let t=e.grammar.relations[s];c=t.cardinality===`one`?`expression`:`many`,u=t.target,l++}}else if(e.context===`variant`&&s===`filters`){let r=Object.getOwnPropertyDescriptor(t,`tag`);if(!r||!(`value`in r)||!n.string().safeParse(r.value).success||!Object.hasOwn(e.grammar.variants?.options??{},r.value))return!1;c=`expression`,u=e.grammar.variants.options[String(r.value)]}else e.context===`fields`?c=`predicate`:(e.context===`composition`||e.context===`many`)&&(c=`expression`);r.push({value:o.value,depth:l,context:c,grammar:u})}}return!0}export{a as MAX_FILTER_DEPTH,s as MAX_FILTER_IN_VALUES,o as MAX_FILTER_NODES,i as MAX_PAGE_SIZE,_ as compileFilters,l as nativePagination,v as parseFilterPlan,g as relationFilterSchema,u as scalarPaginationSchema,d as scalarPredicateSchema};
|
|
2
2
|
//# sourceMappingURL=filters.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filters.mjs","names":["MAX_PAGE_SIZE","MAX_FILTER_DEPTH","MAX_FILTER_NODES","MAX_FILTER_IN_VALUES","nativeOptions","convexToZod","paginationOptsValidator","nativePagination","scalarPaginationSchema","predicates","key","selected","selection","validators","scalarPredicateSchema","filters","boundedFilterSchema","z","assertSupportedScalar","validator","scalar","equality","ranged","supportsRange","explicit","value","entry","relationPredicates","variantPredicates","filterGrammars","BoundedFilterObject","inst","def","parse","namespace","namespaceObject","selectedKeys","grammar","schema","relation","withinFilterBudget","payload","ctx","relationFilterSchema","filterNodeSchema","cache","compileFilters","parsed","input","server","where","paginationGrammar","residual","normalizeFilters","guaranteed","guaranteedBindings","sorted","indexes","a","b","condition","conditions","index","prefixBindings","length","candidate","usable","root","cached","name","predicate","children","expression","child","scoped","field","normalizeScalar","target","operator","valueEqual","compareValues","left","right","values","ast","choices","branches","viable","branch","impossible","prefix","bindings","member","depth","direct","relations","options","tag","node","result","depths","pending","nodes","object","array","prototype","keys","consumed","descriptor","context"],"sources":["../../src/querying/filters.ts"],"sourcesContent":["import { convexToZod, type ConvexValidatorFromZod } from 'convex-helpers/server/zod4'\nimport { paginationOptsValidator } from 'convex/server'\nimport { compareValues, type GenericId, type GenericValidator } from 'convex/values'\nimport { z } from 'zod'\n\n/** Shared hard ceilings; the execution engine may impose lower budgets. */\nexport const MAX_PAGE_SIZE = 100\nexport const MAX_FILTER_DEPTH = 4\nexport const MAX_FILTER_NODES = 256\nexport const MAX_FILTER_IN_VALUES = 100\n\nexport type FieldSchemas = Record<string, z.ZodType>\ntype NativeField<Schema extends z.ZodType> = ConvexValidatorFromZod<Schema, 'required'>\ntype ScalarKind = 'id' | 'string' | 'float64' | 'int64' | 'boolean' | 'null' | 'literal'\ntype SupportedNative<Validator extends GenericValidator> = Validator extends { kind: ScalarKind }\n\t? true\n\t: Validator extends { kind: 'union'; members: infer Members extends GenericValidator[] }\n\t\t? false extends SupportedNative<Members[number]>\n\t\t\t? false\n\t\t\t: true\n\t\t: false\n\nexport type ScalarSelection<Fields extends FieldSchemas> = {\n\t[\n\t\tKey in keyof Fields as false extends SupportedNative<NativeField<Fields[Key]>> ? never : Key\n\t]?: true\n}\nexport type ExactScalarSelection<Fields extends FieldSchemas, Selection> = Selection & {\n\t[Key in keyof Selection]: Key extends keyof ScalarSelection<Fields>\n\t\t? Selection[Key] extends true\n\t\t\t? true\n\t\t\t: never\n\t\t: never\n}\n\ntype NativeValue<Schema extends z.ZodType> = Exclude<NativeField<Schema>['type'], undefined>\ntype FilterInputValue<Value> = Value extends GenericId<string> ? string : Value\ntype FilterValue<Schema extends z.ZodType, Side extends 'input' | 'output'> = Side extends 'input'\n\t? FilterInputValue<NativeValue<Schema>>\n\t: NativeValue<Schema>\ntype RangeKind = 'string' | 'float64' | 'int64' | 'literal'\ntype OrderedNative<Validator extends GenericValidator> = Validator extends { kind: RangeKind }\n\t? Validator['type'] extends string | number | bigint | undefined\n\t\t? true\n\t\t: false\n\t: Validator extends { kind: 'union'; members: infer Members extends GenericValidator[] }\n\t\t? false extends OrderedNative<Members[number]>\n\t\t\t? false\n\t\t\t: true\n\t\t: false\ntype OrderedValue<Schema extends z.ZodType> =\n\tOrderedNative<NativeField<Schema>> extends true ? NativeValue<Schema> : never\n\nexport type ScalarPredicate<Schema extends z.ZodType, Side extends 'input' | 'output'> =\n\t| FilterValue<Schema, Side>\n\t| ({ eq?: FilterValue<Schema, Side>; in?: FilterValue<Schema, Side>[] } & ([\n\t\t\tOrderedValue<Schema>,\n\t ] extends [never]\n\t\t\t? {}\n\t\t\t: {\n\t\t\t\t\tgt?: OrderedValue<Schema>\n\t\t\t\t\tgte?: OrderedValue<Schema>\n\t\t\t\t\tlt?: OrderedValue<Schema>\n\t\t\t\t\tlte?: OrderedValue<Schema>\n\t\t\t\t}) &\n\t\t\t('optional' extends NativeField<Schema>['isOptional'] ? { exists?: boolean } : {}))\n\ntype SelectedFieldFilters<\n\tFields extends FieldSchemas,\n\tSelection,\n\tSide extends 'input' | 'output',\n> = {\n\t[Key in Extract<keyof Selection, keyof Fields>]?: ScalarPredicate<Fields[Key], Side>\n}\n\n/** Direct shorthand and explicit fields combine with AND. The fields namespace\n * accepts every selected scalar name, including boolean-control/namespace names.\n */\ntype FilterDepth = readonly [1, 1, 1, 1]\ntype Tail<Depth extends readonly 1[]> = Depth extends readonly [1, ...infer Rest extends 1[]]\n\t? Rest\n\t: []\n\nexport type ScalarFilters<\n\tFields extends FieldSchemas,\n\tSelection,\n\tSide extends 'input' | 'output' = 'output',\n\tDepth extends readonly 1[] = FilterDepth,\n> = Omit<SelectedFieldFilters<Fields, Selection, Side>, 'and' | 'or' | 'not' | 'fields'> & {\n\tfields?: SelectedFieldFilters<Fields, Selection, Side>\n} & (Depth extends readonly []\n\t\t? {}\n\t\t: {\n\t\t\t\tand?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>[]\n\t\t\t\tor?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>[]\n\t\t\t\tnot?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>\n\t\t\t})\n\nconst nativeOptions = convexToZod(paginationOptsValidator)\nexport const nativePagination = nativeOptions.strict().extend({\n\tnumItems: nativeOptions.shape.numItems.int().positive().max(MAX_PAGE_SIZE),\n\tmaximumRowsRead: nativeOptions.shape.maximumRowsRead.unwrap().int().positive().optional(),\n\tmaximumBytesRead: nativeOptions.shape.maximumBytesRead.unwrap().int().positive().optional(),\n})\nexport type NativePagination = z.output<typeof nativePagination>\n/** Preserve the object boundary so pagination options can be registered directly as query args. */\nexport type PaginationOptionsSchema<Filters, InputFilters = Filters> = z.ZodObject<\n\t(typeof nativePagination)['shape'] & {\n\t\tfilters: z.ZodOptional<z.ZodType<Filters, InputFilters>>\n\t\torder: z.ZodOptional<z.ZodEnum<{ asc: 'asc'; desc: 'desc' }>>\n\t},\n\tz.core.$strict\n>\nexport type ScalarPaginationSchema<\n\tFields extends FieldSchemas,\n\tSelection,\n> = PaginationOptionsSchema<\n\tScalarFilters<Fields, Selection>,\n\tScalarFilters<Fields, Selection, 'input'>\n>\n\n/** Scalar-only schema constructor shared by table and future registry boundaries.\n * Convex metadata describes stored INPUT, so storage refinements/transforms never run here.\n */\nexport function scalarPaginationSchema<\n\tFields extends FieldSchemas,\n\tconst Selection extends ScalarSelection<Fields>,\n>(\n\tvalidators: Record<string, GenericValidator>,\n\tselection: ExactScalarSelection<Fields, Selection>,\n): ScalarPaginationSchema<Fields, Selection> {\n\tconst predicates: FieldSchemas = {}\n\tfor (const [key, selected] of Object.entries(selection)) {\n\t\tif (selected !== true || !Object.hasOwn(validators, key)) {\n\t\t\tthrow new Error(`Invalid scalar filter selection \"${key}\"`)\n\t\t}\n\t\tpredicates[key] = scalarPredicateSchema(validators[key]).optional()\n\t}\n\tconst filters = boundedFilterSchema(predicates)\n\tconst result = nativePagination.extend({\n\t\tfilters: filters.optional(),\n\t\torder: z.enum(['asc', 'desc']).optional(),\n\t})\n\t// SAFETY: selected validators generate the same native scalar input/output relationships\n\t// described by ScalarFilters, including converter-registered ID brands.\n\treturn result as ScalarPaginationSchema<Fields, Selection>\n}\n\nexport function scalarPredicateSchema(validator: GenericValidator): z.ZodType {\n\tassertSupportedScalar(validator)\n\tconst scalar = convexToZod(validator).nonoptional()\n\tconst equality = z.object({\n\t\teq: scalar.optional(),\n\t\tin: z.array(scalar).min(1).max(MAX_FILTER_IN_VALUES).optional(),\n\t})\n\tconst ranged = supportsRange(validator)\n\t\t? equality.extend({\n\t\t\t\tgt: scalar.optional(),\n\t\t\t\tgte: scalar.optional(),\n\t\t\t\tlt: scalar.optional(),\n\t\t\t\tlte: scalar.optional(),\n\t\t\t})\n\t\t: equality\n\tconst existence =\n\t\tvalidator.isOptional === 'optional' ? ranged.extend({ exists: z.boolean().optional() }) : ranged\n\tconst explicit = existence\n\t\t.strict()\n\t\t.refine(\n\t\t\t(value) => Object.values(value).some((entry) => entry !== undefined),\n\t\t\t'At least one operator is required',\n\t\t)\n\treturn z.union([scalar, explicit])\n}\n\ntype FilterTree =\n\t| string\n\t| number\n\t| bigint\n\t| boolean\n\t| null\n\t| undefined\n\t| FilterTree[]\n\t| { [key: string]: FilterTree }\n\n// Compose Zod's exported core constructor with its ordinary object initializer.\n// Clones use this constructor again, preserving preflight without replacing the\n// converter-recognized object definition or any native scalar/ID validators.\nconst relationPredicates = new WeakMap<\n\tz.core.$ZodType,\n\t{ cardinality: 'one' | 'many'; target: FilterGrammar }\n>()\nconst variantPredicates = new WeakMap<z.core.$ZodType, NonNullable<FilterGrammar['variants']>>()\nconst filterGrammars = new WeakMap<z.core.$ZodType, FilterGrammar>()\n\nconst BoundedFilterObject = z.core.$constructor<z.ZodObject>('BoundedFilterObject', (inst, def) => {\n\tz.ZodObject.init(inst, def)\n\tconst parse = inst._zod.parse\n\tconst namespace = def.shape.fields\n\tconst namespaceObject = namespace instanceof z.ZodOptional ? namespace.unwrap() : undefined\n\tconst selectedKeys =\n\t\tnamespaceObject instanceof z.ZodObject\n\t\t\t? Object.keys(namespaceObject.shape)\n\t\t\t: Object.keys(def.shape)\n\tconst grammar: FilterGrammar = {\n\t\tvariants: variantPredicates.get(def.shape.variant),\n\t\tfields: Object.fromEntries(selectedKeys.map((key) => [key, def.shape[key]])),\n\t\trelations: Object.fromEntries(\n\t\t\tObject.entries(def.shape).flatMap(([key, schema]) => {\n\t\t\t\tconst relation = relationPredicates.get(schema)\n\t\t\t\treturn relation ? [[key, relation]] : []\n\t\t\t}),\n\t\t),\n\t}\n\t// Zod refinements run after parsing. Guard parse itself so both the deferred\n\t// no-check run and the refinement run retain the same pre-child boundary.\n\tinst._zod.parse = (payload, ctx) => {\n\t\t// SAFETY: preflight examines layout and budgets only; Zod still validates values.\n\t\tif (!withinFilterBudget(payload.value as FilterTree, grammar)) {\n\t\t\tpayload.issues.push({\n\t\t\t\tcode: 'custom',\n\t\t\t\tinput: payload.value,\n\t\t\t\tmessage: 'Unsupported filter layout or depth/node budget exceeded',\n\t\t\t})\n\t\t\treturn payload\n\t\t}\n\t\treturn parse(payload, ctx)\n\t}\n})\n\n/** Each boolean child and relation target expression consumes one of four edges.\n * some/none wrappers and fields namespaces consume nodes, but no extra depth.\n */\nexport interface FilterGrammar {\n\tfields: FieldSchemas\n\tvariants?: { discriminator: string; options: Record<string, FilterGrammar> }\n\trelations: Record<string, { cardinality: 'one' | 'many'; target: FilterGrammar }>\n}\n\nexport function relationFilterSchema(\n\tgrammar: FilterGrammar,\n\tcache = new WeakMap<FilterGrammar, Map<number, z.ZodObject>>(),\n): z.ZodType {\n\tconst schema = new BoundedFilterObject(filterNodeSchema(grammar, 0, cache)._zod.def)\n\tfilterGrammars.set(schema, grammar)\n\treturn schema\n}\n\nexport type FilterScalar = string | number | bigint | boolean | null\nexport type FilterOperator = 'eq' | 'in' | 'gt' | 'gte' | 'lt' | 'lte' | 'exists'\nexport type FilterAst =\n\t| { kind: 'and' | 'or'; children: FilterAst[] }\n\t| { kind: 'not'; child: FilterAst }\n\t| { kind: 'variant'; discriminator: string; tag: string; child: FilterAst }\n\t| {\n\t\t\tkind: 'scalar'\n\t\t\tfield: string\n\t\t\toperator: FilterOperator\n\t\t\tvalue: FilterScalar | FilterScalar[]\n\t }\n\t| { kind: 'relation'; name: string; mode: 'one' | 'some' | 'none'; predicate: FilterAst | null }\nexport interface QueryIndex {\n\tname: string\n\tfields: readonly string[]\n}\nexport interface IndexCondition {\n\tvalue: FilterScalar | undefined\n\tindex: string\n}\nexport interface CompiledFilters {\n\tresidual: FilterAst\n\tbindings: ReadonlyMap<string, FilterScalar>\n\tunsatisfiable: boolean\n\tindex: QueryIndex | null\n\tprefix: readonly { field: string; value: FilterScalar }[]\n}\ntype FilterSpec<Schema extends z.ZodType> =\n\tz.input<Schema> extends { filters?: infer Spec } ? Spec : never\nexport type ServerFilter<Fields extends FieldSchemas> = ScalarFilters<\n\tFields,\n\tScalarSelection<Fields>\n>\n\ninterface BindingResult {\n\tvalues: Map<string, FilterScalar>\n\timpossible: boolean\n}\n\n/** Validate first; where is independently validated and ANDed with client predicates.\n * OR remains a residual expression over one traversal, never multiple streams.\n */\nexport function compileFilters<Schema extends z.ZodType>(\n\tschema: Schema,\n\tinput: z.input<Schema>,\n\tindexes: readonly QueryIndex[],\n\tconditions: readonly IndexCondition[] = [],\n\twhere?: FilterSpec<Schema>,\n): CompiledFilters {\n\t// SAFETY: pagination schemas validate filters before this internal AST adapter reads them.\n\tconst parsed = schema.parse(input) as { filters?: FilterTree }\n\t// SAFETY: server filters use the same validation boundary as client filters.\n\tconst server =\n\t\twhere === undefined\n\t\t\t? undefined\n\t\t\t: (schema.parse({ ...parsed, filters: where }) as { filters?: FilterTree })\n\tconst grammar = paginationGrammar(schema)\n\tconst residual: FilterAst = {\n\t\tkind: 'and',\n\t\tchildren: [\n\t\t\tnormalizeFilters(parsed.filters, grammar),\n\t\t\tnormalizeFilters(server?.filters, grammar),\n\t\t],\n\t}\n\tconst guaranteed = guaranteedBindings(residual)\n\tconst sorted = [...indexes].sort(\n\t\t(a, b) =>\n\t\t\ta.name.localeCompare(b.name) || a.fields.join('\\0').localeCompare(b.fields.join('\\0')),\n\t)\n\tconst condition = conditions.find((entry) => entry.value !== null && entry.value !== undefined)\n\tlet index: QueryIndex | null = null\n\tif (condition) {\n\t\tindex = sorted.find((entry) => entry.name === condition.index) ?? null\n\t\tif (!index || prefixBindings(index, guaranteed.values).length === 0)\n\t\t\tthrow new Error(`Explicit index \"${condition.index}\" is incompatible with guaranteed filters`)\n\t} else {\n\t\tlet length = 0\n\t\tfor (const candidate of sorted) {\n\t\t\tconst usable = prefixBindings(candidate, guaranteed.values).length\n\t\t\tif (usable > length) {\n\t\t\t\tindex = candidate\n\t\t\t\tlength = usable\n\t\t\t}\n\t\t}\n\t}\n\treturn {\n\t\tresidual,\n\t\tbindings: guaranteed.values,\n\t\tunsatisfiable: guaranteed.impossible,\n\t\tindex,\n\t\tprefix: index ? prefixBindings(index, guaranteed.values) : [],\n\t}\n}\nfunction paginationGrammar(schema: z.ZodType): FilterGrammar | undefined {\n\tif (!(schema instanceof z.ZodObject)) return undefined\n\tconst filters = schema.shape.filters\n\tif (!(filters instanceof z.ZodOptional)) return undefined\n\tconst root = filters.unwrap()\n\tconst cached = filterGrammars.get(root)\n\tif (cached) return cached\n\tif (!(root instanceof z.ZodObject)) return undefined\n\treturn {\n\t\tfields: root.shape,\n\t\tvariants: variantPredicates.get(root.shape.variant),\n\t\trelations: Object.fromEntries(\n\t\t\tObject.entries(root.shape).flatMap(([name, predicate]) => {\n\t\t\t\t// SAFETY: object shapes contain Zod schemas; metadata only recognizes predicates created by our grammar.\n\t\t\t\tconst relation = relationPredicates.get(predicate as z.core.$ZodType)\n\t\t\t\treturn relation ? [[name, relation]] : []\n\t\t\t}),\n\t\t),\n\t}\n}\nfunction normalizeFilters(value: FilterTree, grammar?: FilterGrammar): FilterAst {\n\tconst children: FilterAst[] = []\n\tif (!value) return { kind: 'and', children }\n\t// SAFETY: the caller parsed a strict filter object; its grammar has no array/primitive expression.\n\tconst expression = value as Record<string, FilterTree>\n\tfor (const [name, predicate] of Object.entries(expression)) {\n\t\tif (predicate === undefined) continue\n\t\tif (name === 'and' || name === 'or') {\n\t\t\t// SAFETY: boolean composition is validated as an array of expressions.\n\t\t\tchildren.push({\n\t\t\t\tkind: name,\n\t\t\t\tchildren: (predicate as FilterTree[]).map((child) => normalizeFilters(child, grammar)),\n\t\t\t})\n\t\t} else if (name === 'variant' && grammar?.variants) {\n\t\t\t// SAFETY: strict branch grammar validates the literal tag and its selected target filters.\n\t\t\tconst scoped = predicate as { tag: string; filters: FilterTree }\n\t\t\tchildren.push({\n\t\t\t\tkind: 'variant',\n\t\t\t\tdiscriminator: grammar.variants.discriminator,\n\t\t\t\ttag: scoped.tag,\n\t\t\t\tchild: normalizeFilters(scoped.filters, grammar.variants.options[scoped.tag]),\n\t\t\t})\n\t\t} else if (name === 'not')\n\t\t\tchildren.push({ kind: 'not', child: normalizeFilters(predicate, grammar) })\n\t\telse if (name === 'fields') {\n\t\t\t// SAFETY: fields is a strict selected scalar namespace.\n\t\t\tfor (const [field, scalar] of Object.entries(predicate as Record<string, FilterTree>))\n\t\t\t\tchildren.push(...normalizeScalar(field, scalar))\n\t\t} else if (grammar?.relations[name]) {\n\t\t\tconst relation = grammar.relations[name]\n\t\t\tif (relation.cardinality === 'one')\n\t\t\t\tchildren.push({\n\t\t\t\t\tkind: 'relation',\n\t\t\t\t\tname,\n\t\t\t\t\tmode: 'one',\n\t\t\t\t\tpredicate: predicate === null ? null : normalizeFilters(predicate, relation.target),\n\t\t\t\t})\n\t\t\telse {\n\t\t\t\t// SAFETY: many predicates validate as exactly one some/none target expression.\n\t\t\t\tconst target = predicate as { some?: FilterTree; none?: FilterTree }\n\t\t\t\tchildren.push({\n\t\t\t\t\tkind: 'relation',\n\t\t\t\t\tname,\n\t\t\t\t\tmode: target.some === undefined ? 'none' : 'some',\n\t\t\t\t\tpredicate: normalizeFilters(target.some ?? target.none, relation.target),\n\t\t\t\t})\n\t\t\t}\n\t\t} else children.push(...normalizeScalar(name, predicate))\n\t}\n\treturn { kind: 'and', children }\n}\nfunction normalizeScalar(field: string, predicate: FilterTree): FilterAst[] {\n\tif (predicate === undefined) return []\n\tif (Object(predicate) !== predicate) {\n\t\t// SAFETY: native scalar grammar excludes object/array values and preserves branded ID strings.\n\t\treturn [{ kind: 'scalar', field, operator: 'eq', value: predicate as FilterScalar }]\n\t}\n\t// SAFETY: only selected supported scalar operator objects survive validation.\n\treturn Object.entries(predicate as Record<FilterOperator, FilterScalar | FilterScalar[]>)\n\t\t.filter(([, value]) => value !== undefined)\n\t\t.map(([operator, value]) => ({\n\t\t\tkind: 'scalar',\n\t\t\tfield,\n\t\t\toperator: operator as FilterOperator,\n\t\t\tvalue,\n\t\t}))\n}\nfunction valueEqual(left: FilterScalar, right: FilterScalar): boolean {\n\treturn compareValues(left, right) === 0\n}\nfunction guaranteedBindings(ast: FilterAst): BindingResult {\n\tconst values = new Map<string, FilterScalar>()\n\tif (ast.kind === 'variant')\n\t\treturn guaranteedBindings({\n\t\t\tkind: 'and',\n\t\t\tchildren: [\n\t\t\t\t{ kind: 'scalar', field: ast.discriminator, operator: 'eq', value: ast.tag },\n\t\t\t\tast.child,\n\t\t\t],\n\t\t})\n\tif (ast.kind === 'scalar') {\n\t\tif (ast.operator === 'eq' && !Array.isArray(ast.value)) values.set(ast.field, ast.value)\n\t\tconst choices = Array.isArray(ast.value) ? ast.value : undefined\n\t\tif (ast.operator === 'in' && choices && choices.every((value) => valueEqual(value, choices[0])))\n\t\t\tvalues.set(ast.field, choices[0])\n\t\treturn { values, impossible: false }\n\t}\n\tif (ast.kind === 'not' || ast.kind === 'relation') return { values, impossible: false }\n\tconst branches = ast.children.map(guaranteedBindings)\n\tif (ast.kind === 'or') {\n\t\tconst viable = branches.filter((branch) => !branch.impossible)\n\t\tfor (const [field, value] of viable[0]?.values ?? [])\n\t\t\tif (\n\t\t\t\tviable.every(\n\t\t\t\t\t(branch) => branch.values.has(field) && valueEqual(branch.values.get(field)!, value),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tvalues.set(field, value)\n\t\treturn { values, impossible: viable.every((branch) => branch.impossible) }\n\t}\n\tlet impossible = branches.some((branch) => branch.impossible)\n\tfor (const branch of branches)\n\t\tfor (const [field, value] of branch.values) {\n\t\t\tif (values.has(field) && !valueEqual(values.get(field)!, value)) impossible = true\n\t\t\tvalues.set(field, value)\n\t\t}\n\treturn { values, impossible }\n}\nfunction prefixBindings(\n\tindex: QueryIndex,\n\tbindings: ReadonlyMap<string, FilterScalar>,\n): { field: string; value: FilterScalar }[] {\n\tconst prefix: { field: string; value: FilterScalar }[] = []\n\tfor (const field of index.fields) {\n\t\tif (!bindings.has(field)) break\n\t\tprefix.push({ field, value: bindings.get(field)! })\n\t}\n\treturn prefix\n}\n\nfunction assertSupportedScalar(validator: GenericValidator): void {\n\tswitch (validator.kind) {\n\t\tcase 'id':\n\t\tcase 'string':\n\t\tcase 'float64':\n\t\tcase 'int64':\n\t\tcase 'boolean':\n\t\tcase 'null':\n\t\tcase 'literal':\n\t\t\treturn\n\t\tcase 'union':\n\t\t\tfor (const member of validator.members) {\n\t\t\t\tassertSupportedScalar(member)\n\t\t\t}\n\t\t\treturn\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported scalar filter kind \"${validator.kind}\"`)\n\t}\n}\n\nfunction supportsRange(validator: GenericValidator): boolean {\n\tswitch (validator.kind) {\n\t\tcase 'string':\n\t\tcase 'float64':\n\t\tcase 'int64':\n\t\t\treturn true\n\t\tcase 'literal':\n\t\t\treturn z.union([z.string(), z.number(), z.bigint()]).safeParse(validator.value).success\n\t\tcase 'union':\n\t\t\treturn validator.members.every(supportsRange)\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n\nfunction boundedFilterSchema(predicates: FieldSchemas): z.ZodType {\n\treturn relationFilterSchema({ fields: predicates, relations: {} })\n}\n\nfunction filterNodeSchema(\n\tgrammar: FilterGrammar,\n\tdepth: number,\n\tcache = new WeakMap<FilterGrammar, Map<number, z.ZodObject>>(),\n): z.ZodObject {\n\tconst cached = cache.get(grammar)?.get(depth)\n\tif (cached) return cached\n\tconst predicates = grammar.fields\n\tconst direct = Object.fromEntries(\n\t\tObject.entries(predicates).filter(\n\t\t\t([key]) =>\n\t\t\t\t!['and', 'or', 'not', 'fields', ...(grammar.variants ? ['variant'] : [])].includes(key),\n\t\t),\n\t)\n\tconst relations: FieldSchemas = {}\n\tfor (const [name, relation] of Object.entries(grammar.relations)) {\n\t\tif (depth === MAX_FILTER_DEPTH) continue\n\t\tconst target = filterNodeSchema(relation.target, depth + 1, cache)\n\t\trelations[name] = (\n\t\t\trelation.cardinality === 'one'\n\t\t\t\t? target.nullable()\n\t\t\t\t: z.union([z.object({ some: target }).strict(), z.object({ none: target }).strict()])\n\t\t).optional()\n\t\trelationPredicates.set(relations[name], relation)\n\t}\n\tif (grammar.variants && depth < MAX_FILTER_DEPTH) {\n\t\tconst options = Object.entries(grammar.variants.options).map(([tag, branch]) =>\n\t\t\tz.strictObject({ tag: z.literal(tag), filters: filterNodeSchema(branch, depth + 1, cache) }),\n\t\t)\n\t\tif (options.length) {\n\t\t\trelations.variant = z.union(options).optional()\n\t\t\tvariantPredicates.set(relations.variant, grammar.variants)\n\t\t}\n\t}\n\tconst node = z\n\t\t.object({ ...direct, ...relations, fields: z.object(predicates).strict().optional() })\n\t\t.strict()\n\tconst child = depth === MAX_FILTER_DEPTH ? undefined : filterNodeSchema(grammar, depth + 1, cache)\n\tconst result = child\n\t\t? node.extend({\n\t\t\t\tand: z.array(child).min(1).max(MAX_FILTER_NODES).optional(),\n\t\t\t\tor: z.array(child).min(1).max(MAX_FILTER_NODES).optional(),\n\t\t\t\tnot: child.optional(),\n\t\t\t})\n\t\t: node\n\tlet depths = cache.get(grammar)\n\tif (!depths) {\n\t\tdepths = new Map()\n\t\tcache.set(grammar, depths)\n\t}\n\tdepths.set(depth, result)\n\treturn result\n}\n\nfunction withinFilterBudget(value: FilterTree, grammar: FilterGrammar): boolean {\n\ttype Context =\n\t\t| 'expression'\n\t\t| 'fields'\n\t\t| 'predicate'\n\t\t| 'composition'\n\t\t| 'many'\n\t\t| 'variant'\n\t\t| 'value'\n\tinterface Entry {\n\t\tvalue: FilterTree\n\t\tdepth: number\n\t\tcontext: Context\n\t\tgrammar: FilterGrammar\n\t}\n\tconst pending: Entry[] = [{ value, depth: 0, context: 'expression', grammar }]\n\tlet nodes = 0\n\twhile (pending.length) {\n\t\tconst entry = pending.pop()!\n\t\tif (++nodes > MAX_FILTER_NODES || entry.depth > MAX_FILTER_DEPTH) return false\n\t\tif (Object(entry.value) !== entry.value) continue\n\t\t// SAFETY: boxing excludes null and primitives; descriptors below avoid invoking getters.\n\t\tconst object = entry.value as FilterTree[] | Record<string, FilterTree>\n\t\tconst array = Array.isArray(object) ? object : undefined\n\t\tconst prototype = Object.getPrototypeOf(object)\n\t\tif (\n\t\t\tarray ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null\n\t\t)\n\t\t\treturn false\n\t\tif (array && array.length + nodes + pending.length > MAX_FILTER_NODES) return false\n\t\tconst keys = Reflect.ownKeys(object)\n\t\tif (\n\t\t\tkeys.length - (array ? 1 : 0) + nodes + pending.length > MAX_FILTER_NODES ||\n\t\t\t(array && keys.length !== array.length + 1)\n\t\t)\n\t\t\treturn false\n\t\tlet consumed: string[]\n\t\tswitch (entry.context) {\n\t\t\tcase 'expression':\n\t\t\t\tconsumed = [\n\t\t\t\t\t...Object.keys(entry.grammar.fields),\n\t\t\t\t\t...Object.keys(entry.grammar.relations),\n\t\t\t\t\t'and',\n\t\t\t\t\t'or',\n\t\t\t\t\t'not',\n\t\t\t\t\t'fields',\n\t\t\t\t\t...(entry.grammar.variants ? ['variant'] : []),\n\t\t\t\t]\n\t\t\t\tbreak\n\t\t\tcase 'variant':\n\t\t\t\tconsumed = ['tag', 'filters']\n\t\t\t\tbreak\n\t\t\tcase 'fields':\n\t\t\t\tconsumed = Object.keys(entry.grammar.fields)\n\t\t\t\tbreak\n\t\t\tcase 'predicate':\n\t\t\t\tconsumed = ['eq', 'in', 'gt', 'gte', 'lt', 'lte', 'exists']\n\t\t\t\tbreak\n\t\t\tcase 'many':\n\t\t\t\tconsumed = ['some', 'none']\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tconsumed = []\n\t\t}\n\t\tfor (const key of consumed) if (!Object.hasOwn(object, key) && key in object) return false\n\t\tfor (const key of keys) {\n\t\t\tif (array && key === 'length') continue\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(object, key)!\n\t\t\tconst name = String(key)\n\t\t\tif (!descriptor.enumerable || !('value' in descriptor) || name !== key) return false\n\t\t\tif (array && (!/^(0|[1-9]\\d*)$/.test(name) || Number(name) >= array.length)) return false\n\t\t\tlet context: Context = 'value'\n\t\t\tlet depth = entry.depth\n\t\t\tlet target = entry.grammar\n\t\t\tif (entry.context === 'expression') {\n\t\t\t\tcontext = 'predicate'\n\t\t\t\tif (name === 'variant' && entry.grammar.variants) {\n\t\t\t\t\tcontext = 'variant'\n\t\t\t\t\tdepth++\n\t\t\t\t} else if (name === 'fields') context = 'fields'\n\t\t\t\telse if (name === 'and' || name === 'or') {\n\t\t\t\t\tcontext = 'composition'\n\t\t\t\t\tdepth++\n\t\t\t\t} else if (name === 'not') {\n\t\t\t\t\tcontext = 'expression'\n\t\t\t\t\tdepth++\n\t\t\t\t} else if (entry.grammar.relations[name]) {\n\t\t\t\t\tconst relation = entry.grammar.relations[name]\n\t\t\t\t\tcontext = relation.cardinality === 'one' ? 'expression' : 'many'\n\t\t\t\t\ttarget = relation.target\n\t\t\t\t\tdepth++\n\t\t\t\t}\n\t\t\t} else if (entry.context === 'variant' && name === 'filters') {\n\t\t\t\tconst tag = Object.getOwnPropertyDescriptor(object, 'tag')\n\t\t\t\tif (\n\t\t\t\t\t!tag ||\n\t\t\t\t\t!('value' in tag) ||\n\t\t\t\t\t!z.string().safeParse(tag.value).success ||\n\t\t\t\t\t!Object.hasOwn(entry.grammar.variants?.options ?? {}, tag.value)\n\t\t\t\t)\n\t\t\t\t\treturn false\n\t\t\t\tcontext = 'expression'\n\t\t\t\ttarget = entry.grammar.variants!.options[String(tag.value)]\n\t\t\t} else if (entry.context === 'fields') context = 'predicate'\n\t\t\telse if (entry.context === 'composition' || entry.context === 'many') context = 'expression'\n\t\t\tpending.push({ value: descriptor.value, depth, context, grammar: target })\n\t\t}\n\t}\n\treturn true\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"filters.mjs","names":["MAX_PAGE_SIZE","MAX_FILTER_DEPTH","MAX_FILTER_NODES","MAX_FILTER_IN_VALUES","nativeOptions","convexToZod","paginationOptsValidator","nativePagination","scalarPaginationSchema","predicates","key","selected","selection","validators","scalarPredicateSchema","filters","boundedFilterSchema","z","assertSupportedScalar","validator","scalar","equality","ranged","supportsRange","explicit","value","entry","relationPredicates","variantPredicates","filterGrammars","BoundedFilterObject","inst","def","parse","namespace","namespaceObject","selectedKeys","grammar","schema","relation","withinFilterBudget","payload","ctx","relationFilterSchema","filterNodeSchema","cache","compileFilters","parseFilterPlan","input","indexes","conditions","where","args","parsed","server","paginationGrammar","residual","normalizeFilters","guaranteed","guaranteedBindings","sorted","a","b","condition","index","prefixBindings","length","candidate","usable","root","cached","name","predicate","children","expression","child","scoped","field","normalizeScalar","target","operator","valueEqual","compareValues","left","right","values","ast","choices","branches","viable","branch","impossible","prefix","bindings","member","depth","direct","relations","options","tag","node","result","depths","pending","nodes","object","array","prototype","keys","consumed","descriptor","context"],"sources":["../../src/querying/filters.ts"],"sourcesContent":["import { convexToZod, type ConvexValidatorFromZod } from 'convex-helpers/server/zod4'\nimport { paginationOptsValidator } from 'convex/server'\nimport { compareValues, type GenericId, type GenericValidator } from 'convex/values'\nimport { z } from 'zod'\n\n/** Shared hard ceilings; the execution engine may impose lower budgets. */\nexport const MAX_PAGE_SIZE = 100\nexport const MAX_FILTER_DEPTH = 4\nexport const MAX_FILTER_NODES = 256\nexport const MAX_FILTER_IN_VALUES = 100\n\nexport type FieldSchemas = Record<string, z.ZodType>\ntype NativeField<Schema extends z.ZodType> = ConvexValidatorFromZod<Schema, 'required'>\ntype ScalarKind = 'id' | 'string' | 'float64' | 'int64' | 'boolean' | 'null' | 'literal'\ntype SupportedNative<Validator extends GenericValidator> = Validator extends { kind: ScalarKind }\n\t? true\n\t: Validator extends { kind: 'union'; members: infer Members extends GenericValidator[] }\n\t\t? false extends SupportedNative<Members[number]>\n\t\t\t? false\n\t\t\t: true\n\t\t: false\n\nexport type ScalarSelection<Fields extends FieldSchemas> = {\n\t[\n\t\tKey in keyof Fields as false extends SupportedNative<NativeField<Fields[Key]>> ? never : Key\n\t]?: true\n}\nexport type ExactScalarSelection<Fields extends FieldSchemas, Selection> = Selection & {\n\t[Key in keyof Selection]: Key extends keyof ScalarSelection<Fields>\n\t\t? Selection[Key] extends true\n\t\t\t? true\n\t\t\t: never\n\t\t: never\n}\n\ntype NativeValue<Schema extends z.ZodType> = Exclude<NativeField<Schema>['type'], undefined>\ntype FilterInputValue<Value> = Value extends GenericId<string> ? string : Value\ntype FilterValue<Schema extends z.ZodType, Side extends 'input' | 'output'> = Side extends 'input'\n\t? FilterInputValue<NativeValue<Schema>>\n\t: NativeValue<Schema>\ntype RangeKind = 'string' | 'float64' | 'int64' | 'literal'\ntype OrderedNative<Validator extends GenericValidator> = Validator extends { kind: RangeKind }\n\t? Validator['type'] extends string | number | bigint | undefined\n\t\t? true\n\t\t: false\n\t: Validator extends { kind: 'union'; members: infer Members extends GenericValidator[] }\n\t\t? false extends OrderedNative<Members[number]>\n\t\t\t? false\n\t\t\t: true\n\t\t: false\ntype OrderedValue<Schema extends z.ZodType> =\n\tOrderedNative<NativeField<Schema>> extends true ? NativeValue<Schema> : never\n\nexport type ScalarPredicate<Schema extends z.ZodType, Side extends 'input' | 'output'> =\n\t| FilterValue<Schema, Side>\n\t| ({ eq?: FilterValue<Schema, Side>; in?: FilterValue<Schema, Side>[] } & ([\n\t\t\tOrderedValue<Schema>,\n\t ] extends [never]\n\t\t\t? {}\n\t\t\t: {\n\t\t\t\t\tgt?: OrderedValue<Schema>\n\t\t\t\t\tgte?: OrderedValue<Schema>\n\t\t\t\t\tlt?: OrderedValue<Schema>\n\t\t\t\t\tlte?: OrderedValue<Schema>\n\t\t\t\t}) &\n\t\t\t('optional' extends NativeField<Schema>['isOptional'] ? { exists?: boolean } : {}))\n\ntype SelectedFieldFilters<\n\tFields extends FieldSchemas,\n\tSelection,\n\tSide extends 'input' | 'output',\n> = {\n\t[Key in Extract<keyof Selection, keyof Fields>]?: ScalarPredicate<Fields[Key], Side>\n}\n\n/** Direct shorthand and explicit fields combine with AND. The fields namespace\n * accepts every selected scalar name, including boolean-control/namespace names.\n */\ntype FilterDepth = readonly [1, 1, 1, 1]\ntype Tail<Depth extends readonly 1[]> = Depth extends readonly [1, ...infer Rest extends 1[]]\n\t? Rest\n\t: []\n\nexport type ScalarFilters<\n\tFields extends FieldSchemas,\n\tSelection,\n\tSide extends 'input' | 'output' = 'output',\n\tDepth extends readonly 1[] = FilterDepth,\n> = Omit<SelectedFieldFilters<Fields, Selection, Side>, 'and' | 'or' | 'not' | 'fields'> & {\n\tfields?: SelectedFieldFilters<Fields, Selection, Side>\n} & (Depth extends readonly []\n\t\t? {}\n\t\t: {\n\t\t\t\tand?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>[]\n\t\t\t\tor?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>[]\n\t\t\t\tnot?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>\n\t\t\t})\n\nconst nativeOptions = convexToZod(paginationOptsValidator)\nexport const nativePagination = nativeOptions.strict().extend({\n\tnumItems: nativeOptions.shape.numItems.int().positive().max(MAX_PAGE_SIZE),\n\tmaximumRowsRead: nativeOptions.shape.maximumRowsRead.unwrap().int().positive().optional(),\n\tmaximumBytesRead: nativeOptions.shape.maximumBytesRead.unwrap().int().positive().optional(),\n})\nexport type NativePagination = z.output<typeof nativePagination>\n/** Preserve the object boundary so pagination options can be registered directly as query args. */\nexport type PaginationOptionsSchema<Filters, InputFilters = Filters> = z.ZodObject<\n\t(typeof nativePagination)['shape'] & {\n\t\tfilters: z.ZodOptional<z.ZodType<Filters, InputFilters>>\n\t\torder: z.ZodOptional<z.ZodEnum<{ asc: 'asc'; desc: 'desc' }>>\n\t},\n\tz.core.$strict\n>\nexport type ScalarPaginationSchema<\n\tFields extends FieldSchemas,\n\tSelection,\n> = PaginationOptionsSchema<\n\tScalarFilters<Fields, Selection>,\n\tScalarFilters<Fields, Selection, 'input'>\n>\n\n/** Scalar-only schema constructor shared by table and future registry boundaries.\n * Convex metadata describes stored INPUT, so storage refinements/transforms never run here.\n */\nexport function scalarPaginationSchema<\n\tFields extends FieldSchemas,\n\tconst Selection extends ScalarSelection<Fields>,\n>(\n\tvalidators: Record<string, GenericValidator>,\n\tselection: ExactScalarSelection<Fields, Selection>,\n): ScalarPaginationSchema<Fields, Selection> {\n\tconst predicates: FieldSchemas = {}\n\tfor (const [key, selected] of Object.entries(selection)) {\n\t\tif (selected !== true || !Object.hasOwn(validators, key)) {\n\t\t\tthrow new Error(`Invalid scalar filter selection \"${key}\"`)\n\t\t}\n\t\tpredicates[key] = scalarPredicateSchema(validators[key]).optional()\n\t}\n\tconst filters = boundedFilterSchema(predicates)\n\tconst result = nativePagination.extend({\n\t\tfilters: filters.optional(),\n\t\torder: z.enum(['asc', 'desc']).optional(),\n\t})\n\t// SAFETY: selected validators generate the same native scalar input/output relationships\n\t// described by ScalarFilters, including converter-registered ID brands.\n\treturn result as ScalarPaginationSchema<Fields, Selection>\n}\n\nexport function scalarPredicateSchema(validator: GenericValidator): z.ZodType {\n\tassertSupportedScalar(validator)\n\tconst scalar = convexToZod(validator).nonoptional()\n\tconst equality = z.object({\n\t\teq: scalar.optional(),\n\t\tin: z.array(scalar).min(1).max(MAX_FILTER_IN_VALUES).optional(),\n\t})\n\tconst ranged = supportsRange(validator)\n\t\t? equality.extend({\n\t\t\t\tgt: scalar.optional(),\n\t\t\t\tgte: scalar.optional(),\n\t\t\t\tlt: scalar.optional(),\n\t\t\t\tlte: scalar.optional(),\n\t\t\t})\n\t\t: equality\n\tconst existence =\n\t\tvalidator.isOptional === 'optional' ? ranged.extend({ exists: z.boolean().optional() }) : ranged\n\tconst explicit = existence\n\t\t.strict()\n\t\t.refine(\n\t\t\t(value) => Object.values(value).some((entry) => entry !== undefined),\n\t\t\t'At least one operator is required',\n\t\t)\n\treturn z.union([scalar, explicit])\n}\n\ntype FilterTree =\n\t| string\n\t| number\n\t| bigint\n\t| boolean\n\t| null\n\t| undefined\n\t| FilterTree[]\n\t| { [key: string]: FilterTree }\n\n// Compose Zod's exported core constructor with its ordinary object initializer.\n// Clones use this constructor again, preserving preflight without replacing the\n// converter-recognized object definition or any native scalar/ID validators.\nconst relationPredicates = new WeakMap<\n\tz.core.$ZodType,\n\t{ cardinality: 'one' | 'many'; target: FilterGrammar }\n>()\nconst variantPredicates = new WeakMap<z.core.$ZodType, NonNullable<FilterGrammar['variants']>>()\nconst filterGrammars = new WeakMap<z.core.$ZodType, FilterGrammar>()\n\nconst BoundedFilterObject = z.core.$constructor<z.ZodObject>('BoundedFilterObject', (inst, def) => {\n\tz.ZodObject.init(inst, def)\n\tconst parse = inst._zod.parse\n\tconst namespace = def.shape.fields\n\tconst namespaceObject = namespace instanceof z.ZodOptional ? namespace.unwrap() : undefined\n\tconst selectedKeys =\n\t\tnamespaceObject instanceof z.ZodObject\n\t\t\t? Object.keys(namespaceObject.shape)\n\t\t\t: Object.keys(def.shape)\n\tconst grammar: FilterGrammar = {\n\t\tvariants: variantPredicates.get(def.shape.variant),\n\t\tfields: Object.fromEntries(selectedKeys.map((key) => [key, def.shape[key]])),\n\t\trelations: Object.fromEntries(\n\t\t\tObject.entries(def.shape).flatMap(([key, schema]) => {\n\t\t\t\tconst relation = relationPredicates.get(schema)\n\t\t\t\treturn relation ? [[key, relation]] : []\n\t\t\t}),\n\t\t),\n\t}\n\t// Zod refinements run after parsing. Guard parse itself so both the deferred\n\t// no-check run and the refinement run retain the same pre-child boundary.\n\tinst._zod.parse = (payload, ctx) => {\n\t\t// SAFETY: preflight examines layout and budgets only; Zod still validates values.\n\t\tif (!withinFilterBudget(payload.value as FilterTree, grammar)) {\n\t\t\tpayload.issues.push({\n\t\t\t\tcode: 'custom',\n\t\t\t\tinput: payload.value,\n\t\t\t\tmessage: 'Unsupported filter layout or depth/node budget exceeded',\n\t\t\t})\n\t\t\treturn payload\n\t\t}\n\t\treturn parse(payload, ctx)\n\t}\n})\n\n/** Each boolean child and relation target expression consumes one of four edges.\n * some/none wrappers and fields namespaces consume nodes, but no extra depth.\n */\nexport interface FilterGrammar {\n\tfields: FieldSchemas\n\tvariants?: { discriminator: string; options: Record<string, FilterGrammar> }\n\trelations: Record<string, { cardinality: 'one' | 'many'; target: FilterGrammar }>\n}\n\nexport function relationFilterSchema(\n\tgrammar: FilterGrammar,\n\tcache = new WeakMap<FilterGrammar, Map<number, z.ZodObject>>(),\n): z.ZodType {\n\tconst schema = new BoundedFilterObject(filterNodeSchema(grammar, 0, cache)._zod.def)\n\tfilterGrammars.set(schema, grammar)\n\treturn schema\n}\n\nexport type FilterScalar = string | number | bigint | boolean | null\nexport type FilterOperator = 'eq' | 'in' | 'gt' | 'gte' | 'lt' | 'lte' | 'exists'\nexport type FilterAst =\n\t| { kind: 'and' | 'or'; children: FilterAst[] }\n\t| { kind: 'not'; child: FilterAst }\n\t| { kind: 'variant'; discriminator: string; tag: string; child: FilterAst }\n\t| {\n\t\t\tkind: 'scalar'\n\t\t\tfield: string\n\t\t\toperator: FilterOperator\n\t\t\tvalue: FilterScalar | FilterScalar[]\n\t }\n\t| { kind: 'relation'; name: string; mode: 'one' | 'some' | 'none'; predicate: FilterAst | null }\nexport interface QueryIndex {\n\tname: string\n\tfields: readonly string[]\n}\nexport interface IndexCondition {\n\tvalue: FilterScalar | undefined\n\tindex: string\n}\nexport interface CompiledFilters {\n\tresidual: FilterAst\n\tbindings: ReadonlyMap<string, FilterScalar>\n\tunsatisfiable: boolean\n\tindex: QueryIndex | null\n\tprefix: readonly { field: string; value: FilterScalar }[]\n}\ntype FilterSpec<Schema extends z.ZodType> =\n\tz.input<Schema> extends { filters?: infer Spec } ? Spec : never\nexport type ServerFilter<Fields extends FieldSchemas> = ScalarFilters<\n\tFields,\n\tScalarSelection<Fields>\n>\n\ninterface BindingResult {\n\tvalues: Map<string, FilterScalar>\n\timpossible: boolean\n}\n\n/** Validate first; where is independently validated and ANDed with client predicates.\n * OR remains a residual expression over one traversal, never multiple streams.\n */\nexport function compileFilters<Schema extends z.ZodType>(\n\tschema: Schema,\n\tinput: z.input<Schema>,\n\tindexes: readonly QueryIndex[],\n\tconditions: readonly IndexCondition[] = [],\n\twhere?: FilterSpec<Schema>,\n): CompiledFilters {\n\treturn parseFilterPlan(schema, input, indexes, conditions, where).compiled\n}\n\n/** Internal planner boundary: retain the parsed arguments without parsing them again. */\nexport function parseFilterPlan<Schema extends z.ZodType>(\n\tschema: Schema,\n\tinput: z.input<Schema>,\n\tindexes: readonly QueryIndex[],\n\tconditions: readonly IndexCondition[] = [],\n\twhere?: FilterSpec<Schema>,\n) {\n\tconst args = schema.parse(input)\n\t// SAFETY: pagination schemas validate filters before this internal AST adapter reads them.\n\tconst parsed = args as { filters?: FilterTree }\n\t// SAFETY: server filters use the same validation boundary as client filters.\n\tconst server =\n\t\twhere === undefined\n\t\t\t? undefined\n\t\t\t: (schema.parse({ ...parsed, filters: where }) as { filters?: FilterTree })\n\tconst grammar = paginationGrammar(schema)\n\tconst residual: FilterAst = {\n\t\tkind: 'and',\n\t\tchildren: [\n\t\t\tnormalizeFilters(parsed.filters, grammar),\n\t\t\tnormalizeFilters(server?.filters, grammar),\n\t\t],\n\t}\n\tconst guaranteed = guaranteedBindings(residual)\n\tconst sorted = [...indexes].sort(\n\t\t(a, b) =>\n\t\t\ta.name.localeCompare(b.name) || a.fields.join('\\0').localeCompare(b.fields.join('\\0')),\n\t)\n\tconst condition = conditions.find((entry) => entry.value !== null && entry.value !== undefined)\n\tlet index: QueryIndex | null = null\n\tif (condition) {\n\t\tindex = sorted.find((entry) => entry.name === condition.index) ?? null\n\t\tif (!index || prefixBindings(index, guaranteed.values).length === 0)\n\t\t\tthrow new Error(`Explicit index \"${condition.index}\" is incompatible with guaranteed filters`)\n\t} else {\n\t\tlet length = 0\n\t\tfor (const candidate of sorted) {\n\t\t\tconst usable = prefixBindings(candidate, guaranteed.values).length\n\t\t\tif (usable > length) {\n\t\t\t\tindex = candidate\n\t\t\t\tlength = usable\n\t\t\t}\n\t\t}\n\t}\n\tconst compiled: CompiledFilters = {\n\t\tresidual,\n\t\tbindings: guaranteed.values,\n\t\tunsatisfiable: guaranteed.impossible,\n\t\tindex,\n\t\tprefix: index ? prefixBindings(index, guaranteed.values) : [],\n\t}\n\treturn { args, compiled }\n}\nfunction paginationGrammar(schema: z.ZodType): FilterGrammar | undefined {\n\tif (!(schema instanceof z.ZodObject)) return undefined\n\tconst filters = schema.shape.filters\n\tif (!(filters instanceof z.ZodOptional)) return undefined\n\tconst root = filters.unwrap()\n\tconst cached = filterGrammars.get(root)\n\tif (cached) return cached\n\tif (!(root instanceof z.ZodObject)) return undefined\n\treturn {\n\t\tfields: root.shape,\n\t\tvariants: variantPredicates.get(root.shape.variant),\n\t\trelations: Object.fromEntries(\n\t\t\tObject.entries(root.shape).flatMap(([name, predicate]) => {\n\t\t\t\t// SAFETY: object shapes contain Zod schemas; metadata only recognizes predicates created by our grammar.\n\t\t\t\tconst relation = relationPredicates.get(predicate as z.core.$ZodType)\n\t\t\t\treturn relation ? [[name, relation]] : []\n\t\t\t}),\n\t\t),\n\t}\n}\nfunction normalizeFilters(value: FilterTree, grammar?: FilterGrammar): FilterAst {\n\tconst children: FilterAst[] = []\n\tif (!value) return { kind: 'and', children }\n\t// SAFETY: the caller parsed a strict filter object; its grammar has no array/primitive expression.\n\tconst expression = value as Record<string, FilterTree>\n\tfor (const [name, predicate] of Object.entries(expression)) {\n\t\tif (predicate === undefined) continue\n\t\tif (name === 'and' || name === 'or') {\n\t\t\t// SAFETY: boolean composition is validated as an array of expressions.\n\t\t\tchildren.push({\n\t\t\t\tkind: name,\n\t\t\t\tchildren: (predicate as FilterTree[]).map((child) => normalizeFilters(child, grammar)),\n\t\t\t})\n\t\t} else if (name === 'variant' && grammar?.variants) {\n\t\t\t// SAFETY: strict branch grammar validates the literal tag and its selected target filters.\n\t\t\tconst scoped = predicate as { tag: string; filters: FilterTree }\n\t\t\tchildren.push({\n\t\t\t\tkind: 'variant',\n\t\t\t\tdiscriminator: grammar.variants.discriminator,\n\t\t\t\ttag: scoped.tag,\n\t\t\t\tchild: normalizeFilters(scoped.filters, grammar.variants.options[scoped.tag]),\n\t\t\t})\n\t\t} else if (name === 'not')\n\t\t\tchildren.push({ kind: 'not', child: normalizeFilters(predicate, grammar) })\n\t\telse if (name === 'fields') {\n\t\t\t// SAFETY: fields is a strict selected scalar namespace.\n\t\t\tfor (const [field, scalar] of Object.entries(predicate as Record<string, FilterTree>))\n\t\t\t\tchildren.push(...normalizeScalar(field, scalar))\n\t\t} else if (grammar?.relations[name]) {\n\t\t\tconst relation = grammar.relations[name]\n\t\t\tif (relation.cardinality === 'one')\n\t\t\t\tchildren.push({\n\t\t\t\t\tkind: 'relation',\n\t\t\t\t\tname,\n\t\t\t\t\tmode: 'one',\n\t\t\t\t\tpredicate: predicate === null ? null : normalizeFilters(predicate, relation.target),\n\t\t\t\t})\n\t\t\telse {\n\t\t\t\t// SAFETY: many predicates validate as exactly one some/none target expression.\n\t\t\t\tconst target = predicate as { some?: FilterTree; none?: FilterTree }\n\t\t\t\tchildren.push({\n\t\t\t\t\tkind: 'relation',\n\t\t\t\t\tname,\n\t\t\t\t\tmode: target.some === undefined ? 'none' : 'some',\n\t\t\t\t\tpredicate: normalizeFilters(target.some ?? target.none, relation.target),\n\t\t\t\t})\n\t\t\t}\n\t\t} else children.push(...normalizeScalar(name, predicate))\n\t}\n\treturn { kind: 'and', children }\n}\nfunction normalizeScalar(field: string, predicate: FilterTree): FilterAst[] {\n\tif (predicate === undefined) return []\n\tif (Object(predicate) !== predicate) {\n\t\t// SAFETY: native scalar grammar excludes object/array values and preserves branded ID strings.\n\t\treturn [{ kind: 'scalar', field, operator: 'eq', value: predicate as FilterScalar }]\n\t}\n\t// SAFETY: only selected supported scalar operator objects survive validation.\n\treturn Object.entries(predicate as Record<FilterOperator, FilterScalar | FilterScalar[]>)\n\t\t.filter(([, value]) => value !== undefined)\n\t\t.map(([operator, value]) => ({\n\t\t\tkind: 'scalar',\n\t\t\tfield,\n\t\t\toperator: operator as FilterOperator,\n\t\t\tvalue,\n\t\t}))\n}\nfunction valueEqual(left: FilterScalar, right: FilterScalar): boolean {\n\treturn compareValues(left, right) === 0\n}\nfunction guaranteedBindings(ast: FilterAst): BindingResult {\n\tconst values = new Map<string, FilterScalar>()\n\tif (ast.kind === 'variant')\n\t\treturn guaranteedBindings({\n\t\t\tkind: 'and',\n\t\t\tchildren: [\n\t\t\t\t{ kind: 'scalar', field: ast.discriminator, operator: 'eq', value: ast.tag },\n\t\t\t\tast.child,\n\t\t\t],\n\t\t})\n\tif (ast.kind === 'scalar') {\n\t\tif (ast.operator === 'eq' && !Array.isArray(ast.value)) values.set(ast.field, ast.value)\n\t\tconst choices = Array.isArray(ast.value) ? ast.value : undefined\n\t\tif (ast.operator === 'in' && choices && choices.every((value) => valueEqual(value, choices[0])))\n\t\t\tvalues.set(ast.field, choices[0])\n\t\treturn { values, impossible: false }\n\t}\n\tif (ast.kind === 'not' || ast.kind === 'relation') return { values, impossible: false }\n\tconst branches = ast.children.map(guaranteedBindings)\n\tif (ast.kind === 'or') {\n\t\tconst viable = branches.filter((branch) => !branch.impossible)\n\t\tfor (const [field, value] of viable[0]?.values ?? [])\n\t\t\tif (\n\t\t\t\tviable.every(\n\t\t\t\t\t(branch) => branch.values.has(field) && valueEqual(branch.values.get(field)!, value),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tvalues.set(field, value)\n\t\treturn { values, impossible: viable.every((branch) => branch.impossible) }\n\t}\n\tlet impossible = branches.some((branch) => branch.impossible)\n\tfor (const branch of branches)\n\t\tfor (const [field, value] of branch.values) {\n\t\t\tif (values.has(field) && !valueEqual(values.get(field)!, value)) impossible = true\n\t\t\tvalues.set(field, value)\n\t\t}\n\treturn { values, impossible }\n}\nfunction prefixBindings(\n\tindex: QueryIndex,\n\tbindings: ReadonlyMap<string, FilterScalar>,\n): { field: string; value: FilterScalar }[] {\n\tconst prefix: { field: string; value: FilterScalar }[] = []\n\tfor (const field of index.fields) {\n\t\tif (!bindings.has(field)) break\n\t\tprefix.push({ field, value: bindings.get(field)! })\n\t}\n\treturn prefix\n}\n\nfunction assertSupportedScalar(validator: GenericValidator): void {\n\tswitch (validator.kind) {\n\t\tcase 'id':\n\t\tcase 'string':\n\t\tcase 'float64':\n\t\tcase 'int64':\n\t\tcase 'boolean':\n\t\tcase 'null':\n\t\tcase 'literal':\n\t\t\treturn\n\t\tcase 'union':\n\t\t\tfor (const member of validator.members) {\n\t\t\t\tassertSupportedScalar(member)\n\t\t\t}\n\t\t\treturn\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported scalar filter kind \"${validator.kind}\"`)\n\t}\n}\n\nfunction supportsRange(validator: GenericValidator): boolean {\n\tswitch (validator.kind) {\n\t\tcase 'string':\n\t\tcase 'float64':\n\t\tcase 'int64':\n\t\t\treturn true\n\t\tcase 'literal':\n\t\t\treturn z.union([z.string(), z.number(), z.bigint()]).safeParse(validator.value).success\n\t\tcase 'union':\n\t\t\treturn validator.members.every(supportsRange)\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n\nfunction boundedFilterSchema(predicates: FieldSchemas): z.ZodType {\n\treturn relationFilterSchema({ fields: predicates, relations: {} })\n}\n\nfunction filterNodeSchema(\n\tgrammar: FilterGrammar,\n\tdepth: number,\n\tcache = new WeakMap<FilterGrammar, Map<number, z.ZodObject>>(),\n): z.ZodObject {\n\tconst cached = cache.get(grammar)?.get(depth)\n\tif (cached) return cached\n\tconst predicates = grammar.fields\n\tconst direct = Object.fromEntries(\n\t\tObject.entries(predicates).filter(\n\t\t\t([key]) =>\n\t\t\t\t!['and', 'or', 'not', 'fields', ...(grammar.variants ? ['variant'] : [])].includes(key),\n\t\t),\n\t)\n\tconst relations: FieldSchemas = {}\n\tfor (const [name, relation] of Object.entries(grammar.relations)) {\n\t\tif (depth === MAX_FILTER_DEPTH) continue\n\t\tconst target = filterNodeSchema(relation.target, depth + 1, cache)\n\t\trelations[name] = (\n\t\t\trelation.cardinality === 'one'\n\t\t\t\t? target.nullable()\n\t\t\t\t: z.union([z.object({ some: target }).strict(), z.object({ none: target }).strict()])\n\t\t).optional()\n\t\trelationPredicates.set(relations[name], relation)\n\t}\n\tif (grammar.variants && depth < MAX_FILTER_DEPTH) {\n\t\tconst options = Object.entries(grammar.variants.options).map(([tag, branch]) =>\n\t\t\tz.strictObject({ tag: z.literal(tag), filters: filterNodeSchema(branch, depth + 1, cache) }),\n\t\t)\n\t\tif (options.length) {\n\t\t\trelations.variant = z.union(options).optional()\n\t\t\tvariantPredicates.set(relations.variant, grammar.variants)\n\t\t}\n\t}\n\tconst node = z\n\t\t.object({ ...direct, ...relations, fields: z.object(predicates).strict().optional() })\n\t\t.strict()\n\tconst child = depth === MAX_FILTER_DEPTH ? undefined : filterNodeSchema(grammar, depth + 1, cache)\n\tconst result = child\n\t\t? node.extend({\n\t\t\t\tand: z.array(child).min(1).max(MAX_FILTER_NODES).optional(),\n\t\t\t\tor: z.array(child).min(1).max(MAX_FILTER_NODES).optional(),\n\t\t\t\tnot: child.optional(),\n\t\t\t})\n\t\t: node\n\tlet depths = cache.get(grammar)\n\tif (!depths) {\n\t\tdepths = new Map()\n\t\tcache.set(grammar, depths)\n\t}\n\tdepths.set(depth, result)\n\treturn result\n}\n\nfunction withinFilterBudget(value: FilterTree, grammar: FilterGrammar): boolean {\n\ttype Context =\n\t\t| 'expression'\n\t\t| 'fields'\n\t\t| 'predicate'\n\t\t| 'composition'\n\t\t| 'many'\n\t\t| 'variant'\n\t\t| 'value'\n\tinterface Entry {\n\t\tvalue: FilterTree\n\t\tdepth: number\n\t\tcontext: Context\n\t\tgrammar: FilterGrammar\n\t}\n\tconst pending: Entry[] = [{ value, depth: 0, context: 'expression', grammar }]\n\tlet nodes = 0\n\twhile (pending.length) {\n\t\tconst entry = pending.pop()!\n\t\tif (++nodes > MAX_FILTER_NODES || entry.depth > MAX_FILTER_DEPTH) return false\n\t\tif (Object(entry.value) !== entry.value) continue\n\t\t// SAFETY: boxing excludes null and primitives; descriptors below avoid invoking getters.\n\t\tconst object = entry.value as FilterTree[] | Record<string, FilterTree>\n\t\tconst array = Array.isArray(object) ? object : undefined\n\t\tconst prototype = Object.getPrototypeOf(object)\n\t\tif (\n\t\t\tarray ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null\n\t\t)\n\t\t\treturn false\n\t\tif (array && array.length + nodes + pending.length > MAX_FILTER_NODES) return false\n\t\tconst keys = Reflect.ownKeys(object)\n\t\tif (\n\t\t\tkeys.length - (array ? 1 : 0) + nodes + pending.length > MAX_FILTER_NODES ||\n\t\t\t(array && keys.length !== array.length + 1)\n\t\t)\n\t\t\treturn false\n\t\tlet consumed: string[]\n\t\tswitch (entry.context) {\n\t\t\tcase 'expression':\n\t\t\t\tconsumed = [\n\t\t\t\t\t...Object.keys(entry.grammar.fields),\n\t\t\t\t\t...Object.keys(entry.grammar.relations),\n\t\t\t\t\t'and',\n\t\t\t\t\t'or',\n\t\t\t\t\t'not',\n\t\t\t\t\t'fields',\n\t\t\t\t\t...(entry.grammar.variants ? ['variant'] : []),\n\t\t\t\t]\n\t\t\t\tbreak\n\t\t\tcase 'variant':\n\t\t\t\tconsumed = ['tag', 'filters']\n\t\t\t\tbreak\n\t\t\tcase 'fields':\n\t\t\t\tconsumed = Object.keys(entry.grammar.fields)\n\t\t\t\tbreak\n\t\t\tcase 'predicate':\n\t\t\t\tconsumed = ['eq', 'in', 'gt', 'gte', 'lt', 'lte', 'exists']\n\t\t\t\tbreak\n\t\t\tcase 'many':\n\t\t\t\tconsumed = ['some', 'none']\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tconsumed = []\n\t\t}\n\t\tfor (const key of consumed) if (!Object.hasOwn(object, key) && key in object) return false\n\t\tfor (const key of keys) {\n\t\t\tif (array && key === 'length') continue\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(object, key)!\n\t\t\tconst name = String(key)\n\t\t\tif (!descriptor.enumerable || !('value' in descriptor) || name !== key) return false\n\t\t\tif (array && (!/^(0|[1-9]\\d*)$/.test(name) || Number(name) >= array.length)) return false\n\t\t\tlet context: Context = 'value'\n\t\t\tlet depth = entry.depth\n\t\t\tlet target = entry.grammar\n\t\t\tif (entry.context === 'expression') {\n\t\t\t\tcontext = 'predicate'\n\t\t\t\tif (name === 'variant' && entry.grammar.variants) {\n\t\t\t\t\tcontext = 'variant'\n\t\t\t\t\tdepth++\n\t\t\t\t} else if (name === 'fields') context = 'fields'\n\t\t\t\telse if (name === 'and' || name === 'or') {\n\t\t\t\t\tcontext = 'composition'\n\t\t\t\t\tdepth++\n\t\t\t\t} else if (name === 'not') {\n\t\t\t\t\tcontext = 'expression'\n\t\t\t\t\tdepth++\n\t\t\t\t} else if (entry.grammar.relations[name]) {\n\t\t\t\t\tconst relation = entry.grammar.relations[name]\n\t\t\t\t\tcontext = relation.cardinality === 'one' ? 'expression' : 'many'\n\t\t\t\t\ttarget = relation.target\n\t\t\t\t\tdepth++\n\t\t\t\t}\n\t\t\t} else if (entry.context === 'variant' && name === 'filters') {\n\t\t\t\tconst tag = Object.getOwnPropertyDescriptor(object, 'tag')\n\t\t\t\tif (\n\t\t\t\t\t!tag ||\n\t\t\t\t\t!('value' in tag) ||\n\t\t\t\t\t!z.string().safeParse(tag.value).success ||\n\t\t\t\t\t!Object.hasOwn(entry.grammar.variants?.options ?? {}, tag.value)\n\t\t\t\t)\n\t\t\t\t\treturn false\n\t\t\t\tcontext = 'expression'\n\t\t\t\ttarget = entry.grammar.variants!.options[String(tag.value)]\n\t\t\t} else if (entry.context === 'fields') context = 'predicate'\n\t\t\telse if (entry.context === 'composition' || entry.context === 'many') context = 'expression'\n\t\t\tpending.push({ value: descriptor.value, depth, context, grammar: target })\n\t\t}\n\t}\n\treturn true\n}\n"],"mappings":"uLAMA,MAAaA,EAAgB,IAChBC,EAAmB,EACnBC,EAAmB,IACnBC,EAAuB,IAyF9BC,EAAgBC,EAAYC,CAAuB,EAC5CC,EAAmBH,EAAc,OAAO,CAAC,CAAC,OAAO,CAC7D,SAAUA,EAAc,MAAM,SAAS,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,IAAA,GAAiB,EACzE,gBAAiBA,EAAc,MAAM,gBAAgB,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,EACxF,iBAAkBA,EAAc,MAAM,iBAAiB,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,CAC3F,CAAC,EAqBD,SAAgBI,EAIf,EACA,EAC4C,CAC5C,IAAMC,EAA2B,CAAC,EAClC,IAAK,GAAM,CAACC,EAAKC,KAAa,OAAO,QAAQC,CAAS,EAAG,CACxD,GAAID,IAAa,IAAQ,CAAC,OAAO,OAAOE,EAAYH,CAAG,EACtD,MAAU,MAAM,oCAAoCA,EAAI,EAAE,EAE3D,EAAWA,GAAOI,EAAsBD,EAAWH,EAAI,CAAC,CAAC,SAAS,CACnE,CACA,IAAMK,EAAUC,EAAoBP,CAAU,EAO9C,OANeF,EAAiB,OAAO,CACtC,QAASQ,EAAQ,SAAS,EAC1B,MAAOE,EAAE,KAAK,CAAC,MAAO,MAAM,CAAC,CAAC,CAAC,SAAS,CACzC,CAGY,CACb,CAEA,SAAgBH,EAAsB,EAAwC,CAC7EI,EAAsBC,CAAS,EAC/B,IAAMC,EAASf,EAAYc,CAAS,CAAC,CAAC,YAAY,EAC5CE,EAAWJ,EAAE,OAAO,CACzB,GAAIG,EAAO,SAAS,EACpB,GAAIH,EAAE,MAAMG,CAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAA,GAAwB,CAAC,CAAC,SAAS,CAC/D,CAAC,EACKE,EAASC,EAAcJ,CAAS,EACnCE,EAAS,OAAO,CAChB,GAAID,EAAO,SAAS,EACpB,IAAKA,EAAO,SAAS,EACrB,GAAIA,EAAO,SAAS,EACpB,IAAKA,EAAO,SAAS,CACtB,CAAC,EACAC,EAGGG,GADLL,EAAU,aAAe,WAAaG,EAAO,OAAO,CAAE,OAAQL,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAE,CAAC,EAAIK,EAAA,CAEzF,OAAO,CAAC,CACR,OACC,GAAU,OAAO,OAAOG,CAAK,CAAC,CAAC,KAAM,GAAUC,IAAU,IAAA,EAAS,EACnE,mCACD,EACD,OAAOT,EAAE,MAAM,CAACG,EAAQI,CAAQ,CAAC,CAClC,CAeA,MAAMG,kBAAqB,IAAI,QAIzBC,kBAAoB,IAAI,QACxBC,kBAAiB,IAAI,QAErBC,EAAsBb,EAAE,KAAK,aAA0B,uBAAwB,EAAM,IAAQ,CAClGA,EAAE,UAAU,KAAKc,EAAMC,CAAG,EAC1B,IAAMC,EAAQF,EAAK,KAAK,MAClBG,EAAYF,EAAI,MAAM,OACtBG,EAAkBD,aAAqBjB,EAAE,YAAciB,EAAU,OAAO,EAAI,IAAA,GAC5EE,EACLD,aAA2BlB,EAAE,UAC1B,OAAO,KAAKkB,EAAgB,KAAK,EACjC,OAAO,KAAKH,EAAI,KAAK,EACnBK,EAAyB,CAC9B,SAAUT,EAAkB,IAAII,EAAI,MAAM,OAAO,EACjD,OAAQ,OAAO,YAAYI,EAAa,IAAK,GAAQ,CAAC1B,EAAKsB,EAAI,MAAMtB,EAAI,CAAC,CAAC,EAC3E,UAAW,OAAO,YACjB,OAAO,QAAQsB,EAAI,KAAK,CAAC,CAAC,SAAS,CAACtB,EAAK4B,KAAY,CACpD,IAAMC,EAAWZ,EAAmB,IAAIW,CAAM,EAC9C,OAAOC,EAAW,CAAC,CAAC7B,EAAK6B,CAAQ,CAAC,EAAI,CAAC,CACxC,CAAC,CACF,CACD,EAGA,EAAK,KAAK,OAAS,EAAS,IAEtBC,EAAmBC,EAAQ,MAAqBJ,CAAO,EAQrDJ,EAAMQ,EAASC,CAAG,GAPxBD,EAAQ,OAAO,KAAK,CACnB,KAAM,SACN,MAAOA,EAAQ,MACf,QAAS,yDACV,CAAC,EACMA,EAIV,CAAC,EAWD,SAAgBE,EACf,EACA,kBAAQ,IAAI,QACA,CACZ,IAAML,EAAS,IAAIR,EAAoBc,EAAiBP,EAAS,EAAGQ,CAAK,CAAC,CAAC,KAAK,GAAG,EAEnF,OADAhB,EAAe,IAAIS,EAAQD,CAAO,EAC3BC,CACR,CA6CA,SAAgBQ,EACf,EACA,EACA,EACA,EAAwC,CAAC,EACzC,EACkB,CAClB,OAAOC,EAAgBT,EAAQU,EAAOC,EAASC,EAAYC,CAAK,CAAC,CAAC,QACnE,CAGA,SAAgBJ,EACf,EACA,EACA,EACA,EAAwC,CAAC,EACzC,EACC,CACD,IAAMK,EAAOd,EAAO,MAAMU,CAAK,EAEzBK,EAASD,EAETE,EACLH,IAAU,IAAA,GACP,IAAA,GACCb,EAAO,MAAM,CAAE,GAAGe,EAAQ,QAASF,CAAM,CAAC,EACzCd,EAAUkB,EAAkBjB,CAAM,EAClCkB,EAAsB,CAC3B,KAAM,MACN,SAAU,CACTC,EAAiBJ,EAAO,QAAShB,CAAO,EACxCoB,EAAiBH,GAAQ,QAASjB,CAAO,CAC1C,CACD,EACMqB,EAAaC,EAAmBH,CAAQ,EACxCI,EAAS,CAAC,GAAGX,CAAO,CAAC,CAAC,MAC1B,EAAG,IACHY,EAAE,KAAK,cAAcC,EAAE,IAAI,GAAKD,EAAE,OAAO,KAAK,IAAI,CAAC,CAAC,cAAcC,EAAE,OAAO,KAAK,IAAI,CAAC,CACvF,EACMC,EAAYb,EAAW,KAAM,GAAUxB,EAAM,QAAU,MAAQA,EAAM,QAAU,IAAA,EAAS,EAC1FsC,EAA2B,KAC/B,GAAID,EAEH,IADA,EAAQH,EAAO,KAAM,GAAUlC,EAAM,OAASqC,EAAU,KAAK,GAAK,KAC9D,CAACC,GAASC,EAAeD,EAAON,EAAW,MAAM,CAAC,CAAC,SAAW,EACjE,MAAU,MAAM,mBAAmBK,EAAU,MAAM,0CAA0C,CAAA,KACxF,CACN,IAAIG,EAAS,EACb,IAAK,IAAMC,KAAaP,EAAQ,CAC/B,IAAMQ,EAASH,EAAeE,EAAWT,EAAW,MAAM,CAAC,CAAC,OACxDU,EAASF,IACZ,EAAQC,EACR,EAASC,EAEX,CACD,CAQA,MAAO,CAAE,OAAM,SAAA,CANd,WACA,SAAUV,EAAW,OACrB,cAAeA,EAAW,WAC1B,QACA,OAAQM,EAAQC,EAAeD,EAAON,EAAW,MAAM,EAAI,CAAC,CAEvC,CAAE,CACzB,CACA,SAASH,EAAkB,EAA8C,CACxE,GAAI,EAAEjB,aAAkBrB,EAAE,WAAY,OACtC,IAAMF,EAAUuB,EAAO,MAAM,QAC7B,GAAI,EAAEvB,aAAmBE,EAAE,aAAc,OACzC,IAAMoD,EAAOtD,EAAQ,OAAO,EACtBuD,EAASzC,EAAe,IAAIwC,CAAI,EACtC,GAAIC,EAAQ,OAAOA,EACnB,GAAMD,aAAgBpD,EAAE,UACxB,MAAO,CACN,OAAQoD,EAAK,MACb,SAAUzC,EAAkB,IAAIyC,EAAK,MAAM,OAAO,EAClD,UAAW,OAAO,YACjB,OAAO,QAAQA,EAAK,KAAK,CAAC,CAAC,SAAS,CAACE,EAAMC,KAAe,CAEzD,IAAMjC,EAAWZ,EAAmB,IAAI6C,CAA4B,EACpE,OAAOjC,EAAW,CAAC,CAACgC,EAAMhC,CAAQ,CAAC,EAAI,CAAC,CACzC,CAAC,CACF,CACD,CACD,CACA,SAASkB,EAAiB,EAAmB,EAAoC,CAChF,IAAMgB,EAAwB,CAAC,EAC/B,GAAI,CAAChD,EAAO,MAAO,CAAE,KAAM,MAAO,UAAS,EAE3C,IAAMiD,EAAajD,EACnB,IAAK,GAAM,CAAC8C,EAAMC,KAAc,OAAO,QAAQE,CAAU,EACxD,GAAIF,IAAc,IAAA,GAClB,IAAID,IAAS,OAASA,IAAS,KAE9BE,EAAS,KAAK,CACb,KAAMF,EACN,SAAWC,EAA2B,IAAK,GAAUf,EAAiBkB,EAAOtC,CAAO,CAAC,CACtF,CAAC,OACK,GAAIkC,IAAS,WAAalC,GAAS,SAAU,CAEnD,IAAMuC,EAASJ,EACfC,EAAS,KAAK,CACb,KAAM,UACN,cAAepC,EAAQ,SAAS,cAChC,IAAKuC,EAAO,IACZ,MAAOnB,EAAiBmB,EAAO,QAASvC,EAAQ,SAAS,QAAQuC,EAAO,IAAI,CAC7E,CAAC,CACF,MAAO,GAAIL,IAAS,MACnBE,EAAS,KAAK,CAAE,KAAM,MAAO,MAAOhB,EAAiBe,EAAWnC,CAAO,CAAE,CAAC,OACtE,GAAIkC,IAAS,SAEjB,IAAK,GAAM,CAACM,EAAOzD,KAAW,OAAO,QAAQoD,CAAuC,EACnFC,EAAS,KAAK,GAAGK,EAAgBD,EAAOzD,CAAM,CAAC,OAC1C,GAAIiB,GAAS,UAAUkC,GAAO,CACpC,IAAMhC,EAAWF,EAAQ,UAAUkC,GACnC,GAAIhC,EAAS,cAAgB,MAC5BkC,EAAS,KAAK,CACb,KAAM,WACN,OACA,KAAM,MACN,UAAWD,IAAc,KAAO,KAAOf,EAAiBe,EAAWjC,EAAS,MAAM,CACnF,CAAC,MACG,CAEJ,IAAMwC,EAASP,EACfC,EAAS,KAAK,CACb,KAAM,WACN,OACA,KAAMM,EAAO,OAAS,IAAA,GAAY,OAAS,OAC3C,UAAWtB,EAAiBsB,EAAO,MAAQA,EAAO,KAAMxC,EAAS,MAAM,CACxE,CAAC,CACF,CACD,MAAOkC,EAAS,KAAK,GAAGK,EAAgBP,EAAMC,CAAS,CAAC,CAAA,CAEzD,MAAO,CAAE,KAAM,MAAO,UAAS,CAChC,CACA,SAASM,EAAgB,EAAe,EAAoC,CAO3E,OANIN,IAAc,IAAA,GAAkB,CAAC,EACjC,OAAOA,CAAS,IAAMA,EAKnB,OAAO,QAAQA,CAAkE,CAAC,CACvF,QAAQ,EAAG/C,KAAWA,IAAU,IAAA,EAAS,CAAC,CAC1C,KAAK,CAACuD,EAAUvD,MAAY,CAC5B,KAAM,SACN,QACU,WACV,OACD,EAAE,EAVK,CAAC,CAAE,KAAM,SAAU,QAAO,SAAU,KAAM,MAAO+C,CAA0B,CAAC,CAWrF,CACA,SAASS,EAAW,EAAoB,EAA8B,CACrE,OAAOC,EAAcC,EAAMC,CAAK,IAAM,CACvC,CACA,SAASzB,EAAmB,EAA+B,CAC1D,IAAM0B,kBAAS,IAAI,IACnB,GAAIC,EAAI,OAAS,UAChB,OAAO3B,EAAmB,CACzB,KAAM,MACN,SAAU,CACT,CAAE,KAAM,SAAU,MAAO2B,EAAI,cAAe,SAAU,KAAM,MAAOA,EAAI,GAAI,EAC3EA,EAAI,KACL,CACD,CAAC,EACF,GAAIA,EAAI,OAAS,SAAU,CACtBA,EAAI,WAAa,MAAQ,CAAC,MAAM,QAAQA,EAAI,KAAK,GAAGD,EAAO,IAAIC,EAAI,MAAOA,EAAI,KAAK,EACvF,IAAMC,EAAU,MAAM,QAAQD,EAAI,KAAK,EAAIA,EAAI,MAAQ,IAAA,GAGvD,OAFIA,EAAI,WAAa,MAAQC,GAAWA,EAAQ,MAAO,GAAUN,EAAWxD,EAAO8D,EAAQ,EAAE,CAAC,GAC7FF,EAAO,IAAIC,EAAI,MAAOC,EAAQ,EAAE,EAC1B,CAAE,SAAQ,WAAY,EAAM,CACpC,CACA,GAAID,EAAI,OAAS,OAASA,EAAI,OAAS,WAAY,MAAO,CAAE,SAAQ,WAAY,EAAM,EACtF,IAAME,EAAWF,EAAI,SAAS,IAAI3B,CAAkB,EACpD,GAAI2B,EAAI,OAAS,KAAM,CACtB,IAAMG,EAASD,EAAS,OAAQ,GAAW,CAACE,EAAO,UAAU,EAC7D,IAAK,GAAM,CAACb,EAAOpD,KAAUgE,EAAO,EAAE,EAAE,QAAU,CAAC,EAEjDA,EAAO,MACL,GAAWC,EAAO,OAAO,IAAIb,CAAK,GAAKI,EAAWS,EAAO,OAAO,IAAIb,CAAK,EAAIpD,CAAK,CACpF,GAEA4D,EAAO,IAAIR,EAAOpD,CAAK,EACzB,MAAO,CAAE,SAAQ,WAAYgE,EAAO,MAAO,GAAWC,EAAO,UAAU,CAAE,CAC1E,CACA,IAAIC,EAAaH,EAAS,KAAM,GAAWE,EAAO,UAAU,EAC5D,IAAK,IAAMA,KAAUF,EACpB,IAAK,GAAM,CAACX,EAAOpD,KAAUiE,EAAO,OAC/BL,EAAO,IAAIR,CAAK,GAAK,CAACI,EAAWI,EAAO,IAAIR,CAAK,EAAIpD,CAAK,IAAG,EAAa,IAC9E4D,EAAO,IAAIR,EAAOpD,CAAK,EAEzB,MAAO,CAAE,SAAQ,YAAW,CAC7B,CACA,SAASwC,EACR,EACA,EAC2C,CAC3C,IAAM2B,EAAmD,CAAC,EAC1D,IAAK,IAAMf,KAASb,EAAM,OAAQ,CACjC,GAAI,CAAC6B,EAAS,IAAIhB,CAAK,EAAG,MAC1Be,EAAO,KAAK,CAAE,QAAO,MAAOC,EAAS,IAAIhB,CAAK,CAAG,CAAC,CACnD,CACA,OAAOe,CACR,CAEA,SAAS1E,EAAsB,EAAmC,CACjE,OAAQC,EAAU,KAAlB,CACC,IAAK,KACL,IAAK,SACL,IAAK,UACL,IAAK,QACL,IAAK,UACL,IAAK,OACL,IAAK,UACJ,OACD,IAAK,QACJ,IAAK,IAAM2E,KAAU3E,EAAU,QAC9BD,EAAsB4E,CAAM,EAE7B,OACD,QACC,MAAU,MAAM,mCAAmC3E,EAAU,KAAK,EAAE,CACtE,CACD,CAEA,SAASI,EAAc,EAAsC,CAC5D,OAAQJ,EAAU,KAAlB,CACC,IAAK,SACL,IAAK,UACL,IAAK,QACJ,MAAO,GACR,IAAK,UACJ,OAAOF,EAAE,MAAM,CAACA,EAAE,OAAO,EAAGA,EAAE,OAAO,EAAGA,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,UAAUE,EAAU,KAAK,CAAC,CAAC,QACjF,IAAK,QACJ,OAAOA,EAAU,QAAQ,MAAMI,CAAa,EAC7C,QACC,MAAO,EACT,CACD,CAEA,SAASP,EAAoB,EAAqC,CACjE,OAAO2B,EAAqB,CAAE,OAAQlC,EAAY,UAAW,CAAC,CAAE,CAAC,CAClE,CAEA,SAASmC,EACR,EACA,EACA,kBAAQ,IAAI,QACE,CACd,IAAM0B,EAASzB,EAAM,IAAIR,CAAO,CAAC,EAAE,IAAI0D,CAAK,EAC5C,GAAIzB,EAAQ,OAAOA,EACnB,IAAM7D,EAAa4B,EAAQ,OACrB2D,EAAS,OAAO,YACrB,OAAO,QAAQvF,CAAU,CAAC,CAAC,QACzB,CAACC,KACD,CAAC,CAAC,MAAO,KAAM,MAAO,SAAU,GAAI2B,EAAQ,SAAW,CAAC,SAAS,EAAI,CAAC,CAAE,CAAC,CAAC,SAAS3B,CAAG,CACxF,CACD,EACMuF,EAA0B,CAAC,EACjC,IAAK,GAAM,CAAC1B,EAAMhC,KAAa,OAAO,QAAQF,EAAQ,SAAS,EAAG,CACjE,GAAI0D,IAAA,EAA4B,SAChC,IAAMhB,EAASnC,EAAiBL,EAAS,OAAQwD,EAAQ,EAAGlD,CAAK,EACjE,EAAU0B,IACThC,EAAS,cAAgB,MACtBwC,EAAO,SAAS,EAChB9D,EAAE,MAAM,CAACA,EAAE,OAAO,CAAE,KAAM8D,CAAO,CAAC,CAAC,CAAC,OAAO,EAAG9D,EAAE,OAAO,CAAE,KAAM8D,CAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAAA,CACpF,SAAS,EACXpD,EAAmB,IAAIsE,EAAU1B,GAAOhC,CAAQ,CACjD,CACA,GAAIF,EAAQ,UAAY0D,EAAA,EAA0B,CACjD,IAAMG,EAAU,OAAO,QAAQ7D,EAAQ,SAAS,OAAO,CAAC,CAAC,KAAK,CAAC8D,EAAKT,KACnEzE,EAAE,aAAa,CAAE,IAAKA,EAAE,QAAQkF,CAAG,EAAG,QAASvD,EAAiB8C,EAAQK,EAAQ,EAAGlD,CAAK,CAAE,CAAC,CAC5F,EACIqD,EAAQ,SACX,EAAU,QAAUjF,EAAE,MAAMiF,CAAO,CAAC,CAAC,SAAS,EAC9CtE,EAAkB,IAAIqE,EAAU,QAAS5D,EAAQ,QAAQ,EAE3D,CACA,IAAM+D,EAAOnF,EACX,OAAO,CAAE,GAAG+E,EAAQ,GAAGC,EAAW,OAAQhF,EAAE,OAAOR,CAAU,CAAC,CAAC,OAAO,CAAC,CAAC,SAAS,CAAE,CAAC,CAAC,CACrF,OAAO,EACHkE,EAAQoB,IAAA,EAA6B,IAAA,GAAYnD,EAAiBP,EAAS0D,EAAQ,EAAGlD,CAAK,EAC3FwD,EAAS1B,EACZyB,EAAK,OAAO,CACZ,IAAKnF,EAAE,MAAM0D,CAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAA,GAAoB,CAAC,CAAC,SAAS,EAC1D,GAAI1D,EAAE,MAAM0D,CAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAA,GAAoB,CAAC,CAAC,SAAS,EACzD,IAAKA,EAAM,SAAS,CACrB,CAAC,EACAyB,EACCE,EAASzD,EAAM,IAAIR,CAAO,EAM9B,OALKiE,IACJ,kBAAS,IAAI,IACbzD,EAAM,IAAIR,EAASiE,CAAM,GAE1BA,EAAO,IAAIP,EAAOM,CAAM,EACjBA,CACR,CAEA,SAAS7D,EAAmB,EAAmB,EAAiC,CAe/E,IAAM+D,EAAmB,CAAC,CAAE,QAAO,MAAO,EAAG,QAAS,aAAc,SAAQ,CAAC,EACzEC,EAAQ,EACZ,KAAOD,EAAQ,QAAQ,CACtB,IAAM7E,EAAQ6E,EAAQ,IAAI,EAC1B,GAAI,EAAEC,EAAA,KAA4B9E,EAAM,MAAA,EAA0B,MAAO,GACzE,GAAI,OAAOA,EAAM,KAAK,IAAMA,EAAM,MAAO,SAEzC,IAAM+E,EAAS/E,EAAM,MACfgF,EAAQ,MAAM,QAAQD,CAAM,EAAIA,EAAS,IAAA,GACzCE,EAAY,OAAO,eAAeF,CAAM,EAK9C,IAHCC,EAAQC,IAAc,MAAM,UAAYA,IAAc,OAAO,WAAaA,IAAc,OAGrFD,GAASA,EAAM,OAASF,EAAQD,EAAQ,OAAA,IAA2B,MAAO,GAC9E,IAAMK,EAAO,QAAQ,QAAQH,CAAM,EACnC,GACCG,EAAK,OAAU,KAAiBJ,EAAQD,EAAQ,OAAA,KAC/CG,GAASE,EAAK,SAAWF,EAAM,OAAS,EAEzC,MAAO,GACR,IAAIG,EACJ,OAAQnF,EAAM,QAAd,CACC,IAAK,aACJ,EAAW,CACV,GAAG,OAAO,KAAKA,EAAM,QAAQ,MAAM,EACnC,GAAG,OAAO,KAAKA,EAAM,QAAQ,SAAS,EACtC,MACA,KACA,MACA,SACA,GAAIA,EAAM,QAAQ,SAAW,CAAC,SAAS,EAAI,CAAC,CAC7C,EACA,MACD,IAAK,UACJ,EAAW,CAAC,MAAO,SAAS,EAC5B,MACD,IAAK,SACJ,EAAW,OAAO,KAAKA,EAAM,QAAQ,MAAM,EAC3C,MACD,IAAK,YACJ,EAAW,CAAC,KAAM,KAAM,KAAM,MAAO,KAAM,MAAO,QAAQ,EAC1D,MACD,IAAK,OACJ,EAAW,CAAC,OAAQ,MAAM,EAC1B,MACD,QACC,EAAW,CAAC,CACd,CACA,IAAK,IAAMhB,KAAOmG,EAAU,GAAI,CAAC,OAAO,OAAOJ,EAAQ/F,CAAG,GAAKA,KAAO+F,EAAQ,MAAO,GACrF,IAAK,IAAM/F,KAAOkG,EAAM,CACvB,GAAIF,GAAShG,IAAQ,SAAU,SAC/B,IAAMoG,EAAa,OAAO,yBAAyBL,EAAQ/F,CAAG,EACxD6D,EAAO,OAAO7D,CAAG,EAEvB,GADI,CAACoG,EAAW,YAAc,EAAE,UAAWA,IAAevC,IAAS7D,GAC/DgG,IAAU,CAAC,iBAAiB,KAAKnC,CAAI,GAAK,OAAOA,CAAI,GAAKmC,EAAM,QAAS,MAAO,GACpF,IAAIK,EAAmB,QACnBhB,EAAQrE,EAAM,MACdqD,EAASrD,EAAM,QACnB,GAAIA,EAAM,UAAY,aAAc,CAEnC,GADA,EAAU,YACN6C,IAAS,WAAa7C,EAAM,QAAQ,SACvC,EAAU,UACV,SACM,GAAI6C,IAAS,SAAU,EAAU,cACnC,GAAIA,IAAS,OAASA,IAAS,KACnC,EAAU,cACV,SACM,GAAIA,IAAS,MACnB,EAAU,aACV,SACM,GAAI7C,EAAM,QAAQ,UAAU6C,GAAO,CACzC,IAAMhC,EAAWb,EAAM,QAAQ,UAAU6C,GACzC,EAAUhC,EAAS,cAAgB,MAAQ,aAAe,OAC1D,EAASA,EAAS,OAClB,GACD,CACD,MAAO,GAAIb,EAAM,UAAY,WAAa6C,IAAS,UAAW,CAC7D,IAAM4B,EAAM,OAAO,yBAAyBM,EAAQ,KAAK,EACzD,GACC,CAACN,GACD,EAAE,UAAWA,IACb,CAAClF,EAAE,OAAO,CAAC,CAAC,UAAUkF,EAAI,KAAK,CAAC,CAAC,SACjC,CAAC,OAAO,OAAOzE,EAAM,QAAQ,UAAU,SAAW,CAAC,EAAGyE,EAAI,KAAK,EAE/D,MAAO,GACR,EAAU,aACV,EAASzE,EAAM,QAAQ,SAAU,QAAQ,OAAOyE,EAAI,KAAK,EAC1D,MAAWzE,EAAM,UAAY,SAAU,EAAU,aACxCA,EAAM,UAAY,eAAiBA,EAAM,UAAY,UAAQ,EAAU,cAChF6E,EAAQ,KAAK,CAAE,MAAOO,EAAW,MAAO,QAAO,UAAS,QAAS/B,CAAO,CAAC,CAC1E,CACD,CACA,MAAO,EACR"}
|
|
@@ -8,18 +8,9 @@ export type QueryInput = z.input<ReturnType<ReturnType<typeof getRelationExecuto
|
|
|
8
8
|
export type StoredConstraint = NonNullable<QueryInput['filters']>;
|
|
9
9
|
export declare function createPlanner(schema: SchemaDefinition<GenericSchema, boolean>, relations: RelationRegistryIdentity): {
|
|
10
10
|
metadata: RelationMetadata;
|
|
11
|
-
|
|
12
|
-
page: z.ZodArray<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
13
|
-
isDone: z.ZodBoolean;
|
|
14
|
-
continueCursor: z.ZodString;
|
|
15
|
-
splitCursor: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
16
|
-
pageStatus: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
|
|
17
|
-
SplitRecommended: "SplitRecommended";
|
|
18
|
-
SplitRequired: "SplitRequired";
|
|
19
|
-
}>>>;
|
|
20
|
-
}, z.core.$strip>;
|
|
11
|
+
select: (table: string, selection: SelectionMask) => void;
|
|
21
12
|
validateDocument: (table: string, row: Record<string, import('convex/values').Value>) => void;
|
|
22
|
-
|
|
13
|
+
streamSchema: SchemaDefinition<GenericSchema, boolean>;
|
|
23
14
|
beforeLeaf(table: string): void;
|
|
24
15
|
complete(table: string, document: Record<string, Value>, permitted: boolean, residual: FilterAst): Record<string, unknown> | null;
|
|
25
16
|
plan(table: string, input: QueryInput, conditions: readonly IndexCondition[], constraints: readonly StoredConstraint[]): {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{parseFilterPlan as e}from"./filters.mjs";import{getRelationExecutor as t,getRelationMetadata as n}from"../relations.mjs";import{compareValues as r}from"convex/values";function i(r,i){let o=t(i);o.assertSchema(r.tables);let s=n(i),c=/* @__PURE__ */ new Map;return{metadata:s,select:o.select,validateDocument:o.validateDocument,streamSchema:o.streamSchema,beforeLeaf(e){if(!Object.hasOwn(s.tables,e))throw Error(`Unknown query table`)},complete(e,t,n,r){return o.project(e,t,()=>n&&a(r,t))},plan(t,n,r,i){if(!Object.hasOwn(s.tables,t))throw Error(`Unknown query table`);let a=c.get(t);a||(a=o.pagination(t),c.set(t,a));let l=i.length===1?i[0]:i.length?{and:i}:void 0,{args:u,compiled:d}=e(a,n,s.tables[t].indexes,r,l);if(d.residual.kind!==`and`)throw Error(`Invalid compiled filter root`);let[f,p]=d.residual.children,m={kind:`and`,children:[]},h={kind:`and`,children:[f,m]},g={kind:`and`,children:[p,m]};return{...d,args:u,clientIdentity:h,serverIdentity:g}}}}function a(e,t){switch(e.kind){case`and`:return e.children.every(e=>a(e,t));case`or`:return e.children.some(e=>a(e,t));case`variant`:return t[e.discriminator]===e.tag&&a(e.child,t);case`not`:return!a(e.child,t);case`relation`:throw Error(`Relation execution requires U6`);case`scalar`:{let n=t[e.field];if(e.operator===`exists`)return n!==void 0===e.value;if(e.operator===`in`)return Array.isArray(e.value)&&e.value.some(e=>r(n,e)===0);if(Array.isArray(e.value))throw Error(`Invalid scalar predicate`);let i=r(n,e.value);switch(e.operator){case`eq`:return i===0;case`gt`:return i>0;case`gte`:return i>=0;case`lt`:return i<0;case`lte`:return i<=0}}}}export{i as createPlanner,a as scalarMatches};
|
|
2
2
|
//# sourceMappingURL=planner.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"planner.mjs","names":["createPlanner","owned","getRelationExecutor","relations","
|
|
1
|
+
{"version":3,"file":"planner.mjs","names":["createPlanner","owned","getRelationExecutor","relations","schema","metadata","getRelationMetadata","inputs","table","document","permitted","scalarMatches","residual","validator","where","constraints","parseFilterPlan","input","conditions","compiled","client","server","empty","clientIdentity","serverIdentity","ast","child","actual","compareValues","value","compared"],"sources":["../../src/querying/planner.ts"],"sourcesContent":["import type { z } from 'zod'\nimport type { SchemaDefinition, GenericSchema } from 'convex/server'\nimport { compareValues, type Value } from 'convex/values'\nimport {\n\tgetRelationExecutor,\n\tgetRelationMetadata,\n\ttype RelationRegistryIdentity,\n} from '../relations'\nimport { parseFilterPlan, type FilterAst, type IndexCondition } from './filters'\n\nexport type QueryInput = z.input<ReturnType<ReturnType<typeof getRelationExecutor>['pagination']>>\nexport type StoredConstraint = NonNullable<QueryInput['filters']>\n\nexport function createPlanner(\n\tschema: SchemaDefinition<GenericSchema, boolean>,\n\trelations: RelationRegistryIdentity,\n) {\n\tconst owned = getRelationExecutor(relations)\n\towned.assertSchema(schema.tables)\n\tconst metadata = getRelationMetadata(relations)\n\t// Build each immutable filter grammar only when that table is queried. Engine\n\t// declarations also load in mutations, which never need pagination validators.\n\tconst inputs = new Map<string, ReturnType<typeof owned.pagination>>()\n\treturn {\n\t\tmetadata,\n\t\tselect: owned.select,\n\t\tvalidateDocument: owned.validateDocument,\n\t\tstreamSchema: owned.streamSchema,\n\t\tbeforeLeaf(table: string) {\n\t\t\tif (!Object.hasOwn(metadata.tables, table)) throw new Error('Unknown query table')\n\t\t},\n\t\tcomplete(\n\t\t\ttable: string,\n\t\t\tdocument: Record<string, Value>,\n\t\t\tpermitted: boolean,\n\t\t\tresidual: FilterAst,\n\t\t) {\n\t\t\treturn owned.project(table, document, () => permitted && scalarMatches(residual, document))\n\t\t},\n\t\tplan(\n\t\t\ttable: string,\n\t\t\tinput: QueryInput,\n\t\t\tconditions: readonly IndexCondition[],\n\t\t\tconstraints: readonly StoredConstraint[],\n\t\t) {\n\t\t\tif (!Object.hasOwn(metadata.tables, table)) throw new Error('Unknown query table')\n\t\t\tlet validator = inputs.get(table)\n\t\t\tif (!validator) {\n\t\t\t\tvalidator = owned.pagination(table)\n\t\t\t\tinputs.set(table, validator)\n\t\t\t}\n\t\t\t// Server constraints are independently validated against stored fields,\n\t\t\t// not the endpoint's public input/filter mask. Client values cannot replace them.\n\t\t\tconst where =\n\t\t\t\tconstraints.length === 1\n\t\t\t\t\t? constraints[0]\n\t\t\t\t\t: constraints.length\n\t\t\t\t\t\t? { and: constraints }\n\t\t\t\t\t\t: undefined\n\t\t\tconst { args, compiled } = parseFilterPlan(\n\t\t\t\tvalidator,\n\t\t\t\tinput,\n\t\t\t\tmetadata.tables[table].indexes,\n\t\t\t\tconditions,\n\t\t\t\twhere,\n\t\t\t)\n\t\t\tif (compiled.residual.kind !== 'and') throw new Error('Invalid compiled filter root')\n\t\t\tconst [client, server] = compiled.residual.children\n\t\t\tconst empty: FilterAst = { kind: 'and', children: [] }\n\t\t\tconst clientIdentity = { kind: 'and', children: [client, empty] }\n\t\t\tconst serverIdentity = { kind: 'and', children: [server, empty] }\n\t\t\t// SAFETY: normalized AST nodes contain only validated native scalar Values.\n\t\t\treturn {\n\t\t\t\t...compiled,\n\t\t\t\targs,\n\t\t\t\tclientIdentity: clientIdentity as Value,\n\t\t\t\tserverIdentity: serverIdentity as Value,\n\t\t\t}\n\t\t},\n\t}\n}\n\n/** Native scalar ordering/equality, including missing vs null, without coercion. */\nexport function scalarMatches(\n\tast: FilterAst,\n\tdocument: Record<string, Value | undefined>,\n): boolean {\n\tswitch (ast.kind) {\n\t\tcase 'and':\n\t\t\treturn ast.children.every((child) => scalarMatches(child, document))\n\t\tcase 'or':\n\t\t\treturn ast.children.some((child) => scalarMatches(child, document))\n\t\tcase 'variant':\n\t\t\treturn document[ast.discriminator] === ast.tag && scalarMatches(ast.child, document)\n\t\tcase 'not':\n\t\t\treturn !scalarMatches(ast.child, document)\n\t\tcase 'relation':\n\t\t\tthrow new Error('Relation execution requires U6')\n\t\tcase 'scalar': {\n\t\t\tconst actual = document[ast.field]\n\t\t\tif (ast.operator === 'exists') return (actual !== undefined) === ast.value\n\t\t\tif (ast.operator === 'in')\n\t\t\t\treturn (\n\t\t\t\t\tArray.isArray(ast.value) && ast.value.some((value) => compareValues(actual, value) === 0)\n\t\t\t\t)\n\t\t\tif (Array.isArray(ast.value)) throw new Error('Invalid scalar predicate')\n\t\t\tconst compared = compareValues(actual, ast.value)\n\t\t\tswitch (ast.operator) {\n\t\t\t\tcase 'eq':\n\t\t\t\t\treturn compared === 0\n\t\t\t\tcase 'gt':\n\t\t\t\t\treturn compared > 0\n\t\t\t\tcase 'gte':\n\t\t\t\t\treturn compared >= 0\n\t\t\t\tcase 'lt':\n\t\t\t\t\treturn compared < 0\n\t\t\t\tcase 'lte':\n\t\t\t\t\treturn compared <= 0\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":"8KAaA,SAAgBA,EACf,EACA,EACC,CACD,IAAMC,EAAQC,EAAoBC,CAAS,EAC3CF,EAAM,aAAaG,EAAO,MAAM,EAChC,IAAMC,EAAWC,EAAoBH,CAAS,EAGxCI,kBAAS,IAAI,IACnB,MAAO,CACN,WACA,OAAQN,EAAM,OACd,iBAAkBA,EAAM,iBACxB,aAAcA,EAAM,aACpB,WAAW,EAAe,CACzB,GAAI,CAAC,OAAO,OAAOI,EAAS,OAAQG,CAAK,EAAG,MAAU,MAAM,qBAAqB,CAClF,EACA,SACC,EACA,EACA,EACA,EACC,CACD,OAAOP,EAAM,QAAQO,EAAOC,MAAgBC,GAAaC,EAAcC,EAAUH,CAAQ,CAAC,CAC3F,EACA,KACC,EACA,EACA,EACA,EACC,CACD,GAAI,CAAC,OAAO,OAAOJ,EAAS,OAAQG,CAAK,EAAG,MAAU,MAAM,qBAAqB,EACjF,IAAIK,EAAYN,EAAO,IAAIC,CAAK,EAC3BK,IACJ,EAAYZ,EAAM,WAAWO,CAAK,EAClCD,EAAO,IAAIC,EAAOK,CAAS,GAI5B,IAAMC,EACLC,EAAY,SAAW,EACpBA,EAAY,GACZA,EAAY,OACX,CAAE,IAAKA,CAAY,EACnB,IAAA,GACC,CAAE,OAAM,YAAaC,EAC1BH,EACAI,EACAZ,EAAS,OAAOG,EAAM,CAAC,QACvBU,EACAJ,CACD,EACA,GAAIK,EAAS,SAAS,OAAS,MAAO,MAAU,MAAM,8BAA8B,EACpF,GAAM,CAACC,EAAQC,GAAUF,EAAS,SAAS,SACrCG,EAAmB,CAAE,KAAM,MAAO,SAAU,CAAC,CAAE,EAC/CC,EAAiB,CAAE,KAAM,MAAO,SAAU,CAACH,EAAQE,CAAK,CAAE,EAC1DE,EAAiB,CAAE,KAAM,MAAO,SAAU,CAACH,EAAQC,CAAK,CAAE,EAEhE,MAAO,CACN,GAAGH,EACH,OACgB,iBACA,gBACjB,CACD,CACD,CACD,CAGA,SAAgBR,EACf,EACA,EACU,CACV,OAAQc,EAAI,KAAZ,CACC,IAAK,MACJ,OAAOA,EAAI,SAAS,MAAO,GAAUd,EAAce,EAAOjB,CAAQ,CAAC,EACpE,IAAK,KACJ,OAAOgB,EAAI,SAAS,KAAM,GAAUd,EAAce,EAAOjB,CAAQ,CAAC,EACnE,IAAK,UACJ,OAAOA,EAASgB,EAAI,iBAAmBA,EAAI,KAAOd,EAAcc,EAAI,MAAOhB,CAAQ,EACpF,IAAK,MACJ,MAAO,CAACE,EAAcc,EAAI,MAAOhB,CAAQ,EAC1C,IAAK,WACJ,MAAU,MAAM,gCAAgC,EACjD,IAAK,SAAU,CACd,IAAMkB,EAASlB,EAASgB,EAAI,OAC5B,GAAIA,EAAI,WAAa,SAAU,OAAQE,IAAW,IAAA,KAAeF,EAAI,MACrE,GAAIA,EAAI,WAAa,KACpB,OACC,MAAM,QAAQA,EAAI,KAAK,GAAKA,EAAI,MAAM,KAAM,GAAUG,EAAcD,EAAQE,CAAK,IAAM,CAAC,EAE1F,GAAI,MAAM,QAAQJ,EAAI,KAAK,EAAG,MAAU,MAAM,0BAA0B,EACxE,IAAMK,EAAWF,EAAcD,EAAQF,EAAI,KAAK,EAChD,OAAQA,EAAI,SAAZ,CACC,IAAK,KACJ,OAAOK,IAAa,EACrB,IAAK,KACJ,OAAOA,EAAW,EACnB,IAAK,MACJ,OAAOA,GAAY,EACpB,IAAK,KACJ,OAAOA,EAAW,EACnB,IAAK,MACJ,OAAOA,GAAY,CACrB,CACD,CACD,CACD"}
|
|
@@ -29,11 +29,11 @@ export declare function relationshipExecutor(schema: SchemaDefinition<GenericSch
|
|
|
29
29
|
order?: 'asc' | 'desc';
|
|
30
30
|
}, path?: readonly string[], depth?: number, parents?: readonly string[]) => Promise<Record<string, Value>>;
|
|
31
31
|
continuation: (rootTable: string, rootId: string, path: readonly string[], selection: OutputSelection, input: QueryInput, conditions: readonly IndexCondition[], constraints: readonly StoredConstraint[]) => Promise<{
|
|
32
|
-
page:
|
|
32
|
+
page: Record<string, Value>[];
|
|
33
33
|
isDone: boolean;
|
|
34
|
+
splitCursor?: import("convex/server").Cursor | null;
|
|
35
|
+
pageStatus?: "SplitRecommended" | "SplitRequired" | null;
|
|
34
36
|
continueCursor: string;
|
|
35
|
-
splitCursor?: string | null | undefined;
|
|
36
|
-
pageStatus?: "SplitRecommended" | "SplitRequired" | null | undefined;
|
|
37
37
|
}>;
|
|
38
38
|
edge: (table: string, name: string) => Edge;
|
|
39
39
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{scalarMatches as e}from"./planner.mjs";import{CursorError as t}from"./cursors.mjs";import{executeStream as n,rethrowQueryError as r}from"./execution.mjs";import{z as i}from"zod";import{EmptyStream as a,getIndexFields as o,stream as s}from"convex-helpers/server/stream";
|
|
1
|
+
import{scalarMatches as e}from"./planner.mjs";import{CursorError as t}from"./cursors.mjs";import{executeStream as n,rethrowQueryError as r}from"./execution.mjs";import{z as i}from"zod";import{EmptyStream as a,getIndexFields as o,stream as s}from"convex-helpers/server/stream";const c=i.object({cursor:i.string(),ancestors:i.array(i.string()).max(3)}).strict();var l=class extends Error{code;constructor(e){super(e),this.code=e,this.name=`RelationshipError`}};function u(e,t){for(let n of Object.values(e)){if(t>=4)throw new l(`INVALID_RELATION`);n!==!0&&u(n,t+1)}}function d(d,p,m,h,g){let _=m.metadata,v=e=>`base`in e?e.base:e;function y(e,t){let n=_.declarations[e]?.[t];if(!n)throw new l(`INVALID_RELATION`);return n}let b=(e,t)=>!t||e[t.discriminator]===t.tag,x=i.string();function S(e,t){if(!t)return!0;let n=x.safeParse(e);return n.success&&p.db.normalizeId(t.table,n.data)!==null}function C(e,t){if(!t||!b(t,e.sourceGuard))return;let n=t[v(e.from).field];return S(n,e.sourceMember)&&S(n,e.targetMember)?n:void 0}function w(e,t,n){let r=t===`_id`?{name:`by_id`,fields:[`_id`]}:_.tables[e].indexes.find(e=>{let r=e.fields.indexOf(t);return r>=0&&e.fields.slice(0,r).every(e=>e===n?.discriminator)});if(!r)throw new l(`INVALID_RELATION`);return r}function T(e,t,n,r,i){let a=t===`_id`?[{name:`by_id`,fields:[`_id`]}]:_.tables[e].indexes,o=e=>e===t||e===i?.discriminator||n.bindings.has(e),s=e=>e.fields.includes(t)&&e.fields.slice(0,e.fields.indexOf(t)).every(o),c=e=>{let t=0;for(let n of e.fields){if(!o(n))break;t++}return t},u=r.find(e=>e.value!==null&&e.value!==void 0);if(u){let e=a.find(e=>e.name===u.index&&s(e));if(!e)throw Error(`Explicit index "${u.index}" is incompatible with declared relationship lookup`);return e}let d=a.filter(s).sort((e,t)=>c(t)-c(e)||e.name.localeCompare(t.name)||e.fields.join(`\0`).localeCompare(t.fields.join(`\0`)));if(!d.length)throw new l(`INVALID_RELATION`);return d[0]}function E(e,t,n,r,i){let a=[];for(let o of e.fields)if(o===t)a.push({field:o,value:n});else if(o===i?.discriminator)a.push({field:o,value:i.tag});else if(r?.bindings.has(o))a.push({field:o,value:r.bindings.get(o)});else break;return a}async function D(e,t){if(m.beforeLeaf(e),t==null||!p.db.normalizeId(e,t))return null;let n=await p.db.get(e,t);return n&&m.validateDocument(e,n),n}async function*O(e,t,n,r,i,a){if(p.expand(r,0),n==null||!S(n,a))return;if(t===`_id`){let o=await D(e,n);o&&b(o,i)&&S(o[t],a)&&(p.expand(r),yield o);return}m.beforeLeaf(e);let o=w(e,t,i),s=p.leafQuery(e).withIndex(o.name,e=>{let r=e;for(let e of E(o,t,n,void 0,i))r=r.eq(e.field,e.value);return r})[Symbol.asyncIterator]();try{for(;;){m.beforeLeaf(e);let n=await s.next();if(n.done)return;await p.authorize(e,n.value)&&(m.validateDocument(e,n.value),b(n.value,i)&&S(n.value[t],a)&&(p.expand(r),yield n.value))}}finally{await s.return?.()}}async function*k(e,t,n){p.expand(n,0);let r=C(e,t);if(r!=null){if(`base`in e.from&&`base`in e.to)for await(let t of O(e.from.junction.table,e.from.junction.field,r,n,e.junctionGuard))yield*O(e.target,e.to.base.field,t[e.to.junction.field],n,e.targetGuard);else yield*O(e.target,v(e.to).field,r,n,e.targetGuard,e.targetMember)}}async function A(e,t,n){let r=null;for await(let i of k(e,t,n)){if(r)throw new l(`AMBIGUOUS_RELATION`);r=i}return r}async function j(t,n,r,i=0){if(p.expand(i,0),_.tables[t].variant&&m.validateDocument(t,n),f(r))return e(r,n);if(r.kind===`variant`)return n[r.discriminator]===r.tag&&j(t,n,r.child,i);if(r.kind===`scalar`)return e(r,n);if(r.kind===`not`)return!await j(t,n,r.child,i);if(r.kind===`and`||r.kind===`or`){for(let e of r.children){let a=await j(t,n,e,i);if(r.kind===`and`&&!a)return!1;if(r.kind===`or`&&a)return!0}return r.kind===`and`}if(r.kind!==`relation`)throw new l(`INVALID_RELATION`);let a=y(t,r.name);if(r.mode===`one`){let e=await A(a,n,i+1);return r.predicate===null?e===null:e!==null&&await j(a.target,e,r.predicate,i+1)}for await(let e of k(a,n,i+1))if(await j(a.target,e,r.predicate,i+1))return r.mode===`some`;return r.mode===`none`}async function M(e,t,n,r,i=[],a=0,o=[]){p.expand(a,0);let s=m.complete(e,t,!0,{kind:`and`,children:[]});for(let[c,l]of Object.entries(n)){let n=y(e,c),u=l===!0?{}:l;if(n.cardinality===`one`){let e=await A(n,t,a+1);s[c]=e?await M(n.target,e,u,r,[...i,c],a+1,[...o,t._id]):null}else s[c]=await P(e,t,c,u,{numItems:r.numItems,cursor:null,order:r.order},[],[],[...i,c],a+1,[...o,t._id])}return s}function N(e,n,r,i,a,o,s,d){m.select(y(e,n).target,r),u(r,s.length);let f=y(e,n);if(f.cardinality!==`many`)throw new l(`INVALID_RELATION`);let _=f.targetGuard?{fields:{[f.targetGuard.discriminator]:f.targetGuard.tag}}:void 0,b=m.plan(f.target,i,[],_?[...o,_]:o),x=f.from,S=f.to,C=`base`in x&&`base`in S?{table:x.junction.table,field:x.junction.field,targetField:S.base.field,targetReference:S.junction.field}:null,E=C?C.table:f.target,D=C?C.field:v(S).field,O=T(f.target,C?C.targetField:D,b,a,f.targetGuard),k=C?w(E,D,f.junctionGuard):O,A={...g,table:E,index:k.name,order:b.args.order??`asc`,clientFilters:b.clientIdentity,serverFilters:{predicates:b.serverIdentity,selection:r,scope:{...p.scope},rootTable:g.table,lookupIndex:O.name,guards:{source:f.sourceGuard?{...f.sourceGuard}:null,target:f.targetGuard?{...f.targetGuard}:null,junction:f.junctionGuard?{...f.junctionGuard}:null},...(f.sourceMember||f.targetMember)&&{members:{source:f.sourceMember?{...f.sourceMember}:null,target:f.targetMember?{...f.targetMember}:null}}},relationPath:s,parent:d};function j(e){try{return c.parse(JSON.parse(h.open(e,A)))}catch{throw new t}}return{declared:f,plan:b,through:C,sourceTable:E,field:D,index:k,targetIndex:O,binding:A,open:j}}async function P(e,t,r,i,c,l=[],u=[],f=[r],g=1,_=[],y=N(e,r,i,c,l,u,f,_[0]??t._id),x){p.expand(g,0);let{declared:w,plan:T,through:O,sourceTable:k,field:A,index:P,targetIndex:F,binding:I}=y,L=C(w,t),R={...T.args,...x??{cursor:null}},z={...T,index:P,prefix:L==null?[]:O?E(P,A,L,void 0,w.junctionGuard):E(P,A,L,T,w.targetGuard),unsatisfiable:L==null||T.unsatisfiable},B=O&&O.targetField!==`_id`;return await n(d,p,k,z,m,h,I,R,async(e,t)=>{if(t&&m.validateDocument(B?w.target:k,e),!t||O&&!B&&!b(e,w.junctionGuard))return null;p.expand(g);let n;return n=O&&!B?await D(w.target,e[O.targetReference]):e,!n||!b(n,w.targetGuard)||!S(n[v(w.to).field],w.targetMember)||!await j(w.target,n,T.residual,g)?null:M(w.target,n,i,T.args,f,g,_)},e=>h.seal(JSON.stringify({cursor:e,ancestors:_.slice(1)}),I),B?(e,t)=>{let n=d,r=o(w.target,F.name,n);return e.flatMap(async e=>{p.expand(g);let i=await p.authorize(k,e);i&&m.validateDocument(k,e);let o=e[O.targetReference];return!i||!b(e,w.junctionGuard)||o==null?new a(T.args.order??`asc`,r):s(t,n).query(w.target).withIndex(F.name,e=>{let t=e,n=E(F,O.targetField,o,T,w.targetGuard);for(let e of n)t=t.eq(e.field,e.value);return t}).order(T.args.order??`asc`)},r)}:void 0,B?w.target:k)}async function F(e,n,i,a,o,s,c){if(!i.length||i.length>4||!p.db.normalizeId(e,n))throw new l(`INVALID_RELATION`);let u=e,d=i.map(e=>{let t=y(u,e);return u=t.target,t}),f=d.at(-1),m=N(f.source,i.at(-1),a,o,s,c,i,n),h=o.cursor===null?null:m.open(o.cursor),g=o.endCursor==null?null:m.open(o.endCursor),_=h?.ancestors??g?.ancestors??[];if(_.length!==i.length-1||h&&g&&JSON.stringify(h.ancestors)!==JSON.stringify(g.ancestors)||_.some((e,t)=>!p.db.normalizeId(d[t].target,e)))throw new t;try{let t=await D(e,n);for(let e=0;t&&e<_.length;e++){let n=d[e],r=null;if(n.cardinality===`one`){let i=await A(n,t,e+1);i?._id===_[e]&&(r=i)}else for await(let i of k(n,t,e+1))if(i._id===_[e]){r=i;break}t=r}return await P(f.source,t,i.at(-1),a,o,s,c,i,i.length,[n,..._],m,{cursor:h?.cursor??null,endCursor:g?.cursor??o.endCursor})}catch(e){r(e)}}return{matches:j,output:M,continuation:F,edge:y}}function f(e){return e.kind===`relation`?!1:e.kind===`and`||e.kind===`or`?e.children.every(f):e.kind===`not`||e.kind===`variant`?f(e.child):!0}export{l as RelationshipError,u as assertRelationshipDepth,d as relationshipExecutor};
|
|
2
2
|
//# sourceMappingURL=relationships.mjs.map
|