cvx-kit 0.1.19 → 0.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +50 -2
  2. package/dist/errors.d.mts +3 -2
  3. package/dist/errors.mjs +1 -1
  4. package/dist/errors.mjs.map +1 -1
  5. package/dist/filters.d.mts +4 -0
  6. package/dist/filters.mjs +1 -0
  7. package/dist/index.d.mts +3 -3
  8. package/dist/modules/contracts/errors.d.mts +218 -0
  9. package/dist/modules/contracts/errors.mjs +1 -1
  10. package/dist/modules/contracts/errors.mjs.map +1 -1
  11. package/dist/modules/effect/crud.d.mts +28 -0
  12. package/dist/querying/cursors.d.mts +30 -4
  13. package/dist/querying/cursors.mjs +1 -1
  14. package/dist/querying/cursors.mjs.map +1 -1
  15. package/dist/querying/execution.d.mts +14 -3
  16. package/dist/querying/execution.mjs +1 -1
  17. package/dist/querying/execution.mjs.map +1 -1
  18. package/dist/querying/failures.d.mts +13 -0
  19. package/dist/querying/failures.mjs +2 -0
  20. package/dist/querying/failures.mjs.map +1 -0
  21. package/dist/querying/filters.d.mts +43 -44
  22. package/dist/querying/filters.mjs +1 -1
  23. package/dist/querying/filters.mjs.map +1 -1
  24. package/dist/querying/planner.d.mts +13 -16
  25. package/dist/querying/planner.mjs +1 -1
  26. package/dist/querying/planner.mjs.map +1 -1
  27. package/dist/querying/relationships.d.mts +24 -7
  28. package/dist/querying/relationships.mjs +1 -1
  29. package/dist/querying/relationships.mjs.map +1 -1
  30. package/dist/querying/schema-snapshot.d.mts +29 -0
  31. package/dist/querying/schema-snapshot.mjs +2 -0
  32. package/dist/querying/schema-snapshot.mjs.map +1 -0
  33. package/dist/querying/search.d.mts +69 -0
  34. package/dist/querying/search.mjs +2 -0
  35. package/dist/querying/search.mjs.map +1 -0
  36. package/dist/querying/security.d.mts +19 -2
  37. package/dist/querying/security.mjs +1 -1
  38. package/dist/querying/security.mjs.map +1 -1
  39. package/dist/querying/shared/ast.d.mts +28 -0
  40. package/dist/querying/shared/ast.mjs +2 -0
  41. package/dist/querying/shared/ast.mjs.map +1 -0
  42. package/dist/querying/shared/contract.d.mts +104 -0
  43. package/dist/querying/shared/contract.mjs +2 -0
  44. package/dist/querying/shared/contract.mjs.map +1 -0
  45. package/dist/querying/shared/evaluate.d.mts +7 -0
  46. package/dist/querying/shared/evaluate.mjs +2 -0
  47. package/dist/querying/shared/evaluate.mjs.map +1 -0
  48. package/dist/querying/shared/filters.d.mts +31 -0
  49. package/dist/querying/shared/filters.mjs +2 -0
  50. package/dist/querying/shared/filters.mjs.map +1 -0
  51. package/dist/querying.d.mts +47 -16
  52. package/dist/querying.mjs +1 -1
  53. package/dist/querying.mjs.map +1 -1
  54. package/dist/react/failures.d.mts +23 -0
  55. package/dist/react/failures.mjs +2 -0
  56. package/dist/react/failures.mjs.map +1 -0
  57. package/dist/react/optimistic.d.mts +47 -0
  58. package/dist/react/optimistic.mjs +2 -0
  59. package/dist/react/optimistic.mjs.map +1 -0
  60. package/dist/react/pagination-state.d.mts +40 -0
  61. package/dist/react/pagination-state.mjs +2 -0
  62. package/dist/react/pagination-state.mjs.map +1 -0
  63. package/dist/react/use-command.d.mts +23 -0
  64. package/dist/react/use-command.mjs +2 -0
  65. package/dist/react/use-command.mjs.map +1 -0
  66. package/dist/react/use-paginated-list.d.mts +32 -0
  67. package/dist/react/use-paginated-list.mjs +2 -0
  68. package/dist/react/use-paginated-list.mjs.map +1 -0
  69. package/dist/react.d.mts +6 -0
  70. package/dist/react.mjs +1 -0
  71. package/dist/relations-public.d.mts +2 -2
  72. package/dist/relations.d.mts +65 -22
  73. package/dist/relations.mjs +1 -1
  74. package/dist/relations.mjs.map +1 -1
  75. package/dist/zod-convex.mjs +1 -1
  76. package/dist/zod-convex.mjs.map +1 -1
  77. package/dist/zod-table.d.mts +15 -1
  78. package/dist/zod-table.mjs +1 -1
  79. package/dist/zod-table.mjs.map +1 -1
  80. package/package.json +21 -4
@@ -1 +1 @@
1
- {"version":3,"file":"security.mjs","names":["MAX_DOCUMENT_BYTES","defaultBudgets","ManagedReadError","code","capabilities","registerQueryReader","facade","ordinary","ordinaryQuery","table","raw","snapshot","authority","createManagedSecurity","config","tables","limits","central","policyVersion","trustedAdapter","registered","ctx","adapted","request","lower","suppliedPolicyRules","session","result","rules","operations","wrapped","doc","source","args","iterator","key","value","overrides","counts","view","memo","pendingMemo","pendingCandidates","fail","assertTable","openRange","budgets","reserveLeaf","settled","document","size","getDocumentSize","keyOf","id","checkCycle","ancestors","authorize","cap","rule","reader","get","memoize","reservation","error","query","unsupported","iterate","next","authorized","take","n","docs","blocked","arg1","arg0","name","depth","count"],"sources":["../../src/querying/security.ts"],"sourcesContent":["/** Internal managed-read contracts for the relational executor. No root/package export. */\nimport type {\n\tGenericDataModel,\n\tGenericDatabaseReader,\n\tGenericDocument,\n\tGenericTableInfo,\n\tQueryInitializer,\n} from 'convex/server'\nimport { getDocumentSize, type GenericId } from 'convex/values'\nimport type { Rules } from '../tenancy'\n\nexport const MAX_DOCUMENT_BYTES = 1024 * 1024\n\n/** Additive context supplied to existing read/insert/modify rules on BOTH paths.\n * Reads through db are authorized and bounded. collect, native paginate/filter,\n * search, and system reads fail closed; use get, take/first/unique, or iteration.\n * Trusted server callbacks can close\n * over other readers; arbitrary captured JavaScript cannot be sandboxed here.\n */\nexport interface SecurityPolicyContext<DM extends GenericDataModel> {\n\treadonly db: GenericDatabaseReader<DM>\n}\n\nexport interface ManagedScope {\n\treadonly mode: 'authenticated' | 'system' | 'trusted'\n\treadonly principal: string | null\n\treadonly organization: string | null\n\treadonly tenant: string | null\n\treadonly role: string | null\n}\n\nexport interface ManagedBudgets {\n\treadOperations: number\n\tindexRanges: number\n\trawCandidates: number\n\tdocumentBytes: number\n\tfanout: number\n\tdepth: number\n\tmemoEntries: number\n}\n\n/** Finite starting ceilings; U5/U6 adversarial workload evidence must tune them.\n * Document payload accounting is NOT Convex storage/index/prefetch I/O accounting.\n */\nconst defaultBudgets: Readonly<ManagedBudgets> = Object.freeze({\n\treadOperations: 4096,\n\tindexRanges: 1024,\n\trawCandidates: 8192,\n\tdocumentBytes: 16 * MAX_DOCUMENT_BYTES,\n\tfanout: 1024,\n\tdepth: 8,\n\tmemoEntries: 4096,\n})\n\nexport class ManagedReadError extends Error {\n\tconstructor(\n\t\treadonly code:\n\t\t\t| 'UNMANAGED_QUERY_CONTEXT'\n\t\t\t| 'MANAGED_READ_BUDGET'\n\t\t\t| 'POLICY_RECURSION'\n\t\t\t| 'UNSUPPORTED_MANAGED_READ',\n\t) {\n\t\tsuper(code)\n\t\tthis.name = 'ManagedReadError'\n\t}\n}\n\ninterface Authority<DM extends GenericDataModel> {\n\tscope: ManagedScope\n\trules: Rules<SecurityPolicyContext<DM>, DM>\n\tdefaultPolicy: 'allow' | 'deny'\n}\ninterface Capability<DM extends GenericDataModel> extends Authority<DM> {\n\treader: GenericDatabaseReader<DM>\n}\n// Type erasure stays inside the private identity map; registration retains native DM.\nconst capabilities = new WeakMap<object, Capability<any>>()\n\n/** Unique closure-only ordinary facade: helper implementation fields never leak.\n * Ordinary operations preserve their native/helper behavior and receive no new\n * engine ceilings. Register only from query constructors, never mutations/actions.\n */\nexport function registerQueryReader<DM extends GenericDataModel>(\n\traw: GenericDatabaseReader<DM>,\n\tordinary: GenericDatabaseReader<DM>,\n\tauthority: Authority<DM>,\n): GenericDatabaseReader<DM> {\n\t// SAFETY: all base-reader methods are bound forwards; the new identity hides helper fields.\n\tconst facade: GenericDatabaseReader<DM> = Object.freeze({\n\t\tget: ordinary.get.bind(ordinary),\n\t\tquery: <Table extends Parameters<typeof ordinary.query>[0]>(table: Table) =>\n\t\t\tordinaryQuery(ordinary.query(table)),\n\t\tnormalizeId: ordinary.normalizeId.bind(ordinary),\n\t\tsystem: Object.freeze({\n\t\t\tget: ordinary.system.get.bind(ordinary.system),\n\t\t\tquery: <Table extends Parameters<typeof ordinary.system.query>[0]>(table: Table) =>\n\t\t\t\tordinaryQuery(ordinary.system.query(table)),\n\t\t\tnormalizeId: ordinary.system.normalizeId.bind(ordinary.system),\n\t\t}),\n\t})\n\t// SAFETY: capabilities are retrieved using the same facade with its native DM.\n\tcapabilities.set(facade, { reader: raw, ...snapshot(authority) })\n\treturn facade\n}\n\nexport interface ManagedCounters {\n\treadonly readOperations: number\n\treadonly indexRanges: number\n\treadonly rawCandidates: number\n\treadonly documentBytesObserved: number\n\treadonly documentBytesReserved: number\n\treadonly fanout: number\n\treadonly memoEntries: number\n}\nexport interface LeafReservation {\n\t/** Settle/refund exactly once, including missing/error results (null). */\n\tsettle(document: GenericDocument | null): void\n}\nexport interface ManagedReadRequest<DM extends GenericDataModel> {\n\treadonly db: GenericDatabaseReader<DM>\n\treadonly scope: ManagedScope & { readonly policyVersion: string }\n\treadonly budgets: Readonly<ManagedBudgets>\n\treadonly counters: ManagedCounters\n\t/** Future native leaf adapter reserves BEFORE every actual next/get, not an outer stream. */\n\treserveLeaf(): LeafReservation\n\topenRange(): void\n\texpand(depth: number, count?: number): void\n\t/** Raw candidate visibility for the private executor; every leaf is metered.\n\t * Its iterator reserves each next internally; do not add an outer reservation.\n\t * Eligibility/authorization must complete before its key is eligible to advance.\n\t */\n\tleafQuery(table: string): QueryInitializer<any>\n\tauthorize(table: string, document: GenericDocument): Promise<boolean>\n}\n\nexport interface ManagedSecurityConfig<DM extends GenericDataModel> {\n\t/** Entire registry: parent/junction/target/policy dependencies share one request. */\n\ttables: readonly string[]\n\tpolicyVersion: string\n\tbudgets?: Partial<ManagedBudgets>\n\t/** Central server trust assertion. Never inspect third-party wrapper fields.\n\t * The adapter must attest a QUERY transaction reader and the exact effective\n\t * composed policy and immutable scope. It assumes responsibility for that claim.\n\t */\n\ttrustedAdapter?: (\n\t\tctx: ManagedQueryContext<DM>,\n\t) => (Authority<DM> & { reader: GenericDatabaseReader<DM> }) | null\n}\n\n/** Native writer/action contexts are not accepted by the engine contract. */\nexport type ManagedQueryContext<DM extends GenericDataModel> = {\n\tdb: GenericDatabaseReader<DM> & { insert?: never; patch?: never; replace?: never; delete?: never }\n}\n\n/** Central construction, then one shared request per engine invocation.\n * Requests may lower, never raise ceilings. Unknown/native contexts fail closed.\n */\nexport function createManagedSecurity<DM extends GenericDataModel = GenericDataModel>(\n\tconfig: ManagedSecurityConfig<DM>,\n) {\n\tif (\n\t\t!config.policyVersion ||\n\t\t!config.tables.length ||\n\t\tnew Set(config.tables).size !== config.tables.length ||\n\t\tconfig.tables.some((table) => !table || table.startsWith('_'))\n\t)\n\t\tthrow new Error('Invalid managed security configuration')\n\tconst tables = new Set(config.tables)\n\t// Central values may exceed starting defaults; these are application-owned ceilings.\n\tlimits(config.budgets)\n\tconst central = Object.freeze({ ...defaultBudgets, ...config.budgets })\n\tconst policyVersion = config.policyVersion\n\tconst trustedAdapter = config.trustedAdapter\n\treturn <Ctx extends ManagedQueryContext<DM>>(\n\t\tctx: Ctx,\n\t\tlower?: Partial<ManagedBudgets>,\n\t): ManagedReadRequest<DM> => {\n\t\t// SAFETY: identity is registered privately by the query constructor for this DM.\n\t\tconst registered = capabilities.get(ctx.db) as Capability<DM> | undefined\n\t\tconst adapted = registered ? undefined : trustedAdapter?.(ctx)\n\t\tif (!registered && !adapted) throw new ManagedReadError('UNMANAGED_QUERY_CONTEXT')\n\t\tconst cap = registered ?? { ...adapted!, ...snapshot(adapted!) }\n\t\treturn request(cap, tables, limits(lower, central), policyVersion)\n\t}\n}\n\n/** Ordinary rule invocations share a policy-only meter. Ordinary db reads stay\n * unmetered by engine ceilings; supplied rule dependencies never bypass policy.\n * Mutation insert/modify receive the same supplied contract as read callbacks.\n */\nexport function suppliedPolicyRules<DM extends GenericDataModel>(\n\traw: GenericDatabaseReader<DM>,\n\trules: Rules<SecurityPolicyContext<DM>, DM>,\n\tdefaultPolicy: 'allow' | 'deny',\n): Rules<SecurityPolicyContext<DM>, DM> {\n\tconst cap = {\n\t\treader: raw,\n\t\t...snapshot({\n\t\t\trules,\n\t\t\tdefaultPolicy,\n\t\t\tscope: { mode: 'trusted', principal: null, organization: null, tenant: null, role: null },\n\t\t}),\n\t}\n\t// Mutations can change dependencies between callbacks; never memoize ordinary\n\t// policy reads across writes. The shared policy meter still bounds all calls.\n\tconst session = request(cap, null, defaultBudgets, 'ordinary-policy', false)\n\tconst result: Rules<SecurityPolicyContext<DM>, DM> = {}\n\tfor (const table of Object.keys(rules)) {\n\t\tconst operations = rules[table]\n\t\tconst wrapped: NonNullable<typeof operations> = {}\n\t\tif (operations?.read) wrapped.read = (_ctx, doc) => session.authorize(table, doc)\n\t\tif (operations?.insert)\n\t\t\twrapped.insert = (_ctx, doc) => operations.insert!({ db: session.db }, doc)\n\t\tif (operations?.modify)\n\t\t\twrapped.modify = (_ctx, doc) => operations.modify!({ db: session.db }, doc)\n\t\t// SAFETY: the keys originated from this same Rules map.\n\t\tresult[table as keyof typeof result] = wrapped\n\t}\n\treturn result\n}\n\n/** Forward only native query methods, never the RLS helper's unfiltered fields.\n * Ordinary consumption retains its existing policy and has no engine ceilings.\n */\nfunction ordinaryQuery<T extends GenericTableInfo>(\n\tsource: QueryInitializer<T>,\n): QueryInitializer<T> {\n\t// SAFETY: native fluent methods retain the same table metadata; ordered results\n\t// only expose methods the native/helper source itself can successfully invoke.\n\treturn Object.freeze({\n\t\tfullTableScan: () => ordinaryQuery(source.fullTableScan() as QueryInitializer<T>),\n\t\twithIndex: (...args: Parameters<typeof source.withIndex>) =>\n\t\t\tordinaryQuery(source.withIndex(...args) as QueryInitializer<T>),\n\t\twithSearchIndex: (...args: Parameters<typeof source.withSearchIndex>) =>\n\t\t\tordinaryQuery(source.withSearchIndex(...args) as QueryInitializer<T>),\n\t\tfilter: (...args: Parameters<typeof source.filter>) =>\n\t\t\tordinaryQuery(source.filter(...args) as QueryInitializer<T>),\n\t\torder: (...args: Parameters<typeof source.order>) =>\n\t\t\tordinaryQuery(source.order(...args) as QueryInitializer<T>),\n\t\tpaginate: source.paginate.bind(source),\n\t\tcollect: source.collect.bind(source),\n\t\ttake: source.take.bind(source),\n\t\tfirst: source.first.bind(source),\n\t\tunique: source.unique.bind(source),\n\t\t[Symbol.asyncIterator]: () => {\n\t\t\tconst iterator = source[Symbol.asyncIterator]()\n\t\t\tconst facade: typeof iterator = {\n\t\t\t\tnext: iterator.next.bind(iterator),\n\t\t\t}\n\t\t\tif (iterator.return) facade.return = iterator.return.bind(iterator)\n\t\t\tif (iterator.throw) facade.throw = iterator.throw.bind(iterator)\n\t\t\treturn Object.freeze(facade)\n\t\t},\n\t}) as QueryInitializer<T>\n}\n\nfunction limits(\n\toverrides: Partial<ManagedBudgets> = {},\n\tcentral = defaultBudgets,\n): Readonly<ManagedBudgets> {\n\tfor (const [key, value] of Object.entries(overrides)) {\n\t\tif (!Object.hasOwn(defaultBudgets, key) || !Number.isSafeInteger(value) || value <= 0)\n\t\t\tthrow new Error('Invalid managed read budgets')\n\t}\n\treturn Object.freeze({\n\t\treadOperations: Math.min(\n\t\t\tcentral.readOperations,\n\t\t\toverrides.readOperations ?? central.readOperations,\n\t\t),\n\t\tindexRanges: Math.min(central.indexRanges, overrides.indexRanges ?? central.indexRanges),\n\t\trawCandidates: Math.min(\n\t\t\tcentral.rawCandidates,\n\t\t\toverrides.rawCandidates ?? central.rawCandidates,\n\t\t),\n\t\tdocumentBytes: Math.min(\n\t\t\tcentral.documentBytes,\n\t\t\toverrides.documentBytes ?? central.documentBytes,\n\t\t),\n\t\tfanout: Math.min(central.fanout, overrides.fanout ?? central.fanout),\n\t\tdepth: Math.min(central.depth, overrides.depth ?? central.depth),\n\t\tmemoEntries: Math.min(central.memoEntries, overrides.memoEntries ?? central.memoEntries),\n\t})\n}\n\nfunction snapshot<DM extends GenericDataModel>(authority: Authority<DM>): Authority<DM> {\n\tconst rules = Object.fromEntries(\n\t\tObject.entries(authority.rules).map(([table, rules]) => [table, Object.freeze({ ...rules })]),\n\t)\n\treturn Object.freeze({\n\t\tscope: Object.freeze({ ...authority.scope }),\n\t\t// SAFETY: only the table keys and operation callbacks of Rules were copied.\n\t\trules: Object.freeze(rules) as Rules<SecurityPolicyContext<DM>, DM>,\n\t\tdefaultPolicy: authority.defaultPolicy,\n\t})\n}\n\nfunction request<DM extends GenericDataModel>(\n\tcap: Capability<DM>,\n\ttables: ReadonlySet<string> | null,\n\tbudgets: Readonly<ManagedBudgets>,\n\tpolicyVersion: string,\n\tmemoize = true,\n): ManagedReadRequest<DM> {\n\tconst counts = {\n\t\treadOperations: 0,\n\t\tindexRanges: 0,\n\t\trawCandidates: 0,\n\t\tdocumentBytesObserved: 0,\n\t\tdocumentBytesReserved: 0,\n\t\tfanout: 0,\n\t\tmemoEntries: 0,\n\t}\n\tconst view: ManagedCounters = Object.freeze({\n\t\tget readOperations() {\n\t\t\treturn counts.readOperations\n\t\t},\n\t\tget indexRanges() {\n\t\t\treturn counts.indexRanges\n\t\t},\n\t\tget rawCandidates() {\n\t\t\treturn counts.rawCandidates\n\t\t},\n\t\tget documentBytesObserved() {\n\t\t\treturn counts.documentBytesObserved\n\t\t},\n\t\tget documentBytesReserved() {\n\t\t\treturn counts.documentBytesReserved\n\t\t},\n\t\tget fanout() {\n\t\t\treturn counts.fanout\n\t\t},\n\t\tget memoEntries() {\n\t\t\treturn counts.memoEntries\n\t\t},\n\t})\n\tconst memo = new Map<string, GenericDocument | null>()\n\tlet pendingMemo = 0\n\tlet pendingCandidates = 0\n\tconst fail = (): never => {\n\t\tthrow new ManagedReadError('MANAGED_READ_BUDGET')\n\t}\n\tconst assertTable = (table: string) => {\n\t\tif (table.startsWith('_') || (tables && !tables.has(table)))\n\t\t\tthrow new ManagedReadError('UNSUPPORTED_MANAGED_READ')\n\t}\n\tconst openRange = () => {\n\t\tif (counts.indexRanges >= budgets.indexRanges) fail()\n\t\tcounts.indexRanges++\n\t}\n\tconst reserveLeaf = (): LeafReservation => {\n\t\tif (\n\t\t\tcounts.readOperations >= budgets.readOperations ||\n\t\t\tcounts.rawCandidates + pendingCandidates >= budgets.rawCandidates ||\n\t\t\tcounts.documentBytesObserved + counts.documentBytesReserved + MAX_DOCUMENT_BYTES >\n\t\t\t\tbudgets.documentBytes\n\t\t)\n\t\t\tfail()\n\t\tcounts.readOperations++\n\t\tpendingCandidates++\n\t\tcounts.documentBytesReserved += MAX_DOCUMENT_BYTES\n\t\tlet settled = false\n\t\treturn {\n\t\t\tsettle(document) {\n\t\t\t\tif (settled) throw new Error('Leaf reservation already settled')\n\t\t\t\tsettled = true\n\t\t\t\tpendingCandidates--\n\t\t\t\tcounts.documentBytesReserved -= MAX_DOCUMENT_BYTES\n\t\t\t\tif (document) {\n\t\t\t\t\tcounts.rawCandidates++\n\t\t\t\t\tconst size = getDocumentSize(document)\n\t\t\t\t\tcounts.documentBytesObserved += size\n\t\t\t\t\tif (size > MAX_DOCUMENT_BYTES) fail()\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n\tconst keyOf = (table: string, id: string) => JSON.stringify([table, id])\n\tconst checkCycle = (key: string, ancestors: readonly string[]) => {\n\t\tif (ancestors.includes(key)) throw new ManagedReadError('POLICY_RECURSION')\n\t\tif (ancestors.length >= budgets.depth) fail()\n\t}\n\tasync function authorize(\n\t\ttable: string,\n\t\tdoc: GenericDocument,\n\t\tancestors: readonly string[] = [],\n\t): Promise<boolean> {\n\t\tassertTable(table)\n\t\t// SAFETY: native documents carry a string system ID; normalizeId additionally verifies the declared table.\n\t\tconst id = doc._id as GenericId<string>\n\t\tif (!cap.reader.normalizeId(table, id)) throw new ManagedReadError('UNSUPPORTED_MANAGED_READ')\n\t\tconst key = keyOf(table, id)\n\t\tcheckCycle(key, ancestors)\n\t\tconst rule = cap.rules[table]?.read\n\t\treturn rule\n\t\t\t? rule(Object.freeze({ db: reader([...ancestors, key]) }), doc)\n\t\t\t: cap.defaultPolicy === 'allow'\n\t}\n\tasync function get(\n\t\ttable: string,\n\t\tid: string,\n\t\tancestors: readonly string[],\n\t): Promise<GenericDocument | null> {\n\t\tassertTable(table)\n\t\tconst key = keyOf(table, id)\n\t\tcheckCycle(key, ancestors) // BEFORE consulting memo: active policy cannot await itself.\n\t\tif (memo.has(key)) return memo.get(key)!\n\t\tif (memoize && counts.memoEntries >= budgets.memoEntries) fail()\n\t\t// Completed results only: independent concurrent branches cannot deadlock\n\t\t// through mutually pending memo promises. Concurrent duplicates may fetch twice.\n\t\tif (memoize) {\n\t\t\tpendingMemo++\n\t\t\tcounts.memoEntries = memo.size + pendingMemo\n\t\t}\n\t\ttry {\n\t\t\topenRange()\n\t\t\tconst reservation = reserveLeaf()\n\t\t\tlet doc: GenericDocument | null\n\t\t\ttry {\n\t\t\t\t// SAFETY: IDs come from native readers or caller-branded IDs, with explicit table identity.\n\t\t\t\tdoc = await cap.reader.get(table, id as GenericId<string>)\n\t\t\t} catch (error) {\n\t\t\t\treservation.settle(null)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\treservation.settle(doc)\n\t\t\tconst result = doc && (await authorize(table, doc, ancestors)) ? doc : null\n\t\t\tif (memoize) memo.set(key, result)\n\t\t\treturn result\n\t\t} finally {\n\t\t\tif (memoize) {\n\t\t\t\tpendingMemo--\n\t\t\t\tcounts.memoEntries = memo.size + pendingMemo\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction query(\n\t\ttable: string,\n\t\tsource: QueryInitializer<any>,\n\t\tancestors: readonly string[],\n\t\tauthorized: boolean,\n\t): QueryInitializer<any> {\n\t\tfunction unsupported(): never {\n\t\t\tthrow new ManagedReadError('UNSUPPORTED_MANAGED_READ')\n\t\t}\n\t\tasync function* iterate() {\n\t\t\topenRange()\n\t\t\tlet iterator: AsyncIterator<GenericDocument> | undefined\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\tconst reservation = reserveLeaf()\n\t\t\t\t\tlet next: IteratorResult<GenericDocument>\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Native iterator construction starts the query. Reserve before\n\t\t\t\t\t\t// both initialization and next; every later next reserves anew.\n\t\t\t\t\t\titerator ??= source[Symbol.asyncIterator]()\n\t\t\t\t\t\tnext = await iterator.next()\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treservation.settle(null)\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t\treservation.settle(next.done ? null : next.value)\n\t\t\t\t\tif (next.done) return\n\t\t\t\t\tif (!authorized || (await authorize(table, next.value, ancestors))) yield next.value\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tawait iterator?.return?.()\n\t\t\t}\n\t\t}\n\t\tasync function take(n: number) {\n\t\t\tif (!Number.isSafeInteger(n) || n < 0 || n > budgets.rawCandidates)\n\t\t\t\tthrow new ManagedReadError('MANAGED_READ_BUDGET')\n\t\t\tconst docs: GenericDocument[] = []\n\t\t\tif (n === 0) return docs\n\t\t\tfor await (const doc of iterate()) {\n\t\t\t\tdocs.push(doc)\n\t\t\t\tif (docs.length === n) break\n\t\t\t}\n\t\t\treturn docs\n\t\t}\n\t\t// SAFETY: forwarding preserves native document/index types; only consumers are replaced.\n\t\treturn Object.freeze({\n\t\t\tfullTableScan: () =>\n\t\t\t\tquery(table, source.fullTableScan() as QueryInitializer<any>, ancestors, authorized),\n\t\t\twithIndex: (...args: Parameters<typeof source.withIndex>) =>\n\t\t\t\tquery(table, source.withIndex(...args) as QueryInitializer<any>, ancestors, authorized),\n\t\t\torder: (...args: Parameters<typeof source.order>) =>\n\t\t\t\tquery(table, source.order(...args) as QueryInitializer<any>, ancestors, authorized),\n\t\t\t// Native filters/search/pagination may scan/prefetch hidden rows. They\n\t\t\t// cannot prove per-leaf reservation via public APIs; reject on managed readers.\n\t\t\tfilter: unsupported,\n\t\t\twithSearchIndex: unsupported,\n\t\t\tpaginate: unsupported,\n\t\t\tcollect: unsupported,\n\t\t\ttake,\n\t\t\tfirst: async () => (await take(1))[0] ?? null,\n\t\t\tunique: async () => {\n\t\t\t\tconst docs = await take(2)\n\t\t\t\tif (docs.length > 1) throw new Error('Query is not unique')\n\t\t\t\treturn docs[0] ?? null\n\t\t\t},\n\t\t\t[Symbol.asyncIterator]: iterate,\n\t\t}) as QueryInitializer<any>\n\t}\n\tfunction reader(ancestors: readonly string[]): GenericDatabaseReader<any> {\n\t\tconst blocked = () => {\n\t\t\tthrow new ManagedReadError('UNSUPPORTED_MANAGED_READ')\n\t\t}\n\t\t// SAFETY: normalizeId retains the requested table brand; query forwarding retains native table metadata.\n\t\treturn Object.freeze({\n\t\t\tget: async (arg0: string, arg1?: string) => {\n\t\t\t\tif (arg1 !== undefined) return get(arg0, arg1, ancestors)\n\t\t\t\t// Resolve only from the explicit registry (or known policy tables for\n\t\t\t\t// ordinary policy callbacks). Never raw-get an unrecognized ID.\n\t\t\t\tconst table = [...(tables ?? new Set(Object.keys(cap.rules)))].find((name) =>\n\t\t\t\t\tcap.reader.normalizeId(name, arg0),\n\t\t\t\t)\n\t\t\t\treturn table ? get(table, arg0, ancestors) : null\n\t\t\t},\n\t\t\tquery: (table: string) => {\n\t\t\t\tassertTable(table)\n\t\t\t\treturn query(table, cap.reader.query(table), ancestors, true)\n\t\t\t},\n\t\t\tnormalizeId: (table: string, id: string) => {\n\t\t\t\tassertTable(table)\n\t\t\t\treturn cap.reader.normalizeId(table, id)\n\t\t\t},\n\t\t\tsystem: Object.freeze({ get: blocked, query: blocked, normalizeId: blocked }),\n\t\t}) as GenericDatabaseReader<any>\n\t}\n\treturn Object.freeze({\n\t\tdb: reader([]),\n\t\tscope: Object.freeze({ ...cap.scope, policyVersion }),\n\t\tbudgets,\n\t\tcounters: view,\n\t\treserveLeaf,\n\t\topenRange,\n\t\texpand(depth: number, count = 1) {\n\t\t\tif (\n\t\t\t\t!Number.isSafeInteger(depth) ||\n\t\t\t\tdepth < 0 ||\n\t\t\t\tdepth > budgets.depth ||\n\t\t\t\t!Number.isSafeInteger(count) ||\n\t\t\t\tcount < 0 ||\n\t\t\t\tcounts.fanout + count > budgets.fanout\n\t\t\t)\n\t\t\t\tfail()\n\t\t\tcounts.fanout += count\n\t\t},\n\t\tleafQuery: (table: string) => {\n\t\t\tassertTable(table)\n\t\t\treturn query(table, cap.reader.query(table), [], false)\n\t\t},\n\t\tauthorize: (table: string, doc: GenericDocument) => authorize(table, doc),\n\t})\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"security.mjs","names":["MAX_DOCUMENT_BYTES","defaultBudgets","ManagedReadError","code","capabilities","registerQueryReader","facade","ordinary","ordinaryQuery","table","raw","snapshot","authority","createManagedSecurity","config","tables","limits","central","policyVersion","trustedAdapter","admit","registered","ctx","adapted","managed","request","lower","key","suppliedPolicyRules","session","result","rules","operations","wrapped","doc","source","args","iterator","value","overrides","counts","view","searchUsed","searchCounts","memo","pendingMemo","pendingCandidates","leafFailures","leafLimit","budgets","fail","assertTable","openRange","reserveLeaf","settled","document","size","getDocumentSize","keyOf","id","checkCycle","ancestors","authorize","cap","rule","reader","get","memoize","reservation","error","query","unsupported","iterate","reason","authorized","next","take","n","docs","blocked","arg1","arg0","name","index","filter","q","text","equalities","field","options","depth","count"],"sources":["../../src/querying/security.ts"],"sourcesContent":["/** Internal managed-read contracts for the relational executor. No root/package export. */\nimport type {\n\tGenericDataModel,\n\tGenericDatabaseReader,\n\tGenericDocument,\n\tGenericTableInfo,\n\tQueryInitializer,\n} from 'convex/server'\nimport { getDocumentSize, type GenericId } from 'convex/values'\nimport type { SearchIndexMetadata } from '../relations'\nimport type { PaginationOptions, PaginationResult } from 'convex/server'\nimport type { FilterScalar } from './filters'\nimport type { Rules } from '../tenancy'\n\nexport const MAX_DOCUMENT_BYTES = 1024 * 1024\n\n/** Additive context supplied to existing read/insert/modify rules on BOTH paths.\n * Reads through db are authorized and bounded. collect, native paginate/filter,\n * search, and system reads fail closed; use get, take/first/unique, or iteration.\n * Trusted server callbacks can close\n * over other readers; arbitrary captured JavaScript cannot be sandboxed here.\n */\nexport interface SecurityPolicyContext<DM extends GenericDataModel> {\n\treadonly db: GenericDatabaseReader<DM>\n}\n\nexport interface ManagedScope {\n\treadonly mode: 'authenticated' | 'system' | 'trusted'\n\treadonly principal: string | null\n\treadonly organization: string | null\n\treadonly tenant: string | null\n\treadonly role: string | null\n}\n\nexport interface ManagedBudgets {\n\tsearchCandidates: number\n\treadOperations: number\n\tindexRanges: number\n\trawCandidates: number\n\tdocumentBytes: number\n\tfanout: number\n\tdepth: number\n\tmemoEntries: number\n}\n\n/** Finite starting ceilings; U5/U6 adversarial workload evidence must tune them.\n * Document payload accounting is NOT Convex storage/index/prefetch I/O accounting.\n */\nconst defaultBudgets: Readonly<ManagedBudgets> = Object.freeze({\n\tsearchCandidates: 100,\n\treadOperations: 4096,\n\tindexRanges: 1024,\n\trawCandidates: 8192,\n\tdocumentBytes: 16 * MAX_DOCUMENT_BYTES,\n\tfanout: 1024,\n\tdepth: 8,\n\tmemoEntries: 4096,\n})\n\nexport class ManagedReadError extends Error {\n\tconstructor(\n\t\treadonly code:\n\t\t\t| 'UNMANAGED_QUERY_CONTEXT'\n\t\t\t| 'MANAGED_READ_BUDGET'\n\t\t\t| 'POLICY_RECURSION'\n\t\t\t| 'UNSUPPORTED_MANAGED_READ',\n\t) {\n\t\tsuper(code)\n\t\tthis.name = 'ManagedReadError'\n\t}\n}\n\ninterface Authority<DM extends GenericDataModel> {\n\tscope: ManagedScope\n\trules: Rules<SecurityPolicyContext<DM>, DM>\n\tdefaultPolicy: 'allow' | 'deny'\n}\ninterface Capability<DM extends GenericDataModel> extends Authority<DM> {\n\treader: GenericDatabaseReader<DM>\n}\n// Type erasure stays inside the private identity map; registration retains native DM.\nconst capabilities = new WeakMap<object, Capability<any>>()\n\n/** Unique closure-only ordinary facade: helper implementation fields never leak.\n * Ordinary operations preserve their native/helper behavior and receive no new\n * engine ceilings. Register only from query constructors, never mutations/actions.\n */\nexport function registerQueryReader<DM extends GenericDataModel>(\n\traw: GenericDatabaseReader<DM>,\n\tordinary: GenericDatabaseReader<DM>,\n\tauthority: Authority<DM>,\n): GenericDatabaseReader<DM> {\n\t// SAFETY: all base-reader methods are bound forwards; the new identity hides helper fields.\n\tconst facade: GenericDatabaseReader<DM> = Object.freeze({\n\t\tget: ordinary.get.bind(ordinary),\n\t\tquery: <Table extends Parameters<typeof ordinary.query>[0]>(table: Table) =>\n\t\t\tordinaryQuery(ordinary.query(table)),\n\t\tnormalizeId: ordinary.normalizeId.bind(ordinary),\n\t\tsystem: Object.freeze({\n\t\t\tget: ordinary.system.get.bind(ordinary.system),\n\t\t\tquery: <Table extends Parameters<typeof ordinary.system.query>[0]>(table: Table) =>\n\t\t\t\tordinaryQuery(ordinary.system.query(table)),\n\t\t\tnormalizeId: ordinary.system.normalizeId.bind(ordinary.system),\n\t\t}),\n\t})\n\t// SAFETY: capabilities are retrieved using the same facade with its native DM.\n\tcapabilities.set(facade, { reader: raw, ...snapshot(authority) })\n\treturn facade\n}\n\nexport interface ManagedCounters {\n\treadonly readOperations: number\n\treadonly indexRanges: number\n\treadonly rawCandidates: number\n\treadonly documentBytesObserved: number\n\treadonly documentBytesReserved: number\n\treadonly fanout: number\n\treadonly memoEntries: number\n}\nexport interface LeafReservation {\n\t/** Settle/refund exactly once, including missing/error results (null). */\n\tsettle(document: GenericDocument | null): void\n}\nexport interface ManagedReadRequest<DM extends GenericDataModel> {\n\treadonly db: GenericDatabaseReader<DM>\n\treadonly scope: ManagedScope & { readonly policyVersion: string }\n\treadonly budgets: Readonly<ManagedBudgets>\n\treadonly counters: ManagedCounters\n\t/** Only the private search operation enables atomic descendant completion. */\n\treadonly atomicCompletion: boolean\n\tsearch(\n\t\ttable: string,\n\t\tindex: SearchIndexMetadata,\n\t\ttext: string,\n\t\tequalities: readonly { field: string; value: FilterScalar }[],\n\t\toptions: PaginationOptions,\n\t): Promise<PaginationResult<GenericDocument>>\n\t/** Future native leaf adapter reserves BEFORE every actual next/get, not an outer stream. */\n\treserveLeaf(): LeafReservation\n\t/** Only raw leaf reservation failures from this request can be resumed by the executor. */\n\tleafBudgetFailure(error: ManagedReadError): 'row-budget' | 'byte-budget' | undefined\n\topenRange(): void\n\texpand(depth: number, count?: number): void\n\t/** Raw candidate visibility for the private executor; every leaf is metered.\n\t * Its iterator reserves each next internally; do not add an outer reservation.\n\t * Eligibility/authorization must complete before its key is eligible to advance.\n\t */\n\tleafQuery(table: string): QueryInitializer<any>\n\tauthorize(table: string, document: GenericDocument): Promise<boolean>\n}\n\nexport interface ManagedSecurityConfig<DM extends GenericDataModel> {\n\t/** Entire registry: parent/junction/target/policy dependencies share one request. */\n\ttables: readonly string[]\n\tpolicyVersion: string\n\tbudgets?: Partial<ManagedBudgets>\n\t/** Central server trust assertion. Never inspect third-party wrapper fields.\n\t * The adapter must attest a QUERY transaction reader and the exact effective\n\t * composed policy and immutable scope. It assumes responsibility for that claim.\n\t */\n\ttrustedAdapter?: (\n\t\tctx: ManagedQueryContext<DM>,\n\t) => (Authority<DM> & { reader: GenericDatabaseReader<DM> }) | null\n}\n\n/** Native writer/action contexts are not accepted by the engine contract. */\nexport type ManagedQueryContext<DM extends GenericDataModel> = {\n\tdb: GenericDatabaseReader<DM> & {\n\t\tinsert?: never\n\t\tpatch?: never\n\t\treplace?: never\n\t\tdelete?: never\n\t}\n}\n\n/** Central construction, then one shared request per engine invocation.\n * Requests may lower, never raise ceilings. Unknown/native contexts fail closed.\n */\nexport function createManagedSecurity<DM extends GenericDataModel = GenericDataModel>(\n\tconfig: ManagedSecurityConfig<DM>,\n) {\n\tif (\n\t\t!config.policyVersion ||\n\t\t!config.tables.length ||\n\t\tnew Set(config.tables).size !== config.tables.length ||\n\t\tconfig.tables.some((table) => !table || table.startsWith('_'))\n\t)\n\t\tthrow new Error('Invalid managed security configuration')\n\tconst tables = new Set(config.tables)\n\t// Central values may exceed starting defaults; these are application-owned ceilings.\n\tlimits(config.budgets)\n\tconst central = Object.freeze({ ...defaultBudgets, ...config.budgets })\n\tconst policyVersion = config.policyVersion\n\tconst trustedAdapter = config.trustedAdapter\n\tfunction admit(ctx: ManagedQueryContext<DM>): Capability<DM> {\n\t\t// SAFETY: identity is registered privately by the query constructor for this DM.\n\t\tconst registered = capabilities.get(ctx.db) as Capability<DM> | undefined\n\t\tconst adapted = registered ? undefined : trustedAdapter?.(ctx)\n\t\tif (!registered && !adapted) throw new ManagedReadError('UNMANAGED_QUERY_CONTEXT')\n\t\treturn registered ?? { ...adapted!, ...snapshot(adapted!) }\n\t}\n\tfunction managed<Ctx extends ManagedQueryContext<DM>>(\n\t\tctx: Ctx,\n\t\tlower?: Partial<ManagedBudgets>,\n\t): ManagedReadRequest<DM> {\n\t\treturn request(admit(ctx), tables, limits(lower, central), policyVersion)\n\t}\n\treturn Object.assign(managed, {\n\t\t/** Scope-only admission for public cursors. Never returns the registered reader. */\n\t\tcursorScope(ctx: ManagedQueryContext<DM>): ManagedScope & { readonly policyVersion: string } {\n\t\t\tif (!ctx?.db || ['insert', 'patch', 'replace', 'delete'].some((key) => key in ctx.db))\n\t\t\t\tthrow new ManagedReadError('UNMANAGED_QUERY_CONTEXT')\n\t\t\treturn Object.freeze({ ...admit(ctx).scope, policyVersion })\n\t\t},\n\t})\n}\n\n/** Ordinary rule invocations share a policy-only meter. Ordinary db reads stay\n * unmetered by engine ceilings; supplied rule dependencies never bypass policy.\n * Mutation insert/modify receive the same supplied contract as read callbacks.\n */\nexport function suppliedPolicyRules<DM extends GenericDataModel>(\n\traw: GenericDatabaseReader<DM>,\n\trules: Rules<SecurityPolicyContext<DM>, DM>,\n\tdefaultPolicy: 'allow' | 'deny',\n): Rules<SecurityPolicyContext<DM>, DM> {\n\tconst cap = {\n\t\treader: raw,\n\t\t...snapshot({\n\t\t\trules,\n\t\t\tdefaultPolicy,\n\t\t\tscope: {\n\t\t\t\tmode: 'trusted',\n\t\t\t\tprincipal: null,\n\t\t\t\torganization: null,\n\t\t\t\ttenant: null,\n\t\t\t\trole: null,\n\t\t\t},\n\t\t}),\n\t}\n\t// Mutations can change dependencies between callbacks; never memoize ordinary\n\t// policy reads across writes. The shared policy meter still bounds all calls.\n\tconst session = request(cap, null, defaultBudgets, 'ordinary-policy', false)\n\tconst result: Rules<SecurityPolicyContext<DM>, DM> = {}\n\tfor (const table of Object.keys(rules)) {\n\t\tconst operations = rules[table]\n\t\tconst wrapped: NonNullable<typeof operations> = {}\n\t\tif (operations?.read) wrapped.read = (_ctx, doc) => session.authorize(table, doc)\n\t\tif (operations?.insert)\n\t\t\twrapped.insert = (_ctx, doc) => operations.insert!({ db: session.db }, doc)\n\t\tif (operations?.modify)\n\t\t\twrapped.modify = (_ctx, doc) => operations.modify!({ db: session.db }, doc)\n\t\t// SAFETY: the keys originated from this same Rules map.\n\t\tresult[table as keyof typeof result] = wrapped\n\t}\n\treturn result\n}\n\n/** Forward only native query methods, never the RLS helper's unfiltered fields.\n * Ordinary consumption retains its existing policy and has no engine ceilings.\n */\nfunction ordinaryQuery<T extends GenericTableInfo>(\n\tsource: QueryInitializer<T>,\n): QueryInitializer<T> {\n\t// SAFETY: native fluent methods retain the same table metadata; ordered results\n\t// only expose methods the native/helper source itself can successfully invoke.\n\treturn Object.freeze({\n\t\tfullTableScan: () => ordinaryQuery(source.fullTableScan() as QueryInitializer<T>),\n\t\twithIndex: (...args: Parameters<typeof source.withIndex>) =>\n\t\t\tordinaryQuery(source.withIndex(...args) as QueryInitializer<T>),\n\t\twithSearchIndex: (...args: Parameters<typeof source.withSearchIndex>) =>\n\t\t\tordinaryQuery(source.withSearchIndex(...args) as QueryInitializer<T>),\n\t\tfilter: (...args: Parameters<typeof source.filter>) =>\n\t\t\tordinaryQuery(source.filter(...args) as QueryInitializer<T>),\n\t\torder: (...args: Parameters<typeof source.order>) =>\n\t\t\tordinaryQuery(source.order(...args) as QueryInitializer<T>),\n\t\tpaginate: source.paginate.bind(source),\n\t\tcollect: source.collect.bind(source),\n\t\ttake: source.take.bind(source),\n\t\tfirst: source.first.bind(source),\n\t\tunique: source.unique.bind(source),\n\t\t[Symbol.asyncIterator]: () => {\n\t\t\tconst iterator = source[Symbol.asyncIterator]()\n\t\t\tconst facade: typeof iterator = {\n\t\t\t\tnext: iterator.next.bind(iterator),\n\t\t\t}\n\t\t\tif (iterator.return) facade.return = iterator.return.bind(iterator)\n\t\t\tif (iterator.throw) facade.throw = iterator.throw.bind(iterator)\n\t\t\treturn Object.freeze(facade)\n\t\t},\n\t}) as QueryInitializer<T>\n}\n\nfunction limits(\n\toverrides: Partial<ManagedBudgets> = {},\n\tcentral = defaultBudgets,\n): Readonly<ManagedBudgets> {\n\tfor (const [key, value] of Object.entries(overrides)) {\n\t\tif (!Object.hasOwn(defaultBudgets, key) || !Number.isSafeInteger(value) || value <= 0)\n\t\t\tthrow new Error('Invalid managed read budgets')\n\t}\n\treturn Object.freeze({\n\t\tsearchCandidates: Math.min(\n\t\t\tcentral.searchCandidates,\n\t\t\toverrides.searchCandidates ?? central.searchCandidates,\n\t\t),\n\t\treadOperations: Math.min(\n\t\t\tcentral.readOperations,\n\t\t\toverrides.readOperations ?? central.readOperations,\n\t\t),\n\t\tindexRanges: Math.min(central.indexRanges, overrides.indexRanges ?? central.indexRanges),\n\t\trawCandidates: Math.min(\n\t\t\tcentral.rawCandidates,\n\t\t\toverrides.rawCandidates ?? central.rawCandidates,\n\t\t),\n\t\tdocumentBytes: Math.min(\n\t\t\tcentral.documentBytes,\n\t\t\toverrides.documentBytes ?? central.documentBytes,\n\t\t),\n\t\tfanout: Math.min(central.fanout, overrides.fanout ?? central.fanout),\n\t\tdepth: Math.min(central.depth, overrides.depth ?? central.depth),\n\t\tmemoEntries: Math.min(central.memoEntries, overrides.memoEntries ?? central.memoEntries),\n\t})\n}\n\nfunction snapshot<DM extends GenericDataModel>(authority: Authority<DM>): Authority<DM> {\n\tconst rules = Object.fromEntries(\n\t\tObject.entries(authority.rules).map(([table, rules]) => [table, Object.freeze({ ...rules })]),\n\t)\n\treturn Object.freeze({\n\t\tscope: Object.freeze({ ...authority.scope }),\n\t\t// SAFETY: only the table keys and operation callbacks of Rules were copied.\n\t\trules: Object.freeze(rules) as Rules<SecurityPolicyContext<DM>, DM>,\n\t\tdefaultPolicy: authority.defaultPolicy,\n\t})\n}\n\nfunction request<DM extends GenericDataModel>(\n\tcap: Capability<DM>,\n\ttables: ReadonlySet<string> | null,\n\tbudgets: Readonly<ManagedBudgets>,\n\tpolicyVersion: string,\n\tmemoize = true,\n): ManagedReadRequest<DM> {\n\tconst counts = {\n\t\treadOperations: 0,\n\t\tindexRanges: 0,\n\t\trawCandidates: 0,\n\t\tdocumentBytesObserved: 0,\n\t\tdocumentBytesReserved: 0,\n\t\tfanout: 0,\n\t\tmemoEntries: 0,\n\t}\n\tconst view: ManagedCounters = Object.freeze({\n\t\tget readOperations() {\n\t\t\treturn counts.readOperations\n\t\t},\n\t\tget indexRanges() {\n\t\t\treturn counts.indexRanges\n\t\t},\n\t\tget rawCandidates() {\n\t\t\treturn counts.rawCandidates\n\t\t},\n\t\tget documentBytesObserved() {\n\t\t\treturn counts.documentBytesObserved\n\t\t},\n\t\tget documentBytesReserved() {\n\t\t\treturn counts.documentBytesReserved\n\t\t},\n\t\tget fanout() {\n\t\t\treturn counts.fanout\n\t\t},\n\t\tget memoEntries() {\n\t\t\treturn counts.memoEntries\n\t\t},\n\t})\n\tlet searchUsed = false\n\tconst searchCounts = { rawCandidates: 0, documentBytes: 0 }\n\tconst memo = new Map<string, GenericDocument | null>()\n\tlet pendingMemo = 0\n\tlet pendingCandidates = 0\n\tconst leafFailures = new WeakMap<ManagedReadError, 'row-budget' | 'byte-budget'>()\n\tconst leafLimit = () => {\n\t\tif (\n\t\t\tcounts.readOperations >= budgets.readOperations ||\n\t\t\tcounts.rawCandidates + pendingCandidates >= budgets.rawCandidates\n\t\t)\n\t\t\treturn 'row-budget'\n\t\tif (\n\t\t\tcounts.documentBytesObserved + counts.documentBytesReserved + MAX_DOCUMENT_BYTES >\n\t\t\tbudgets.documentBytes\n\t\t)\n\t\t\treturn 'byte-budget'\n\t\treturn undefined\n\t}\n\tconst fail = (): never => {\n\t\tthrow new ManagedReadError('MANAGED_READ_BUDGET')\n\t}\n\tconst assertTable = (table: string) => {\n\t\tif (table.startsWith('_') || (tables && !tables.has(table)))\n\t\t\tthrow new ManagedReadError('UNSUPPORTED_MANAGED_READ')\n\t}\n\tconst openRange = () => {\n\t\tif (counts.indexRanges >= budgets.indexRanges) fail()\n\t\tcounts.indexRanges++\n\t}\n\tconst reserveLeaf = (): LeafReservation => {\n\t\tif (leafLimit()) fail()\n\t\tcounts.readOperations++\n\t\tpendingCandidates++\n\t\tcounts.documentBytesReserved += MAX_DOCUMENT_BYTES\n\t\tlet settled = false\n\t\treturn {\n\t\t\tsettle(document) {\n\t\t\t\tif (settled) throw new Error('Leaf reservation already settled')\n\t\t\t\tsettled = true\n\t\t\t\tpendingCandidates--\n\t\t\t\tcounts.documentBytesReserved -= MAX_DOCUMENT_BYTES\n\t\t\t\tif (document) {\n\t\t\t\t\tcounts.rawCandidates++\n\t\t\t\t\tconst size = getDocumentSize(document)\n\t\t\t\t\tcounts.documentBytesObserved += size\n\t\t\t\t\tif (size > MAX_DOCUMENT_BYTES) fail()\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n\tconst keyOf = (table: string, id: string) => JSON.stringify([table, id])\n\tconst checkCycle = (key: string, ancestors: readonly string[]) => {\n\t\tif (ancestors.includes(key)) throw new ManagedReadError('POLICY_RECURSION')\n\t\tif (ancestors.length >= budgets.depth) fail()\n\t}\n\tasync function authorize(\n\t\ttable: string,\n\t\tdoc: GenericDocument,\n\t\tancestors: readonly string[] = [],\n\t): Promise<boolean> {\n\t\tassertTable(table)\n\t\t// SAFETY: native documents carry a string system ID; normalizeId additionally verifies the declared table.\n\t\tconst id = doc._id as GenericId<string>\n\t\tif (!cap.reader.normalizeId(table, id)) throw new ManagedReadError('UNSUPPORTED_MANAGED_READ')\n\t\tconst key = keyOf(table, id)\n\t\tcheckCycle(key, ancestors)\n\t\tconst rule = cap.rules[table]?.read\n\t\treturn rule\n\t\t\t? rule(Object.freeze({ db: reader([...ancestors, key]) }), doc)\n\t\t\t: cap.defaultPolicy === 'allow'\n\t}\n\tasync function get(\n\t\ttable: string,\n\t\tid: string,\n\t\tancestors: readonly string[],\n\t): Promise<GenericDocument | null> {\n\t\tassertTable(table)\n\t\tconst key = keyOf(table, id)\n\t\tcheckCycle(key, ancestors) // BEFORE consulting memo: active policy cannot await itself.\n\t\tif (memo.has(key)) return memo.get(key)!\n\t\tif (memoize && counts.memoEntries >= budgets.memoEntries) fail()\n\t\t// Completed results only: independent concurrent branches cannot deadlock\n\t\t// through mutually pending memo promises. Concurrent duplicates may fetch twice.\n\t\tif (memoize) {\n\t\t\tpendingMemo++\n\t\t\tcounts.memoEntries = memo.size + pendingMemo\n\t\t}\n\t\ttry {\n\t\t\topenRange()\n\t\t\tconst reservation = reserveLeaf()\n\t\t\tlet doc: GenericDocument | null\n\t\t\ttry {\n\t\t\t\t// SAFETY: IDs come from native readers or caller-branded IDs, with explicit table identity.\n\t\t\t\tdoc = await cap.reader.get(table, id as GenericId<string>)\n\t\t\t} catch (error) {\n\t\t\t\treservation.settle(null)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\treservation.settle(doc)\n\t\t\tconst result = doc && (await authorize(table, doc, ancestors)) ? doc : null\n\t\t\tif (memoize) memo.set(key, result)\n\t\t\treturn result\n\t\t} finally {\n\t\t\tif (memoize) {\n\t\t\t\tpendingMemo--\n\t\t\t\tcounts.memoEntries = memo.size + pendingMemo\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction query(\n\t\ttable: string,\n\t\tsource: QueryInitializer<any>,\n\t\tancestors: readonly string[],\n\t\tauthorized: boolean,\n\t): QueryInitializer<any> {\n\t\tfunction unsupported(): never {\n\t\t\tthrow new ManagedReadError('UNSUPPORTED_MANAGED_READ')\n\t\t}\n\t\tasync function* iterate() {\n\t\t\topenRange()\n\t\t\tlet iterator: AsyncIterator<GenericDocument> | undefined\n\t\t\ttry {\n\t\t\t\twhile (true) {\n\t\t\t\t\t// Mark only this raw leaf's pre-fetch reservation, never native or policy errors.\n\t\t\t\t\tconst reason = !authorized && leafLimit()\n\t\t\t\t\tif (reason) {\n\t\t\t\t\t\tconst error = new ManagedReadError('MANAGED_READ_BUDGET')\n\t\t\t\t\t\tleafFailures.set(error, reason)\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t\tconst reservation = reserveLeaf()\n\t\t\t\t\tlet next: IteratorResult<GenericDocument>\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Native iterator construction starts the query. Reserve before\n\t\t\t\t\t\t// both initialization and next; every later next reserves anew.\n\t\t\t\t\t\titerator ??= source[Symbol.asyncIterator]()\n\t\t\t\t\t\tnext = await iterator.next()\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treservation.settle(null)\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t\treservation.settle(next.done ? null : next.value)\n\t\t\t\t\tif (next.done) return\n\t\t\t\t\tif (!authorized || (await authorize(table, next.value, ancestors))) yield next.value\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tawait iterator?.return?.()\n\t\t\t}\n\t\t}\n\t\tasync function take(n: number) {\n\t\t\tif (!Number.isSafeInteger(n) || n < 0 || n > budgets.rawCandidates)\n\t\t\t\tthrow new ManagedReadError('MANAGED_READ_BUDGET')\n\t\t\tconst docs: GenericDocument[] = []\n\t\t\tif (n === 0) return docs\n\t\t\tfor await (const doc of iterate()) {\n\t\t\t\tdocs.push(doc)\n\t\t\t\tif (docs.length === n) break\n\t\t\t}\n\t\t\treturn docs\n\t\t}\n\t\t// SAFETY: forwarding preserves native document/index types; only consumers are replaced.\n\t\treturn Object.freeze({\n\t\t\tfullTableScan: () =>\n\t\t\t\tquery(table, source.fullTableScan() as QueryInitializer<any>, ancestors, authorized),\n\t\t\twithIndex: (...args: Parameters<typeof source.withIndex>) =>\n\t\t\t\tquery(table, source.withIndex(...args) as QueryInitializer<any>, ancestors, authorized),\n\t\t\torder: (...args: Parameters<typeof source.order>) =>\n\t\t\t\tquery(table, source.order(...args) as QueryInitializer<any>, ancestors, authorized),\n\t\t\t// Native filters/search/pagination may scan/prefetch hidden rows. They\n\t\t\t// cannot prove per-leaf reservation via public APIs; reject on managed readers.\n\t\t\tfilter: unsupported,\n\t\t\twithSearchIndex: unsupported,\n\t\t\tpaginate: unsupported,\n\t\t\tcollect: unsupported,\n\t\t\ttake,\n\t\t\tfirst: async () => (await take(1))[0] ?? null,\n\t\t\tunique: async () => {\n\t\t\t\tconst docs = await take(2)\n\t\t\t\tif (docs.length > 1) throw new Error('Query is not unique')\n\t\t\t\treturn docs[0] ?? null\n\t\t\t},\n\t\t\t[Symbol.asyncIterator]: iterate,\n\t\t}) as QueryInitializer<any>\n\t}\n\tfunction reader(ancestors: readonly string[]): GenericDatabaseReader<any> {\n\t\tconst blocked = () => {\n\t\t\tthrow new ManagedReadError('UNSUPPORTED_MANAGED_READ')\n\t\t}\n\t\t// SAFETY: normalizeId retains the requested table brand; query forwarding retains native table metadata.\n\t\treturn Object.freeze({\n\t\t\tget: async (arg0: string, arg1?: string) => {\n\t\t\t\tif (arg1 !== undefined) return get(arg0, arg1, ancestors)\n\t\t\t\t// Resolve only from the explicit registry (or known policy tables for\n\t\t\t\t// ordinary policy callbacks). Never raw-get an unrecognized ID.\n\t\t\t\tconst table = [...(tables ?? new Set(Object.keys(cap.rules)))].find((name) =>\n\t\t\t\t\tcap.reader.normalizeId(name, arg0),\n\t\t\t\t)\n\t\t\t\treturn table ? get(table, arg0, ancestors) : null\n\t\t\t},\n\t\t\tquery: (table: string) => {\n\t\t\t\tassertTable(table)\n\t\t\t\treturn query(table, cap.reader.query(table), ancestors, true)\n\t\t\t},\n\t\t\tnormalizeId: (table: string, id: string) => {\n\t\t\t\tassertTable(table)\n\t\t\t\treturn cap.reader.normalizeId(table, id)\n\t\t\t},\n\t\t\tsystem: Object.freeze({\n\t\t\t\tget: blocked,\n\t\t\t\tquery: blocked,\n\t\t\t\tnormalizeId: blocked,\n\t\t\t}),\n\t\t}) as GenericDatabaseReader<any>\n\t}\n\treturn Object.freeze({\n\t\tdb: reader([]),\n\t\tscope: Object.freeze({ ...cap.scope, policyVersion }),\n\t\tbudgets,\n\t\tcounters: view,\n\t\tget atomicCompletion() {\n\t\t\treturn searchUsed\n\t\t},\n\t\tasync search(\n\t\t\ttable: string,\n\t\t\tindex: SearchIndexMetadata,\n\t\t\ttext: string,\n\t\t\tequalities: readonly { field: string; value: FilterScalar }[],\n\t\t\toptions: PaginationOptions,\n\t\t) {\n\t\t\tassertTable(table)\n\t\t\tif (searchUsed) throw new ManagedReadError('UNSUPPORTED_MANAGED_READ')\n\t\t\tsearchUsed = true\n\t\t\tif (counts.readOperations >= budgets.readOperations) fail()\n\t\t\topenRange()\n\t\t\tcounts.readOperations++\n\t\t\t// SAFETY: registered immutable metadata admits the native search field and equality fields.\n\t\t\tconst source: QueryInitializer<any> = cap.reader.query(table)\n\t\t\tconst result = await source\n\t\t\t\t.withSearchIndex(index.name, (q) => {\n\t\t\t\t\tlet filter = q.search(index.searchField, text)\n\t\t\t\t\tfor (const { field, value } of equalities) filter = filter.eq(field, value)\n\t\t\t\t\treturn filter\n\t\t\t\t})\n\t\t\t\t.paginate(options)\n\t\t\tsearchCounts.rawCandidates += result.page.length\n\t\t\tfor (const document of result.page) searchCounts.documentBytes += getDocumentSize(document)\n\t\t\treturn result\n\t\t},\n\t\treserveLeaf,\n\t\tleafBudgetFailure: (error: ManagedReadError) => leafFailures.get(error),\n\t\topenRange,\n\t\texpand(depth: number, count = 1) {\n\t\t\tif (\n\t\t\t\t!Number.isSafeInteger(depth) ||\n\t\t\t\tdepth < 0 ||\n\t\t\t\tdepth > budgets.depth ||\n\t\t\t\t!Number.isSafeInteger(count) ||\n\t\t\t\tcount < 0 ||\n\t\t\t\tcounts.fanout + count > budgets.fanout\n\t\t\t)\n\t\t\t\tfail()\n\t\t\tcounts.fanout += count\n\t\t},\n\t\tleafQuery: (table: string) => {\n\t\t\tassertTable(table)\n\t\t\treturn query(table, cap.reader.query(table), [], false)\n\t\t},\n\t\tauthorize: (table: string, doc: GenericDocument) => authorize(table, doc),\n\t})\n}\n"],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ //#region src/querying/shared/ast.d.ts
2
+ export type FilterScalar = string | number | bigint | boolean | null;
3
+ export type FilterOperator = 'eq' | 'in' | 'gt' | 'gte' | 'lt' | 'lte' | 'exists';
4
+ export type FilterAst = {
5
+ kind: 'and' | 'or';
6
+ children: FilterAst[];
7
+ } | {
8
+ kind: 'not';
9
+ child: FilterAst;
10
+ } | {
11
+ kind: 'variant';
12
+ discriminator: string;
13
+ tag: string;
14
+ child: FilterAst;
15
+ } | {
16
+ kind: 'scalar';
17
+ field: string;
18
+ operator: FilterOperator;
19
+ value: FilterScalar | FilterScalar[];
20
+ } | {
21
+ kind: 'relation';
22
+ name: string;
23
+ mode: 'one' | 'some' | 'none';
24
+ predicate: FilterAst | null;
25
+ };
26
+ export declare function valueEqual(left: FilterScalar, right: FilterScalar): boolean;
27
+ //#endregion
28
+ //# sourceMappingURL=ast.d.mts.map
@@ -0,0 +1,2 @@
1
+ import{compareValues as e}from"convex/values";function t(t,n){return e(t,n)===0}export{t as valueEqual};
2
+ //# sourceMappingURL=ast.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.mjs","names":["valueEqual","compareValues","left","right"],"sources":["../../../src/querying/shared/ast.ts"],"sourcesContent":["import { compareValues } from 'convex/values'\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 }\n\nexport function valueEqual(left: FilterScalar, right: FilterScalar): boolean {\n\treturn compareValues(left, right) === 0\n}\n"],"mappings":"8CAeA,SAAgBA,EAAW,EAAoB,EAA8B,CAC5E,OAAOC,EAAcC,EAAMC,CAAK,IAAM,CACvC"}
@@ -0,0 +1,104 @@
1
+ import { z } from "zod";
2
+ import { GenericId, GenericValidator, ValidatorJSON, Value } from "convex/values";
3
+ //#region src/querying/shared/contract.d.ts
4
+ export declare function sharedNativeValidator(schema: z.core.$ZodType): GenericValidator | undefined;
5
+ type RequiredSharedField<V extends GenericValidator> = V extends {
6
+ kind: 'string';
7
+ } ? z.ZodString : V extends {
8
+ kind: 'float64';
9
+ } ? z.ZodNumber : V extends {
10
+ kind: 'int64';
11
+ } ? z.ZodBigInt : V extends {
12
+ kind: 'boolean';
13
+ } ? z.ZodBoolean : V extends {
14
+ kind: 'null';
15
+ } ? z.ZodNull : V extends {
16
+ kind: 'literal';
17
+ value: infer L extends string | number | bigint | boolean;
18
+ } ? z.ZodLiteral<L> : V extends {
19
+ kind: 'id';
20
+ } ? z.ZodCustom<Extract<V['type'], GenericId<string>>, string> : V extends {
21
+ kind: 'union';
22
+ members: [];
23
+ } ? z.ZodUnion<[]> : V extends {
24
+ kind: 'union';
25
+ members: [infer A extends GenericValidator, ...infer R extends GenericValidator[]];
26
+ } ? z.ZodUnion<readonly [SharedField<A>, ...{ [K in keyof R]: SharedField<R[K]>; }]> : V extends {
27
+ kind: 'union';
28
+ members: (infer E extends GenericValidator)[];
29
+ } ? [GenericValidator] extends [E] ? z.ZodUnion<readonly z.core.SomeType[]> : z.ZodUnion<readonly SharedField<E>[]> : never;
30
+ export type SharedField<V extends GenericValidator> = V extends {
31
+ isOptional: 'optional';
32
+ } ? z.ZodOptional<RequiredSharedField<V>> : RequiredSharedField<V>;
33
+ type FieldSchemas<F extends Record<string, GenericValidator>> = { [K in keyof F]: SharedField<F[K]>; };
34
+ type FilterSide = 'input' | 'output';
35
+ type InputValue<V> = V extends GenericId<string> ? string : V;
36
+ type Scalar<V extends GenericValidator, S extends FilterSide> = S extends 'input' ? InputValue<Exclude<V['type'], undefined>> : Exclude<V['type'], undefined>;
37
+ type Ordered<V extends GenericValidator> = [GenericValidator] extends [V] ? false : V extends {
38
+ kind: 'string' | 'float64' | 'int64';
39
+ } ? true : V extends {
40
+ kind: 'literal';
41
+ } ? Exclude<V['type'], undefined> extends string | number | bigint ? true : false : V extends {
42
+ kind: 'union';
43
+ members: infer M extends GenericValidator[];
44
+ } ? false extends Ordered<M[number]> ? false : true : false;
45
+ type Predicate<V extends GenericValidator, S extends FilterSide> = Scalar<V, S> | ({
46
+ eq?: Scalar<V, S>;
47
+ in?: Scalar<V, S>[];
48
+ } & (Ordered<V> extends true ? {
49
+ gt?: Scalar<V, S>;
50
+ gte?: Scalar<V, S>;
51
+ lt?: Scalar<V, S>;
52
+ lte?: Scalar<V, S>;
53
+ } : {}) & (V['isOptional'] extends 'optional' ? {
54
+ exists?: boolean;
55
+ } : {}));
56
+ type Fields<F extends Record<string, GenericValidator>, S extends FilterSide> = { [K in keyof F]?: Predicate<F[K], S>; };
57
+ type Tail<D extends 1[]> = D extends [1, ...infer R extends 1[]] ? R : [];
58
+ export type SharedFilters<F extends Record<string, GenericValidator>, S extends FilterSide = 'output', D extends 1[] = [1, 1, 1, 1]> = Omit<Fields<F, S>, 'and' | 'or' | 'not' | 'fields'> & {
59
+ fields?: Fields<F, S>;
60
+ } & (D extends [] ? {} : {
61
+ and?: SharedFilters<F, S, Tail<D>>[];
62
+ or?: SharedFilters<F, S, Tail<D>>[];
63
+ not?: SharedFilters<F, S, Tail<D>>;
64
+ });
65
+ export interface FilterDescriptor {
66
+ field: string;
67
+ kind: 'string' | 'number' | 'bigint' | 'boolean' | 'null' | 'id' | 'enum' | 'union';
68
+ optional: boolean;
69
+ nullable: boolean;
70
+ values?: readonly (string | number | bigint | boolean)[];
71
+ idTables: readonly string[];
72
+ operators: readonly string[];
73
+ members?: readonly FilterDescriptor[];
74
+ }
75
+ export interface FilterContract<F extends Record<string, GenericValidator>> {
76
+ readonly fields: FieldSchemas<F>;
77
+ readonly native: Readonly<Record<string, {
78
+ json: ValidatorJSON;
79
+ optional: boolean;
80
+ }>>;
81
+ readonly schema: z.ZodType<SharedFilters<F>, SharedFilters<F, 'input'>>;
82
+ describe(): readonly FilterDescriptor[];
83
+ evaluate(filters: SharedFilters<F, 'input'>, document: Record<string, Value | undefined>, accessible?: ReadonlySet<string>): boolean | 'unknown';
84
+ readonly searchParam: {
85
+ encode(value: SharedFilters<F, 'input'>): {
86
+ success: true;
87
+ value: string;
88
+ } | {
89
+ success: false;
90
+ error: string;
91
+ };
92
+ decode(value: string): {
93
+ success: true;
94
+ value: SharedFilters<F>;
95
+ } | {
96
+ success: false;
97
+ error: string;
98
+ };
99
+ };
100
+ }
101
+ /** Scalar-only contracts explicitly reject objects, arrays, relations and variants. */
102
+ export declare function defineFilterContract<const F extends Record<string, GenericValidator>>(input: F): FilterContract<F>;
103
+ //#endregion
104
+ //# sourceMappingURL=contract.d.mts.map
@@ -0,0 +1,2 @@
1
+ import{boundedFilterSchema as e,normalizeFilters as t,scalarPredicateFromSchema as n}from"./filters.mjs";import{evaluateFilter as r}from"./evaluate.mjs";import{z as i}from"zod";import{convexToJson as a,jsonToConvex as o,v as s}from"convex/values";const c=/* @__PURE__ */ new WeakMap,l=/* @__PURE__ */ new WeakMap;function u(e){let t=c.get(e)??(e instanceof i.core.$ZodCustom?l.get(e._zod.def.fn):void 0);return t?v(t):void 0}function d(i){let s={},c={},l={},u=[];for(let[e,t]of Object.entries(i)){if([`__proto__`,`constructor`,`prototype`].includes(e))throw Error(`Unsupported filter field`);let r=f(t);s[e]=t.isOptional===`optional`?r.optional():r;let i=[`eq`,`in`];p(t)&&i.push(`gt`,`gte`,`lt`,`lte`),t.isOptional===`optional`&&i.push(`exists`),c[e]=n(r,p(t),t.isOptional===`optional`).optional(),l[e]={json:_(t),optional:t.isOptional===`optional`},u.push(m(e,t,i))}let d=e(c);return Object.freeze({fields:Object.freeze(s),native:y(l),schema:d,describe:()=>structuredClone(u),evaluate:(e,n,i)=>r(t(d.parse(e)),n,i),searchParam:Object.freeze({encode:e=>{try{if(!b(e))throw Error(`URL size exceeded`);let t=d.parse(e),n=JSON.stringify({v:1,filters:h(a(t))});if(n.length>32768)throw Error(`URL size exceeded`);return{success:!0,value:n}}catch{return{success:!1,error:`Invalid filters`}}},decode:e=>{try{if(e.length>32768)throw Error(`URL size exceeded`);let t=JSON.parse(e);if(!t||t.v!==1||Object.keys(t).sort().join(`,`)!==`filters,v`)throw Error(`Unsupported URL version`);return g(t.filters),{success:!0,value:d.parse(o(t.filters))}}catch{return{success:!1,error:`Invalid filter URL`}}}})})}function f(e){let t;switch(e.kind){case`string`:t=i.string();break;case`float64`:t=i.number();break;case`int64`:t=i.bigint().min(-(1n<<63n)).max((1n<<63n)-1n);break;case`boolean`:t=i.boolean();break;case`null`:t=i.null();break;case`literal`:t=i.literal(e.value);break;case`id`:t=i.custom(e=>i.string().safeParse(e).success);break;case`union`:t=i.union(e.members.map(f));break;default:throw Error(`Unsupported shared scalar kind "${e.kind}"; relations and variants require a server contract`)}let n=v(e,!1);return c.set(t,n),e.kind===`id`&&t instanceof i.core.$ZodCustom&&l.set(t._zod.def.fn,n),t}function p(e){return[`string`,`float64`,`int64`].includes(e.kind)||e.kind===`literal`&&i.union([i.string(),i.number(),i.bigint()]).safeParse(e.value).success||e.kind===`union`&&e.members.every(p)}function m(e,t,n){let r=t.kind===`union`?t.members.map(t=>m(e,t,n)):void 0,i=r?.every(e=>e.kind===`enum`);return{field:e,kind:t.kind===`float64`?`number`:t.kind===`int64`?`bigint`:t.kind===`literal`||i?`enum`:t.kind,optional:t.isOptional===`optional`,nullable:t.kind===`null`||!!r?.some(e=>e.nullable),values:t.kind===`literal`?[t.value]:i?r.flatMap(e=>e.values??[]):void 0,idTables:t.kind===`id`?[t.tableName]:r?.flatMap(e=>e.idTables)??[],operators:n,members:r}}function h(e){if(Array.isArray(e))return e.map(h);if(e&&typeof e==`object`){let t=e;return Object.fromEntries(Object.keys(t).sort().map(e=>[e,h(t[e])]))}return e}function g(e,t=0,n={nodes:0}){if(t>16||++n.nodes>2048)throw Error(`URL budget exceeded`);if(e&&typeof e==`object`)for(let r of Object.values(e))g(r,t+1,n)}function _(e){return e.json}function v(e,t=!0){let n;switch(e.kind){case`id`:n=s.id(e.tableName);break;case`string`:n=s.string();break;case`float64`:n=s.float64();break;case`int64`:n=s.int64();break;case`null`:n=s.null();break;case`boolean`:n=s.boolean();break;case`literal`:n=s.literal(e.value);break;case`union`:n=s.union(...e.members.map(e=>v(e)));break;default:throw Error(`Unsupported shared scalar`)}return t&&e.isOptional===`optional`?s.optional(n):n}function y(e){if(e&&typeof e==`object`){for(let t of Object.values(e))y(t);Object.freeze(e)}return e}function b(e){let t=[e],n=0,r=0;for(;t.length;){let e=t.pop();if(++r>512||typeof e==`string`&&(n+=e.length,n>32768))return!1;if(Object(e)!==e)continue;let i=e,a=Reflect.ownKeys(i);if(a.length+r+t.length>512)return!1;for(let e of a){if(Array.isArray(i)&&e===`length`)continue;let r=Object.getOwnPropertyDescriptor(i,e);if(!r||!(`value`in r)||(n+=String(e).length,n>32768))return!1;t.push(r.value)}}return!0}export{d as defineFilterContract,u as sharedNativeValidator};
2
+ //# sourceMappingURL=contract.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.mjs","names":["nativeSchemas","nativeIdPredicates","sharedNativeValidator","validator","schema","z","cloneNative","defineFilterContract","fields","predicates","native","descriptors","field","input","scalar","scalarSchema","operators","ordered","scalarPredicateFromSchema","nativeJson","describe","boundedFilterSchema","freezeMetadata","evaluateFilter","normalizeFilters","filters","document","accessible","withinUrlSize","value","parsed","encoded","canonical","convexToJson","envelope","guardJson","jsonToConvex","result","children","v","literals","c","object","k","depth","state","child","member","optional","pending","size","nodes","current","keys","key","descriptor"],"sources":["../../../src/querying/shared/contract.ts"],"sourcesContent":["import { z } from 'zod'\nimport {\n\tconvexToJson,\n\tjsonToConvex,\n\tv,\n\ttype GenericId,\n\ttype JSONValue,\n\ttype ValidatorJSON,\n\ttype GenericValidator,\n\ttype Value,\n} from 'convex/values'\nimport {\n\tboundedFilterSchema,\n\tnormalizeFilters,\n\tscalarPredicateFromSchema,\n\ttype FilterTree,\n} from './filters'\nimport { evaluateFilter } from './evaluate'\n\n// Metadata only: no parser callbacks or server state cross this boundary.\nconst nativeSchemas = new WeakMap<z.core.$ZodType, GenericValidator>()\n// Zod preserves a custom predicate through clone/describe/meta/refine. Associate\n// ID metadata with that private predicate too, so those derivations stay native IDs.\nconst nativeIdPredicates = new WeakMap<z.core.$ZodCustom['_zod']['def']['fn'], GenericValidator>()\nexport function sharedNativeValidator(schema: z.core.$ZodType): GenericValidator | undefined {\n\tconst validator =\n\t\tnativeSchemas.get(schema) ??\n\t\t(schema instanceof z.core.$ZodCustom ? nativeIdPredicates.get(schema._zod.def.fn) : undefined)\n\treturn validator ? cloneNative(validator) : undefined\n}\ntype RequiredSharedField<V extends GenericValidator> = V extends { kind: 'string' }\n\t? z.ZodString\n\t: V extends { kind: 'float64' }\n\t\t? z.ZodNumber\n\t\t: V extends { kind: 'int64' }\n\t\t\t? z.ZodBigInt\n\t\t\t: V extends { kind: 'boolean' }\n\t\t\t\t? z.ZodBoolean\n\t\t\t\t: V extends { kind: 'null' }\n\t\t\t\t\t? z.ZodNull\n\t\t\t\t\t: V extends { kind: 'literal'; value: infer L extends string | number | bigint | boolean }\n\t\t\t\t\t\t? z.ZodLiteral<L>\n\t\t\t\t\t\t: V extends { kind: 'id' }\n\t\t\t\t\t\t\t? z.ZodCustom<Extract<V['type'], GenericId<string>>, string>\n\t\t\t\t\t\t\t: V extends { kind: 'union'; members: [] }\n\t\t\t\t\t\t\t\t? z.ZodUnion<[]>\n\t\t\t\t\t\t\t\t: V extends {\n\t\t\t\t\t\t\t\t\t\t\tkind: 'union'\n\t\t\t\t\t\t\t\t\t\t\tmembers: [\n\t\t\t\t\t\t\t\t\t\t\t\tinfer A extends GenericValidator,\n\t\t\t\t\t\t\t\t\t\t\t\t...infer R extends GenericValidator[],\n\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t }\n\t\t\t\t\t\t\t\t\t? z.ZodUnion<readonly [SharedField<A>, ...{ [K in keyof R]: SharedField<R[K]> }]>\n\t\t\t\t\t\t\t\t\t: V extends { kind: 'union'; members: (infer E extends GenericValidator)[] }\n\t\t\t\t\t\t\t\t\t\t? [GenericValidator] extends [E]\n\t\t\t\t\t\t\t\t\t\t\t? z.ZodUnion<readonly z.core.SomeType[]>\n\t\t\t\t\t\t\t\t\t\t\t: z.ZodUnion<readonly SharedField<E>[]>\n\t\t\t\t\t\t\t\t\t\t: never\nexport type SharedField<V extends GenericValidator> = V extends { isOptional: 'optional' }\n\t? z.ZodOptional<RequiredSharedField<V>>\n\t: RequiredSharedField<V>\ntype FieldSchemas<F extends Record<string, GenericValidator>> = {\n\t[K in keyof F]: SharedField<F[K]>\n}\ntype FilterSide = 'input' | 'output'\ntype InputValue<V> = V extends GenericId<string> ? string : V\ntype Scalar<V extends GenericValidator, S extends FilterSide> = S extends 'input'\n\t? InputValue<Exclude<V['type'], undefined>>\n\t: Exclude<V['type'], undefined>\ntype Ordered<V extends GenericValidator> = [GenericValidator] extends [V]\n\t? false\n\t: V extends { kind: 'string' | 'float64' | 'int64' }\n\t\t? true\n\t\t: V extends { kind: 'literal' }\n\t\t\t? Exclude<V['type'], undefined> extends string | number | bigint\n\t\t\t\t? true\n\t\t\t\t: false\n\t\t\t: V extends { kind: 'union'; members: infer M extends GenericValidator[] }\n\t\t\t\t? false extends Ordered<M[number]>\n\t\t\t\t\t? false\n\t\t\t\t\t: true\n\t\t\t\t: false\ntype Predicate<V extends GenericValidator, S extends FilterSide> =\n\t| Scalar<V, S>\n\t| ({ eq?: Scalar<V, S>; in?: Scalar<V, S>[] } & (Ordered<V> extends true\n\t\t\t? { gt?: Scalar<V, S>; gte?: Scalar<V, S>; lt?: Scalar<V, S>; lte?: Scalar<V, S> }\n\t\t\t: {}) &\n\t\t\t(V['isOptional'] extends 'optional' ? { exists?: boolean } : {}))\ntype Fields<F extends Record<string, GenericValidator>, S extends FilterSide> = {\n\t[K in keyof F]?: Predicate<F[K], S>\n}\ntype Tail<D extends 1[]> = D extends [1, ...infer R extends 1[]] ? R : []\nexport type SharedFilters<\n\tF extends Record<string, GenericValidator>,\n\tS extends FilterSide = 'output',\n\tD extends 1[] = [1, 1, 1, 1],\n> = Omit<Fields<F, S>, 'and' | 'or' | 'not' | 'fields'> & { fields?: Fields<F, S> } & (D extends []\n\t\t? {}\n\t\t: {\n\t\t\t\tand?: SharedFilters<F, S, Tail<D>>[]\n\t\t\t\tor?: SharedFilters<F, S, Tail<D>>[]\n\t\t\t\tnot?: SharedFilters<F, S, Tail<D>>\n\t\t\t})\nexport interface FilterDescriptor {\n\tfield: string\n\tkind: 'string' | 'number' | 'bigint' | 'boolean' | 'null' | 'id' | 'enum' | 'union'\n\toptional: boolean\n\tnullable: boolean\n\tvalues?: readonly (string | number | bigint | boolean)[]\n\tidTables: readonly string[]\n\toperators: readonly string[]\n\tmembers?: readonly FilterDescriptor[]\n}\nexport interface FilterContract<F extends Record<string, GenericValidator>> {\n\treadonly fields: FieldSchemas<F>\n\treadonly native: Readonly<Record<string, { json: ValidatorJSON; optional: boolean }>>\n\treadonly schema: z.ZodType<SharedFilters<F>, SharedFilters<F, 'input'>>\n\tdescribe(): readonly FilterDescriptor[]\n\tevaluate(\n\t\tfilters: SharedFilters<F, 'input'>,\n\t\tdocument: Record<string, Value | undefined>,\n\t\taccessible?: ReadonlySet<string>,\n\t): boolean | 'unknown'\n\treadonly searchParam: {\n\t\tencode(\n\t\t\tvalue: SharedFilters<F, 'input'>,\n\t\t): { success: true; value: string } | { success: false; error: string }\n\t\tdecode(\n\t\t\tvalue: string,\n\t\t): { success: true; value: SharedFilters<F> } | { success: false; error: string }\n\t}\n}\n\n/** Scalar-only contracts explicitly reject objects, arrays, relations and variants. */\nexport function defineFilterContract<const F extends Record<string, GenericValidator>>(\n\tinput: F,\n): FilterContract<F> {\n\ttype Schemas = Record<string, z.ZodType>\n\tconst fields: Schemas = {},\n\t\tpredicates: Record<string, z.ZodType> = {}\n\tconst native: Record<string, { json: ValidatorJSON; optional: boolean }> = {}\n\tconst descriptors: FilterDescriptor[] = []\n\tfor (const [field, validator] of Object.entries(input)) {\n\t\tif (['__proto__', 'constructor', 'prototype'].includes(field))\n\t\t\tthrow new Error('Unsupported filter field')\n\t\tconst scalar = scalarSchema(validator)\n\t\tfields[field] = validator.isOptional === 'optional' ? scalar.optional() : scalar\n\t\tconst operators = ['eq', 'in']\n\t\tif (ordered(validator)) operators.push('gt', 'gte', 'lt', 'lte')\n\t\tif (validator.isOptional === 'optional') operators.push('exists')\n\t\tpredicates[field] = scalarPredicateFromSchema(\n\t\t\tscalar,\n\t\t\tordered(validator),\n\t\t\tvalidator.isOptional === 'optional',\n\t\t).optional()\n\t\tnative[field] = { json: nativeJson(validator), optional: validator.isOptional === 'optional' }\n\t\tdescriptors.push(describe(field, validator, operators))\n\t}\n\t// SAFETY: every selected native scalar generates precisely the mapped predicate grammar.\n\tconst schema = boundedFilterSchema(predicates) as z.ZodType<\n\t\tSharedFilters<F>,\n\t\tSharedFilters<F, 'input'>\n\t>\n\tconst encode = (value: SharedFilters<F, 'input'>) => {\n\t\t// SAFETY: bounded scalar schema outputs contain only Convex scalar values and containers.\n\t\ttry {\n\t\t\t// SAFETY: the input boundary inspects descriptors before parsing or serializing.\n\t\t\tif (!withinUrlSize(value as FilterTree)) throw new Error('URL size exceeded')\n\t\t\tconst parsed = schema.parse(value)\n\t\t\t// SAFETY: bounded schema output contains only Convex values.\n\t\t\tconst encoded = JSON.stringify({ v: 1, filters: canonical(convexToJson(parsed as Value)) })\n\t\t\tif (encoded.length > 32768) throw new Error('URL size exceeded')\n\t\t\treturn { success: true as const, value: encoded }\n\t\t} catch {\n\t\t\treturn { success: false as const, error: 'Invalid filters' }\n\t\t}\n\t}\n\tconst decode = (value: string) => {\n\t\ttry {\n\t\t\tif (value.length > 32768) throw new Error('URL size exceeded')\n\t\t\tconst envelope = JSON.parse(value)\n\t\t\tif (!envelope || envelope.v !== 1 || Object.keys(envelope).sort().join(',') !== 'filters,v')\n\t\t\t\tthrow new Error('Unsupported URL version')\n\t\t\tguardJson(envelope.filters)\n\t\t\tconst parsed = schema.parse(jsonToConvex(envelope.filters))\n\t\t\treturn { success: true as const, value: parsed }\n\t\t} catch {\n\t\t\treturn { success: false as const, error: 'Invalid filter URL' }\n\t\t}\n\t}\n\treturn Object.freeze({\n\t\t// SAFETY: fields were generated from their corresponding native validator types.\n\t\tfields: Object.freeze(fields) as FieldSchemas<F>,\n\t\tnative: freezeMetadata(native),\n\t\tschema,\n\t\tdescribe: () => structuredClone(descriptors),\n\t\t// SAFETY: the bounded scalar schema validates the tree before normalization.\n\t\tevaluate: (\n\t\t\tfilters: SharedFilters<F, 'input'>,\n\t\t\tdocument: Record<string, Value | undefined>,\n\t\t\taccessible?: ReadonlySet<string>,\n\t\t) =>\n\t\t\tevaluateFilter(normalizeFilters(schema.parse(filters) as FilterTree), document, accessible),\n\t\tsearchParam: Object.freeze({ encode, decode }),\n\t})\n}\nfunction scalarSchema(validator: GenericValidator): z.ZodType {\n\tlet result: z.ZodType\n\tswitch (validator.kind) {\n\t\tcase 'string':\n\t\t\tresult = z.string()\n\t\t\tbreak\n\t\tcase 'float64':\n\t\t\tresult = z.number()\n\t\t\tbreak\n\t\tcase 'int64':\n\t\t\tresult = z\n\t\t\t\t.bigint()\n\t\t\t\t.min(-(1n << 63n))\n\t\t\t\t.max((1n << 63n) - 1n)\n\t\t\tbreak\n\t\tcase 'boolean':\n\t\t\tresult = z.boolean()\n\t\t\tbreak\n\t\tcase 'null':\n\t\t\tresult = z.null()\n\t\t\tbreak\n\t\tcase 'literal':\n\t\t\tresult = z.literal(validator.value)\n\t\t\tbreak\n\t\tcase 'id':\n\t\t\tresult = z.custom<string>((value) => z.string().safeParse(value).success)\n\t\t\tbreak\n\t\tcase 'union':\n\t\t\tresult = z.union(validator.members.map(scalarSchema))\n\t\t\tbreak\n\t\tdefault:\n\t\t\tthrow new Error(\n\t\t\t\t`Unsupported shared scalar kind \"${validator.kind}\"; relations and variants require a server contract`,\n\t\t\t)\n\t}\n\t// Capture immutable scalar metadata. Binding and conversion never consult mutable callers.\n\tconst native = cloneNative(validator, false)\n\tnativeSchemas.set(result, native)\n\tif (validator.kind === 'id' && result instanceof z.core.$ZodCustom)\n\t\tnativeIdPredicates.set(result._zod.def.fn, native)\n\treturn result\n}\nfunction ordered(validator: GenericValidator): boolean {\n\treturn (\n\t\t['string', 'float64', 'int64'].includes(validator.kind) ||\n\t\t(validator.kind === 'literal' &&\n\t\t\tz.union([z.string(), z.number(), z.bigint()]).safeParse(validator.value).success) ||\n\t\t(validator.kind === 'union' && validator.members.every(ordered))\n\t)\n}\nfunction describe(\n\tfield: string,\n\tvalidator: GenericValidator,\n\toperators: string[],\n): FilterDescriptor {\n\tconst children =\n\t\tvalidator.kind === 'union'\n\t\t\t? validator.members.map((v) => describe(field, v, operators))\n\t\t\t: undefined\n\tconst literals = children?.every((c) => c.kind === 'enum')\n\tconst kind =\n\t\tvalidator.kind === 'float64'\n\t\t\t? 'number'\n\t\t\t: validator.kind === 'int64'\n\t\t\t\t? 'bigint'\n\t\t\t\t: validator.kind === 'literal'\n\t\t\t\t\t? 'enum'\n\t\t\t\t\t: literals\n\t\t\t\t\t\t? 'enum'\n\t\t\t\t\t\t: validator.kind\n\t// SAFETY: unsupported native kinds were rejected while generating scalar schemas.\n\treturn {\n\t\tfield,\n\t\tkind: kind as FilterDescriptor['kind'],\n\t\toptional: validator.isOptional === 'optional',\n\t\tnullable: validator.kind === 'null' || !!children?.some((c) => c.nullable),\n\t\tvalues:\n\t\t\tvalidator.kind === 'literal'\n\t\t\t\t? [validator.value]\n\t\t\t\t: literals\n\t\t\t\t\t? children!.flatMap((c) => c.values ?? [])\n\t\t\t\t\t: undefined,\n\t\tidTables:\n\t\t\tvalidator.kind === 'id'\n\t\t\t\t? [validator.tableName]\n\t\t\t\t: (children?.flatMap((c) => c.idTables) ?? []),\n\t\toperators,\n\t\tmembers: children,\n\t}\n}\n// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Serialized Convex JSON contains only validated JSON containers and scalars.\nfunction canonical(value: JSONValue): JSONValue {\n\tif (Array.isArray(value)) return value.map(canonical)\n\t// SAFETY: arrays were handled; the remaining JSON object supports string-key lookup.\n\t// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Traverse JSON containers before canonical encoding.\n\tif (value && typeof value === 'object') {\n\t\t// SAFETY: arrays were handled, leaving a JSON object with string keys.\n\t\tconst object = value as Record<string, JSONValue>\n\t\treturn Object.fromEntries(\n\t\t\tObject.keys(object)\n\t\t\t\t.sort()\n\t\t\t\t.map((k) => [k, canonical(object[k])]),\n\t\t)\n\t}\n\treturn value\n}\n// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Bound untrusted JSON before Convex decoding or recursive schema validation.\nfunction guardJson(value: JSONValue, depth = 0, state = { nodes: 0 }): void {\n\tif (depth > 16 || ++state.nodes > 2048) throw new Error('URL budget exceeded')\n\t// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Traverse only JSON containers or owned metadata; no domain narrowing occurs.\n\tif (value && typeof value === 'object')\n\t\tfor (const child of Object.values(value)) guardJson(child, depth + 1, state)\n}\n\nfunction nativeJson(validator: GenericValidator): ValidatorJSON {\n\t// SAFETY: native scalar validators expose json at runtime, stripped from declarations.\n\treturn (validator as GenericValidator & { json: ValidatorJSON }).json\n}\nfunction cloneNative(validator: GenericValidator, optional = true): GenericValidator {\n\tlet result: GenericValidator\n\tswitch (validator.kind) {\n\t\tcase 'id':\n\t\t\tresult = v.id(validator.tableName)\n\t\t\tbreak\n\t\tcase 'string':\n\t\t\tresult = v.string()\n\t\t\tbreak\n\t\tcase 'float64':\n\t\t\tresult = v.float64()\n\t\t\tbreak\n\t\tcase 'int64':\n\t\t\tresult = v.int64()\n\t\t\tbreak\n\t\tcase 'null':\n\t\t\tresult = v.null()\n\t\t\tbreak\n\t\tcase 'boolean':\n\t\t\tresult = v.boolean()\n\t\t\tbreak\n\t\tcase 'literal':\n\t\t\tresult = v.literal(validator.value)\n\t\t\tbreak\n\t\tcase 'union':\n\t\t\tresult = v.union(...validator.members.map((member) => cloneNative(member)))\n\t\t\tbreak\n\t\tdefault:\n\t\t\tthrow new Error('Unsupported shared scalar')\n\t}\n\treturn optional && validator.isOptional === 'optional' ? v.optional(result) : result\n}\n\nfunction freezeMetadata<T>(value: T): T {\n\t// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Traverse only JSON containers or owned metadata; no domain narrowing occurs.\n\tif (value && typeof value === 'object') {\n\t\tfor (const child of Object.values(value)) freezeMetadata(child)\n\t\tObject.freeze(value)\n\t}\n\treturn value\n}\n\nfunction withinUrlSize(value: FilterTree): boolean {\n\tconst pending = [value]\n\tlet size = 0,\n\t\tnodes = 0\n\twhile (pending.length) {\n\t\tconst current = pending.pop()\n\t\tif (++nodes > 512) return false\n\t\t// oxlint-disable-next-line anti-slop/no-runtime-typeof -- Count raw scalar text before any serialization allocation.\n\t\tif (typeof current === 'string') {\n\t\t\tsize += current.length\n\t\t\tif (size > 32768) return false\n\t\t}\n\t\tif (Object(current) !== current) continue\n\t\t// SAFETY: boxing excluded null and primitives; inspect data descriptors without invoking getters.\n\t\tconst object = current as object\n\t\tconst keys = Reflect.ownKeys(object)\n\t\tif (keys.length + nodes + pending.length > 512) return false\n\t\tfor (const key of keys) {\n\t\t\tif (Array.isArray(object) && key === 'length') continue\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(object, key)\n\t\t\tif (!descriptor || !('value' in descriptor)) return false\n\t\t\tsize += String(key).length\n\t\t\tif (size > 32768) return false\n\t\t\tpending.push(descriptor.value)\n\t\t}\n\t}\n\treturn true\n}\n"],"mappings":"uPAoBA,MAAMA,kBAAgB,IAAI,QAGpBC,kBAAqB,IAAI,QAC/B,SAAgBC,EAAsB,EAAuD,CAC5F,IAAMC,EACLH,EAAc,IAAII,CAAM,IACvBA,aAAkBC,EAAE,KAAK,WAAaJ,EAAmB,IAAIG,EAAO,KAAK,IAAI,EAAE,EAAI,IAAA,IACrF,OAAOD,EAAYG,EAAYH,CAAS,EAAI,IAAA,EAC7C,CA0GA,SAAgBI,EACf,EACoB,CAEpB,IAAMC,EAAkB,CAAC,EACxBC,EAAwC,CAAC,EACpCC,EAAqE,CAAC,EACtEC,EAAkC,CAAC,EACzC,IAAK,GAAM,CAACC,EAAOT,KAAc,OAAO,QAAQU,CAAK,EAAG,CACvD,GAAI,CAAC,YAAa,cAAe,WAAW,CAAC,CAAC,SAASD,CAAK,EAC3D,MAAU,MAAM,0BAA0B,EAC3C,IAAME,EAASC,EAAaZ,CAAS,EACrC,EAAOS,GAAST,EAAU,aAAe,WAAaW,EAAO,SAAS,EAAIA,EAC1E,IAAME,EAAY,CAAC,KAAM,IAAI,EACzBC,EAAQd,CAAS,GAAGa,EAAU,KAAK,KAAM,MAAO,KAAM,KAAK,EAC3Db,EAAU,aAAe,YAAYa,EAAU,KAAK,QAAQ,EAChE,EAAWJ,GAASM,EACnBJ,EACAG,EAAQd,CAAS,EACjBA,EAAU,aAAe,UAC1B,CAAC,CAAC,SAAS,EACX,EAAOS,GAAS,CAAE,KAAMO,EAAWhB,CAAS,EAAG,SAAUA,EAAU,aAAe,UAAW,EAC7FQ,EAAY,KAAKS,EAASR,EAAOT,EAAWa,CAAS,CAAC,CACvD,CAEA,IAAMZ,EAASiB,EAAoBZ,CAAU,EA+B7C,OAAO,OAAO,OAAO,CAEpB,OAAQ,OAAO,OAAOD,CAAM,EAC5B,OAAQc,EAAeZ,CAAM,EAC7B,SACA,aAAgB,gBAAgBC,CAAW,EAE3C,UACC,EACA,EACA,IAEAY,EAAeC,EAAiBpB,EAAO,MAAMqB,CAAO,CAAe,EAAGC,EAAUC,CAAU,EAC3F,YAAa,OAAO,OAAO,CAAE,OAxCd,GAAqC,CAEpD,GAAI,CAEH,GAAI,CAACC,EAAcC,CAAmB,EAAG,MAAU,MAAM,mBAAmB,EAC5E,IAAMC,EAAS1B,EAAO,MAAMyB,CAAK,EAE3BE,EAAU,KAAK,UAAU,CAAE,EAAG,EAAG,QAASC,EAAUC,EAAaH,CAAe,CAAC,CAAE,CAAC,EAC1F,GAAIC,EAAQ,OAAS,MAAO,MAAU,MAAM,mBAAmB,EAC/D,MAAO,CAAE,QAAS,GAAe,MAAOA,CAAQ,CACjD,MAAQ,CACP,MAAO,CAAE,QAAS,GAAgB,MAAO,iBAAkB,CAC5D,CACD,EA2BsC,OA1BtB,GAAkB,CACjC,GAAI,CACH,GAAIF,EAAM,OAAS,MAAO,MAAU,MAAM,mBAAmB,EAC7D,IAAMK,EAAW,KAAK,MAAML,CAAK,EACjC,GAAI,CAACK,GAAYA,EAAS,IAAM,GAAK,OAAO,KAAKA,CAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,IAAM,YAC/E,MAAU,MAAM,yBAAyB,EAG1C,OAFAC,EAAUD,EAAS,OAAO,EAEnB,CAAE,QAAS,GAAe,MADlB9B,EAAO,MAAMgC,EAAaF,EAAS,OAAO,CACZ,CAAE,CAChD,MAAQ,CACP,MAAO,CAAE,QAAS,GAAgB,MAAO,oBAAqB,CAC/D,CACD,CAc6C,CAAC,CAC9C,CAAC,CACF,CACA,SAASnB,EAAa,EAAwC,CAC7D,IAAIsB,EACJ,OAAQlC,EAAU,KAAlB,CACC,IAAK,SACJ,EAASE,EAAE,OAAO,EAClB,MACD,IAAK,UACJ,EAASA,EAAE,OAAO,EAClB,MACD,IAAK,QACJ,EAASA,EACP,OAAO,CAAC,CACR,IAAI,EAAE,IAAM,IAAI,CAAC,CACjB,KAAK,IAAM,KAAO,EAAE,EACtB,MACD,IAAK,UACJ,EAASA,EAAE,QAAQ,EACnB,MACD,IAAK,OACJ,EAASA,EAAE,KAAK,EAChB,MACD,IAAK,UACJ,EAASA,EAAE,QAAQF,EAAU,KAAK,EAClC,MACD,IAAK,KACJ,EAASE,EAAE,OAAgB,GAAUA,EAAE,OAAO,CAAC,CAAC,UAAUwB,CAAK,CAAC,CAAC,OAAO,EACxE,MACD,IAAK,QACJ,EAASxB,EAAE,MAAMF,EAAU,QAAQ,IAAIY,CAAY,CAAC,EACpD,MACD,QACC,MAAU,MACT,mCAAmCZ,EAAU,KAAK,oDACnD,CACF,CAEA,IAAMO,EAASJ,EAAYH,EAAW,EAAK,EAI3C,OAHAH,EAAc,IAAIqC,EAAQ3B,CAAM,EAC5BP,EAAU,OAAS,MAAQkC,aAAkBhC,EAAE,KAAK,YACvDJ,EAAmB,IAAIoC,EAAO,KAAK,IAAI,GAAI3B,CAAM,EAC3C2B,CACR,CACA,SAASpB,EAAQ,EAAsC,CACtD,MACC,CAAC,SAAU,UAAW,OAAO,CAAC,CAAC,SAASd,EAAU,IAAI,GACrDA,EAAU,OAAS,WACnBE,EAAE,MAAM,CAACA,EAAE,OAAO,EAAGA,EAAE,OAAO,EAAGA,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,UAAUF,EAAU,KAAK,CAAC,CAAC,SACzEA,EAAU,OAAS,SAAWA,EAAU,QAAQ,MAAMc,CAAO,CAEhE,CACA,SAASG,EACR,EACA,EACA,EACmB,CACnB,IAAMkB,EACLnC,EAAU,OAAS,QAChBA,EAAU,QAAQ,IAAK,GAAMiB,EAASR,EAAO2B,EAAGvB,CAAS,CAAC,EAC1D,IAAA,GACEwB,EAAWF,GAAU,MAAO,GAAMG,EAAE,OAAS,MAAM,EAYzD,MAAO,CACN,QACA,KAZAtC,EAAU,OAAS,UAChB,SACAA,EAAU,OAAS,QAClB,SACAA,EAAU,OAAS,WAElBqC,EADA,OAGCrC,EAAU,KAKhB,SAAUA,EAAU,aAAe,WACnC,SAAUA,EAAU,OAAS,QAAU,CAAC,CAACmC,GAAU,KAAM,GAAMG,EAAE,QAAQ,EACzE,OACCtC,EAAU,OAAS,UAChB,CAACA,EAAU,KAAK,EAChBqC,EACCF,EAAU,QAAS,GAAMG,EAAE,QAAU,CAAC,CAAC,EACvC,IAAA,GACL,SACCtC,EAAU,OAAS,KAChB,CAACA,EAAU,SAAS,EACnBmC,GAAU,QAAS,GAAMG,EAAE,QAAQ,GAAK,CAAC,EAC9C,YACA,QAASH,CACV,CACD,CAEA,SAASN,EAAU,EAA6B,CAC/C,GAAI,MAAM,QAAQH,CAAK,EAAG,OAAOA,EAAM,IAAIG,CAAS,EAGpD,GAAIH,GAAS,OAAOA,GAAU,SAAU,CAEvC,IAAMa,EAASb,EACf,OAAO,OAAO,YACb,OAAO,KAAKa,CAAM,CAAC,CACjB,KAAK,CAAC,CACN,IAAK,GAAM,CAACC,EAAGX,EAAUU,EAAOC,EAAE,CAAC,CAAC,CACvC,CACD,CACA,OAAOd,CACR,CAEA,SAASM,EAAU,EAAkB,EAAQ,EAAG,EAAQ,CAAE,MAAO,CAAE,EAAS,CAC3E,GAAIS,EAAQ,IAAM,EAAEC,EAAM,MAAQ,KAAM,MAAU,MAAM,qBAAqB,EAE7E,GAAIhB,GAAS,OAAOA,GAAU,SAC7B,IAAK,IAAMiB,KAAS,OAAO,OAAOjB,CAAK,EAAGM,EAAUW,EAAOF,EAAQ,EAAGC,CAAK,CAC7E,CAEA,SAAS1B,EAAW,EAA4C,CAE/D,OAAQhB,EAAyD,IAClE,CACA,SAASG,EAAY,EAA6B,EAAW,GAAwB,CACpF,IAAI+B,EACJ,OAAQlC,EAAU,KAAlB,CACC,IAAK,KACJ,EAASoC,EAAE,GAAGpC,EAAU,SAAS,EACjC,MACD,IAAK,SACJ,EAASoC,EAAE,OAAO,EAClB,MACD,IAAK,UACJ,EAASA,EAAE,QAAQ,EACnB,MACD,IAAK,QACJ,EAASA,EAAE,MAAM,EACjB,MACD,IAAK,OACJ,EAASA,EAAE,KAAK,EAChB,MACD,IAAK,UACJ,EAASA,EAAE,QAAQ,EACnB,MACD,IAAK,UACJ,EAASA,EAAE,QAAQpC,EAAU,KAAK,EAClC,MACD,IAAK,QACJ,EAASoC,EAAE,MAAM,GAAGpC,EAAU,QAAQ,IAAK,GAAWG,EAAYyC,CAAM,CAAC,CAAC,EAC1E,MACD,QACC,MAAU,MAAM,2BAA2B,CAC7C,CACA,OAAOC,GAAY7C,EAAU,aAAe,WAAaoC,EAAE,SAASF,CAAM,EAAIA,CAC/E,CAEA,SAASf,EAAkB,EAAa,CAEvC,GAAIO,GAAS,OAAOA,GAAU,SAAU,CACvC,IAAK,IAAMiB,KAAS,OAAO,OAAOjB,CAAK,EAAGP,EAAewB,CAAK,EAC9D,OAAO,OAAOjB,CAAK,CACpB,CACA,OAAOA,CACR,CAEA,SAASD,EAAc,EAA4B,CAClD,IAAMqB,EAAU,CAACpB,CAAK,EAClBqB,EAAO,EACVC,EAAQ,EACT,KAAOF,EAAQ,QAAQ,CACtB,IAAMG,EAAUH,EAAQ,IAAI,EAG5B,GAFI,EAAEE,EAAQ,KAEV,OAAOC,GAAY,WACtB,GAAQA,EAAQ,OACZF,EAAO,OAAO,MAAO,GAE1B,GAAI,OAAOE,CAAO,IAAMA,EAAS,SAEjC,IAAMV,EAASU,EACTC,EAAO,QAAQ,QAAQX,CAAM,EACnC,GAAIW,EAAK,OAASF,EAAQF,EAAQ,OAAS,IAAK,MAAO,GACvD,IAAK,IAAMK,KAAOD,EAAM,CACvB,GAAI,MAAM,QAAQX,CAAM,GAAKY,IAAQ,SAAU,SAC/C,IAAMC,EAAa,OAAO,yBAAyBb,EAAQY,CAAG,EAG9D,GAFI,CAACC,GAAc,EAAE,UAAWA,KAChC,GAAQ,OAAOD,CAAG,CAAC,CAAC,OAChBJ,EAAO,OAAO,MAAO,GACzBD,EAAQ,KAAKM,EAAW,KAAK,CAC9B,CACD,CACA,MAAO,EACR"}
@@ -0,0 +1,7 @@
1
+ import { FilterAst } from "./ast.mjs";
2
+ import { Value } from "convex/values";
3
+ //#region src/querying/shared/evaluate.d.ts
4
+ /** Native scalar ordering/equality, including missing vs null, without coercion. */
5
+ export declare function evaluateFilter(ast: FilterAst, document: Record<string, Value | undefined>, accessible?: ReadonlySet<string>): boolean | 'unknown';
6
+ //#endregion
7
+ //# sourceMappingURL=evaluate.d.mts.map
@@ -0,0 +1,2 @@
1
+ import{compareValues as e}from"convex/values";function t(n,r,i){switch(n.kind){case`and`:{let e=!1;for(let a of n.children){let n=t(a,r,i);if(n===!1)return!1;n===`unknown`&&(e=!0)}return!e||`unknown`}case`or`:{let e=!1;for(let a of n.children){let n=t(a,r,i);if(n===!0)return!0;n===`unknown`&&(e=!0)}return e?`unknown`:!1}case`variant`:return t({kind:`and`,children:[{kind:`scalar`,field:n.discriminator,operator:`eq`,value:n.tag},n.child]},r,i);case`not`:{let e=t(n.child,r,i);return e===`unknown`?e:!e}case`relation`:if(!i)throw Error(`Relation execution requires U6`);return`unknown`;case`scalar`:{if(i&&!i.has(n.field))return`unknown`;let t=r[n.field];if(n.operator===`exists`)return t!==void 0===n.value;if(n.operator===`in`)return Array.isArray(n.value)&&n.value.some(n=>e(t,n)===0);if(Array.isArray(n.value))throw Error(`Invalid scalar predicate`);let a=e(t,n.value);switch(n.operator){case`eq`:return a===0;case`gt`:return a>0;case`gte`:return a>=0;case`lt`:return a<0;case`lte`:return a<=0}}}}export{t as evaluateFilter};
2
+ //# sourceMappingURL=evaluate.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.mjs","names":["evaluateFilter","ast","unknown","child","value","document","accessible","actual","compareValues","compared"],"sources":["../../../src/querying/shared/evaluate.ts"],"sourcesContent":["import { compareValues, type Value } from 'convex/values'\nimport type { FilterAst } from './ast'\n/** Native scalar ordering/equality, including missing vs null, without coercion. */\nexport function evaluateFilter(\n\tast: FilterAst,\n\tdocument: Record<string, Value | undefined>,\n\taccessible?: ReadonlySet<string>,\n): boolean | 'unknown' {\n\tswitch (ast.kind) {\n\t\tcase 'and': {\n\t\t\tlet unknown = false\n\t\t\tfor (const child of ast.children) {\n\t\t\t\tconst value = evaluateFilter(child, document, accessible)\n\t\t\t\tif (value === false) return false\n\t\t\t\tif (value === 'unknown') unknown = true\n\t\t\t}\n\t\t\treturn unknown ? 'unknown' : true\n\t\t}\n\t\tcase 'or': {\n\t\t\tlet unknown = false\n\t\t\tfor (const child of ast.children) {\n\t\t\t\tconst value = evaluateFilter(child, document, accessible)\n\t\t\t\tif (value === true) return true\n\t\t\t\tif (value === 'unknown') unknown = true\n\t\t\t}\n\t\t\treturn unknown ? 'unknown' : false\n\t\t}\n\t\tcase 'variant':\n\t\t\treturn evaluateFilter(\n\t\t\t\t{\n\t\t\t\t\tkind: 'and',\n\t\t\t\t\tchildren: [\n\t\t\t\t\t\t{ kind: 'scalar', field: ast.discriminator, operator: 'eq', value: ast.tag },\n\t\t\t\t\t\tast.child,\n\t\t\t\t\t],\n\t\t\t\t},\n\t\t\t\tdocument,\n\t\t\t\taccessible,\n\t\t\t)\n\t\tcase 'not': {\n\t\t\tconst value = evaluateFilter(ast.child, document, accessible)\n\t\t\treturn value === 'unknown' ? value : !value\n\t\t}\n\t\tcase 'relation':\n\t\t\tif (!accessible) throw new Error('Relation execution requires U6')\n\t\t\treturn 'unknown'\n\t\tcase 'scalar': {\n\t\t\tif (accessible && !accessible.has(ast.field)) return 'unknown'\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":"8CAGA,SAAgBA,EACf,EACA,EACA,EACsB,CACtB,OAAQC,EAAI,KAAZ,CACC,IAAK,MAAO,CACX,IAAIC,EAAU,GACd,IAAK,IAAMC,KAASF,EAAI,SAAU,CACjC,IAAMG,EAAQJ,EAAeG,EAAOE,EAAUC,CAAU,EACxD,GAAIF,IAAU,GAAO,MAAO,GACxBA,IAAU,YAAW,EAAU,GACpC,CACA,MAAO,IAAU,SAClB,CACA,IAAK,KAAM,CACV,IAAIF,EAAU,GACd,IAAK,IAAMC,KAASF,EAAI,SAAU,CACjC,IAAMG,EAAQJ,EAAeG,EAAOE,EAAUC,CAAU,EACxD,GAAIF,IAAU,GAAM,MAAO,GACvBA,IAAU,YAAW,EAAU,GACpC,CACA,OAAOF,EAAU,UAAY,EAC9B,CACA,IAAK,UACJ,OAAOF,EACN,CACC,KAAM,MACN,SAAU,CACT,CAAE,KAAM,SAAU,MAAOC,EAAI,cAAe,SAAU,KAAM,MAAOA,EAAI,GAAI,EAC3EA,EAAI,KACL,CACD,EACAI,EACAC,CACD,EACD,IAAK,MAAO,CACX,IAAMF,EAAQJ,EAAeC,EAAI,MAAOI,EAAUC,CAAU,EAC5D,OAAOF,IAAU,UAAYA,EAAQ,CAACA,CACvC,CACA,IAAK,WACJ,GAAI,CAACE,EAAY,MAAU,MAAM,gCAAgC,EACjE,MAAO,UACR,IAAK,SAAU,CACd,GAAIA,GAAc,CAACA,EAAW,IAAIL,EAAI,KAAK,EAAG,MAAO,UACrD,IAAMM,EAASF,EAASJ,EAAI,OAC5B,GAAIA,EAAI,WAAa,SAAU,OAAQM,IAAW,IAAA,KAAeN,EAAI,MACrE,GAAIA,EAAI,WAAa,KACpB,OACC,MAAM,QAAQA,EAAI,KAAK,GAAKA,EAAI,MAAM,KAAM,GAAUO,EAAcD,EAAQH,CAAK,IAAM,CAAC,EAE1F,GAAI,MAAM,QAAQH,EAAI,KAAK,EAAG,MAAU,MAAM,0BAA0B,EACxE,IAAMQ,EAAWD,EAAcD,EAAQN,EAAI,KAAK,EAChD,OAAQA,EAAI,SAAZ,CACC,IAAK,KACJ,OAAOQ,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"}
@@ -0,0 +1,31 @@
1
+ import { FilterAst } from "./ast.mjs";
2
+ import { z } from "zod";
3
+ //#region src/querying/shared/filters.d.ts
4
+ export declare const MAX_FILTER_DEPTH = 4;
5
+ export declare const MAX_FILTER_NODES = 256;
6
+ export declare const MAX_FILTER_IN_VALUES = 100;
7
+ export type FieldSchemas = Record<string, z.ZodType>;
8
+ export type FilterTree = string | number | bigint | boolean | null | undefined | FilterTree[] | {
9
+ [key: string]: FilterTree;
10
+ };
11
+ /** Each boolean child and relation target expression consumes one of four edges.
12
+ * some/none wrappers and fields namespaces consume nodes, but no extra depth.
13
+ */
14
+ export interface FilterGrammar {
15
+ fields: FieldSchemas;
16
+ variants?: {
17
+ discriminator: string;
18
+ options: Record<string, FilterGrammar>;
19
+ };
20
+ relations: Record<string, {
21
+ cardinality: 'one' | 'many';
22
+ target: FilterGrammar;
23
+ }>;
24
+ }
25
+ export declare function relationFilterSchema(grammar: FilterGrammar, cache?: WeakMap<FilterGrammar, Map<number, z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>>): z.ZodType;
26
+ export declare function paginationGrammar(schema: z.ZodType): FilterGrammar | undefined;
27
+ export declare function normalizeFilters(value: FilterTree, grammar?: FilterGrammar): FilterAst;
28
+ export declare function scalarPredicateFromSchema(scalar: z.ZodType, range: boolean, optional: boolean): z.ZodType;
29
+ export declare function boundedFilterSchema(predicates: FieldSchemas): z.ZodType;
30
+ //#endregion
31
+ //# sourceMappingURL=filters.d.mts.map
@@ -0,0 +1,2 @@
1
+ import{z as e}from"zod";const t=4,n=256,r=100,i=/* @__PURE__ */ new WeakMap,a=/* @__PURE__ */ new WeakMap,o=/* @__PURE__ */ new WeakMap,s=e.core.$constructor(`BoundedFilterObject`,(t,n)=>{e.ZodObject.init(t,n);let r=t._zod.parse,o=n.shape.fields,s=o instanceof e.ZodOptional?o.unwrap():void 0,c=s instanceof e.ZodObject?Object.keys(s.shape):Object.keys(n.shape),l={variants:a.get(n.shape.variant),fields:Object.fromEntries(c.map(e=>[e,n.shape[e]])),relations:Object.fromEntries(Object.entries(n.shape).flatMap(([e,t])=>{let n=i.get(t);return n?[[e,n]]:[]}))};t._zod.parse=(e,t)=>m(e.value,l)?r(e,t):(e.issues.push({code:`custom`,input:e.value,message:`Unsupported filter layout or depth/node budget exceeded`}),e)});function c(e,t=/* @__PURE__ */ new WeakMap){let n=new s(p(e,0,t)._zod.def);return o.set(n,e),n}function l(t){if(!(t instanceof e.ZodObject))return;let n=t.shape.filters;if(!(n instanceof e.ZodOptional))return;let r=n.unwrap(),s=o.get(r);if(s)return s;if(r instanceof e.ZodObject)return{fields:r.shape,variants:a.get(r.shape.variant),relations:Object.fromEntries(Object.entries(r.shape).flatMap(([e,t])=>{let n=i.get(t);return n?[[e,n]]:[]}))}}function u(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=>u(e,t))});else if(e===`variant`&&t?.variants){let e=i;n.push({kind:`variant`,discriminator:t.variants.discriminator,tag:e.tag,child:u(e.filters,t.variants.options[e.tag])})}else if(e===`not`)n.push({kind:`not`,child:u(i,t)});else if(e===`fields`)for(let[e,t]of Object.entries(i))n.push(...h(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:u(i,r.target)});else{let t=i;n.push({kind:`relation`,name:e,mode:t.some===void 0?`none`:`some`,predicate:u(t.some??t.none,r.target)})}}else n.push(...h(e,i))}return{kind:`and`,children:n}}function d(t,n,r){let i=e.object({eq:t.optional(),in:e.array(t).max(100).optional()}),a=n?i.extend({gt:t.optional(),gte:t.optional(),lt:t.optional(),lte:t.optional()}):i,o=r?a.extend({exists:e.boolean().optional()}):a;return e.union([t,o.strict().refine(e=>Object.values(e).some(e=>e!==void 0),`At least one operator is required`)])}function f(e){return c({fields:e,relations:{}})}function p(t,n,r=/* @__PURE__ */ new WeakMap){let o=r.get(t)?.get(n);if(o)return o;let s=t.fields,c=Object.fromEntries(Object.entries(s).filter(([e])=>![`and`,`or`,`not`,`fields`,...t.variants?[`variant`]:[]].includes(e))),l={};for(let[a,o]of Object.entries(t.relations)){if(n===4)continue;let t=p(o.target,n+1,r);l[a]=(o.cardinality===`one`?t.nullable():e.union([e.object({some:t}).strict(),e.object({none:t}).strict()])).optional(),i.set(l[a],o)}if(t.variants&&n<4){let i=Object.entries(t.variants.options).map(([t,i])=>e.strictObject({tag:e.literal(t),filters:p(i,n+1,r)}));i.length&&(l.variant=e.union(i).optional(),a.set(l.variant,t.variants))}let u=e.object({...c,...l,fields:e.object(s).strict().optional()}).strict(),d=n===4?void 0:p(t,n+1,r),f=d?u.extend({and:e.array(d).min(1).max(256).optional(),or:e.array(d).min(1).max(256).optional(),not:d.optional()}):u,m=r.get(t);return m||(m=/* @__PURE__ */ new Map,r.set(t,m)),m.set(n,f),f}function m(t,n){let r=[{value:t,depth:0,context:`expression`,grammar:n}],i=0;for(;r.length;){let t=r.pop();if(++i>256||t.depth>4)return!1;if(Object(t.value)!==t.value)continue;let n=t.value,a=Array.isArray(n)?n:void 0,o=Object.getPrototypeOf(n);if((a?o!==Array.prototype:o!==Object.prototype&&o!==null)||a&&a.length+i+r.length>256)return!1;let s=Reflect.ownKeys(n);if(s.length-+!!a+i+r.length>256||a&&s.length!==a.length+1)return!1;let c;switch(t.context){case`expression`:c=[...Object.keys(t.grammar.fields),...Object.keys(t.grammar.relations),`and`,`or`,`not`,`fields`,...t.grammar.variants?[`variant`]:[]];break;case`variant`:c=[`tag`,`filters`];break;case`fields`:c=Object.keys(t.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(n,e)&&e in n)return!1;for(let i of s){if(a&&i===`length`)continue;let o=Object.getOwnPropertyDescriptor(n,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=t.depth,u=t.grammar;if(t.context===`expression`){if(c=`predicate`,s===`variant`&&t.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(t.grammar.relations[s]){let e=t.grammar.relations[s];c=e.cardinality===`one`?`expression`:`many`,u=e.target,l++}}else if(t.context===`variant`&&s===`filters`){let r=Object.getOwnPropertyDescriptor(n,`tag`);if(!r||!(`value`in r)||!e.string().safeParse(r.value).success||!Object.hasOwn(t.grammar.variants?.options??{},r.value))return!1;c=`expression`,u=t.grammar.variants.options[String(r.value)]}else t.context===`fields`?c=`predicate`:(t.context===`composition`||t.context===`many`)&&(c=`expression`);r.push({value:o.value,depth:l,context:c,grammar:u})}}return!0}function h(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}]}export{t as MAX_FILTER_DEPTH,r as MAX_FILTER_IN_VALUES,n as MAX_FILTER_NODES,f as boundedFilterSchema,u as normalizeFilters,l as paginationGrammar,c as relationFilterSchema,d as scalarPredicateFromSchema};
2
+ //# sourceMappingURL=filters.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filters.mjs","names":["MAX_FILTER_DEPTH","MAX_FILTER_NODES","MAX_FILTER_IN_VALUES","relationPredicates","variantPredicates","filterGrammars","BoundedFilterObject","z","inst","def","parse","namespace","namespaceObject","selectedKeys","grammar","key","schema","relation","withinFilterBudget","payload","ctx","relationFilterSchema","filterNodeSchema","cache","paginationGrammar","filters","root","cached","name","predicate","normalizeFilters","children","value","expression","child","scoped","field","scalar","normalizeScalar","target","scalarPredicateFromSchema","equality","ranged","range","existence","optional","entry","boundedFilterSchema","predicates","depth","direct","relations","options","tag","branch","node","result","depths","pending","nodes","object","array","prototype","keys","consumed","descriptor","context","operator"],"sources":["../../../src/querying/shared/filters.ts"],"sourcesContent":["import { z } from 'zod'\nimport type { FilterAst, FilterScalar, FilterOperator } from './ast'\nexport const MAX_FILTER_DEPTH = 4\nexport const MAX_FILTER_NODES = 256\nexport const MAX_FILTER_IN_VALUES = 100\nexport type FieldSchemas = Record<string, z.ZodType>\nexport type 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 function 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}\nexport function 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}\nexport function scalarPredicateFromSchema(\n\tscalar: z.ZodType,\n\trange: boolean,\n\toptional: boolean,\n): z.ZodType {\n\tconst equality = z.object({\n\t\teq: scalar.optional(),\n\t\tin: z.array(scalar).max(MAX_FILTER_IN_VALUES).optional(),\n\t})\n\tconst ranged = range\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 = optional ? ranged.extend({ exists: z.boolean().optional() }) : ranged\n\treturn z.union([\n\t\tscalar,\n\t\texistence\n\t\t\t.strict()\n\t\t\t.refine(\n\t\t\t\t(value) => Object.values(value).some((entry) => entry !== undefined),\n\t\t\t\t'At least one operator is required',\n\t\t\t),\n\t])\n}\n\nexport function 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}\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}\n"],"mappings":"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"}