cvx-kit 0.1.15 → 0.1.17

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 (58) hide show
  1. package/README.md +67 -40
  2. package/dist/querying/filters.d.mts +10 -1
  3. package/dist/querying/filters.mjs +1 -1
  4. package/dist/querying/filters.mjs.map +1 -1
  5. package/dist/querying/planner.d.mts +1 -0
  6. package/dist/querying/planner.mjs +1 -1
  7. package/dist/querying/planner.mjs.map +1 -1
  8. package/dist/querying/relationships.d.mts +1 -1
  9. package/dist/querying/relationships.mjs +1 -1
  10. package/dist/querying/relationships.mjs.map +1 -1
  11. package/dist/querying.d.mts +9 -8
  12. package/dist/querying.mjs.map +1 -1
  13. package/dist/relations-public.d.mts +2 -2
  14. package/dist/relations.d.mts +74 -25
  15. package/dist/relations.mjs +1 -1
  16. package/dist/relations.mjs.map +1 -1
  17. package/dist/zod-table.d.mts +59 -2
  18. package/dist/zod-table.mjs +1 -1
  19. package/dist/zod-table.mjs.map +1 -1
  20. package/package.json +12 -3
  21. package/AGENTS.md +0 -47
  22. package/CHANGELOG.md +0 -337
  23. package/ROADMAP.md +0 -126
  24. package/docs/agent-tools.md +0 -41
  25. package/docs/approvals.md +0 -201
  26. package/docs/architecture.md +0 -124
  27. package/docs/auth.md +0 -443
  28. package/docs/command-context.md +0 -150
  29. package/docs/commands-legacy.md +0 -513
  30. package/docs/commands.md +0 -173
  31. package/docs/conventions.md +0 -345
  32. package/docs/crud.md +0 -66
  33. package/docs/domain-contracts.md +0 -148
  34. package/docs/domain-operations.md +0 -615
  35. package/docs/effect.md +0 -403
  36. package/docs/llms.md +0 -324
  37. package/docs/maintainability.md +0 -124
  38. package/docs/migration.md +0 -351
  39. package/docs/oxlint.md +0 -260
  40. package/docs/querying.md +0 -329
  41. package/docs/tenancy.md +0 -176
  42. package/docs/triggers.md +0 -77
  43. package/docs/upgrading.md +0 -287
  44. package/docs/webhooks.md +0 -58
  45. package/docs/zod-table.md +0 -278
  46. package/scripts/convex-config-bridge.mjs +0 -44
  47. package/scripts/convex-smoke-env.mjs +0 -33
  48. package/scripts/extract-changelog.mjs +0 -29
  49. package/scripts/owned-process.mjs +0 -140
  50. package/scripts/owned-process.test.mjs +0 -107
  51. package/scripts/smoke-packed-components.mjs +0 -341
  52. package/scripts/smoke-packed-effect.mjs +0 -439
  53. package/scripts/smoke-packed-querying.mjs +0 -298
  54. package/scripts/test-packed-auth.mjs +0 -72
  55. package/scripts/test-packed-effect.mjs +0 -482
  56. package/scripts/test-packed-ids.mjs +0 -150
  57. package/scripts/test-packed-oxlint.mjs +0 -167
  58. package/scripts/test-packed-querying.mjs +0 -185
package/README.md CHANGED
@@ -28,26 +28,47 @@ Supported versions: Convex `^1.46.0`, Zod `^4.5.4`, Bun `1.3+`, and Node.js
28
28
 
29
29
  ## Documentation
30
30
 
31
- Full docs ship with the package — `node_modules/cvx-kit/docs/` when installed,
32
- authored in [`src/docs/`](./src/docs/):
33
-
34
- - [`architecture.md`](./src/docs/architecture.md) — the layered application shape the kit encodes
35
- - [`conventions.md`](./src/docs/conventions.md) — the doctrine: folder structure, file anatomy, naming grammar, import rules
36
- - [`zod-table.md`](./src/docs/zod-table.md) — one zod shape per entity, masked into every boundary
37
- - [`auth.md`](./src/docs/auth.md) — auth-aware function constructors from injected policy
38
- - [`commands.md`](./src/docs/commands.md) — Effect command declarations, lifecycle, and migration
39
- - [`effect.md`](./src/docs/effect.md) — opt-in Effect v4 registries, injected services, and shared API builders
40
- - [`domain-operations.md`](./src/docs/domain-operations.md) — server domain/API examples, contracts, errors, receipts, workflow steps, and selected tools
41
- - [`triggers.md`](./src/docs/triggers.md) — trigger registry, timestamps/append-only/no-delete
42
- - [`approvals.md`](./src/docs/approvals.md) — declarative approval workflows
43
- - [`tenancy.md`](./src/docs/tenancy.md) — row-level security (roles) with opt-in multi-tenant isolation
44
- - [`crud.md`](./src/docs/crud.md) — the CRUD command factory
45
- - [`webhooks.md`](./src/docs/webhooks.md) — the sanctioned webhook boundary
46
- - [`agent-tools.md`](./src/docs/agent-tools.md) — agent tools that are audited commands
47
- - [`upgrading.md`](./src/docs/upgrading.md) — 0.0.x → 0.1.0, old way → new way
48
- - [`maintainability.md`](./src/docs/maintainability.md) — keeping a Convex app maintainable long-term
49
- - [`migration.md`](./src/docs/migration.md) — restructuring an existing raw Convex project into this architecture
50
- - [`llms.md`](./src/docs/llms.md) — condensed reference to paste into an LLM/agent context
31
+ Full docs live on [GitHub](https://github.com/ImRLopezAG/cvx-kit/tree/main/skills/cvx-kit/references)
32
+ and are bundled with the repository's [agent skill](https://github.com/ImRLopezAG/cvx-kit/tree/main/skills/cvx-kit).
33
+ The npm package includes this README; full docs and skills are installed separately.
34
+
35
+ If you use a coding agent, install the cvx-kit skill from your application directory:
36
+
37
+ ```sh
38
+ npx skills add ImRLopezAG/cvx-kit --skill cvx-kit
39
+ ```
40
+
41
+ The [skills CLI](https://www.skills.sh/docs) lets you choose your agent. Installation
42
+ is project-scoped by default; add `--global` only for a user-wide install. Public
43
+ GitHub installation needs no login. Preview available skills with
44
+ `npx skills add ImRLopezAG/cvx-kit --list`.
45
+ The skill reads the guides needed for each task instead of loading all docs.
46
+ Check your installed package version against the skill's source revision before
47
+ using newer APIs. For a release containing the skill, use its GitHub tag URL
48
+ (`https://github.com/ImRLopezAG/cvx-kit/tree/<tag>/skills/cvx-kit`) as the install source.
49
+
50
+ Maintainers edit `skills/cvx-kit/references/` directly. CI verifies that relative
51
+ links remain inside the installed skill with `bun run skills:check`.
52
+
53
+ Guides:
54
+
55
+ - [`architecture.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/architecture.md) — the layered application shape the kit encodes
56
+ - [`conventions.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/conventions.md) — the doctrine: folder structure, file anatomy, naming grammar, import rules
57
+ - [`zod-table.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/zod-table.md) — one zod shape per entity, masked into every boundary
58
+ - [`auth.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/auth.md) — auth-aware function constructors from injected policy
59
+ - [`commands.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/commands.md) — Effect command declarations, lifecycle, and migration
60
+ - [`effect.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/effect.md) — opt-in Effect v4 registries, injected services, and shared API builders
61
+ - [`domain-operations.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/domain-operations.md) — server domain/API examples, contracts, errors, receipts, workflow steps, and selected tools
62
+ - [`triggers.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/triggers.md) — trigger registry, timestamps/append-only/no-delete
63
+ - [`approvals.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/approvals.md) — declarative approval workflows
64
+ - [`tenancy.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/tenancy.md) — row-level security (roles) with opt-in multi-tenant isolation
65
+ - [`crud.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/crud.md) — the CRUD command factory
66
+ - [`webhooks.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/webhooks.md) — the sanctioned webhook boundary
67
+ - [`agent-tools.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/agent-tools.md) — agent tools that are audited commands
68
+ - [`upgrading.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/upgrading.md) — 0.0.x → 0.1.0, old way → new way
69
+ - [`maintainability.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/maintainability.md) — keeping a Convex app maintainable long-term
70
+ - [`migration.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/migration.md) — restructuring an existing raw Convex project into this architecture
71
+ - [`llms.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/llms.md) — optional condensed reference for coding agents
51
72
 
52
73
  ## Modules
53
74
 
@@ -85,9 +106,9 @@ LLM-tool JSON schemas primitive while preserving the `Id<...>` type.
85
106
 
86
107
  ### `cvx-kit/relations` and `cvx-kit/querying`
87
108
 
88
- See the [querying guide](./src/docs/querying.md),
89
- [DTO parameters](./src/docs/zod-table.md#public-dto-parameters), and
90
- [consumer rule reference](./src/docs/oxlint.md#dto-and-pagination-diagnostics).
109
+ See the [querying guide](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/querying.md),
110
+ [DTO parameters](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/zod-table.md#public-dto-parameters), and
111
+ [consumer rule reference](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/oxlint.md#dto-and-pagination-diagnostics).
91
112
 
92
113
  Declare relationships once in `convex/relations.ts`, then configure the engine
93
114
  once in `convex/querying.ts`. Both factories are also available from the package
@@ -111,13 +132,19 @@ export const relations = defineRelations({ tasks, labels, edges }, (r) => ({
111
132
  }))
112
133
  ```
113
134
 
114
- The tables must be object-shaped `zodTable` or `tenantTable` boundaries, with
135
+ The tables can be `zodTable`, `tenantTable`, or three-argument `zodVariantTable` boundaries, with
115
136
  indexes supporting each declared lookup. Names can distinguish multiple edges
116
137
  to the same table; references can point to the same table. A one edge returns a
117
138
  public DTO or `null`. A many edge returns a bounded native page. Junction output
118
139
  preserves duplicate edge occurrences, even when target reads are memoized.
119
140
  Missing and unreadable targets have the same public result.
120
141
 
142
+ Variant tables keep their discriminated public unions. Use the relation callback's
143
+ second argument, `branch('payments', 'creditor_payment').creditorId`, for a
144
+ branch-specific reference; its tag is checked before lookup. See the
145
+ [variant querying guide](skills/cvx-kit/references/variant-querying.md) for the
146
+ complete authenticated flow and branch filter syntax.
147
+
121
148
  ```ts
122
149
  // convex/querying.ts
123
150
  import { createQueryEngine } from 'cvx-kit/querying'
@@ -131,7 +158,7 @@ One engine serves every registered table. Cursor keys load internally on first
131
158
  pagination from server environment variables `CVX_QUERY_CURSOR_KEYS_JSON`
132
159
  (key IDs mapped to 128-character hex secrets) and `CVX_QUERY_CURSOR_CURRENT_KEY_ID`.
133
160
  Keep key material out of arguments, client code, and logs. Optional overrides
134
- belong in a separate second argument; see [querying](src/docs/querying.md). The default codec
161
+ belong in a separate second argument; see [querying](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/querying.md). The default codec
135
162
  uses authenticated AES-SIV encryption without a plaintext fallback; identical
136
163
  payloads under the same binding reveal equality. Tokens bind filters, selected
137
164
  output, index, order, relation ancestry, principal/tenant/security scope, and
@@ -188,7 +215,7 @@ many reads. Application/helper accounting does not measure physical platform I/O
188
215
  Kit auth query contexts carry the engine's private read capability. Unknown
189
216
  contexts fail closed unless a centrally configured trusted adapter attests their
190
217
  query reader, effective policy, and security scope. Mutation/action contexts,
191
- variant tables, search/vector optimization, arbitrary predicates/projections,
218
+ search/vector optimization, arbitrary predicates/projections,
192
219
  and custom sorts are outside this API.
193
220
 
194
221
  For manual projection, `tasks.toPublicDto(row)` still omits IDs and unselected
@@ -324,12 +351,12 @@ export const executeRename = (input: z.input<typeof renameInput>, host: Host) =>
324
351
  services and execute it; domain code can compose `Effect.gen` and `Effect.fn`
325
352
  without starting another runner. Commands validate inputs and results, enforce
326
353
  permissions and guards, and write audit entries in the mutation transaction.
327
- See [commands](./src/docs/commands.md), [complete Effect wiring](./src/docs/effect.md),
328
- and [authenticated builders](./src/docs/auth.md).
354
+ See [commands](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/commands.md), [complete Effect wiring](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/effect.md),
355
+ and [authenticated builders](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/auth.md).
329
356
 
330
357
  The old `Foundation.Command` Promise facade is **deprecated** and retained for
331
358
  existing consumers. Its separate executor handler options are covered only in
332
- the [compatibility reference](./src/docs/commands-legacy.md). The Foundation
359
+ the [compatibility reference](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/commands-legacy.md). The Foundation
333
360
  component and its transaction result helpers remain available.
334
361
 
335
362
  ### `cvx-kit/components/approvals` (component)
@@ -349,7 +376,7 @@ app.use(approvals)
349
376
  ## Application lint rules
350
377
 
351
378
  The TypeScript plugin `cvx-kit/oxlint` checks applications using the kit against
352
- [`conventions.md`](./src/docs/conventions.md). It targets `convex/`, including
379
+ [`conventions.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/conventions.md). It targets `convex/`, including
353
380
  public adapters, domain modules, root facades, and locally authored components.
354
381
  Load it in your application's `.oxlintrc.json` and select rules:
355
382
 
@@ -390,11 +417,11 @@ module cycles. Run Oxlint on the whole backend, including `convex.config.ts`;
390
417
  changed-file-only lint cannot guarantee a project scan. No application-owned
391
418
  architecture test framework is required. The plugin also exports `checkArchitecture()`
392
419
  for tools that need project diagnostics directly, named `rules`, and `RuleName`. Verified with
393
- Oxlint 1.87.0 and Vite+ 1.1.0. See [rule coverage and limitations](./src/docs/oxlint.md)
394
- and the [migration from the initial 0.1.4 plugin](./src/docs/upgrading.md).
420
+ Oxlint 1.87.0 and Vite+ 1.1.0. See [rule coverage and limitations](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/oxlint.md)
421
+ and the [migration from the initial 0.1.4 plugin](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/upgrading.md).
395
422
 
396
423
  Repository checks and the enforced library rules are documented in
397
- [`tools/oxlint/README.md`](./tools/oxlint/README.md). Run `bun run lint` for
424
+ [`tools/oxlint/README.md`](https://github.com/ImRLopezAG/cvx-kit/blob/main/tools/oxlint/README.md). Run `bun run lint` for
398
425
  anti-slop and architecture checks, `bun run test:lint` to test the custom
399
426
  rules, and `bun run check` for the full Vite+ lint and formatting check.
400
427
 
@@ -425,7 +452,7 @@ registerApprovals(t)
425
452
  ## Releasing
426
453
 
427
454
  Every release also creates a GitHub Release whose notes are the matching
428
- [CHANGELOG](./CHANGELOG.md) section (extracted by
455
+ [CHANGELOG](https://github.com/ImRLopezAG/cvx-kit/blob/main/CHANGELOG.md) section (extracted by
429
456
  `scripts/extract-changelog.mjs` in the release workflow).
430
457
 
431
458
  ### OIDC setup
@@ -434,10 +461,10 @@ Publishing uses [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishe
434
461
  (OIDC) — no tokens stored anywhere. The release workflow authenticates via
435
462
  GitHub's OIDC and npm generates provenance attestations automatically.
436
463
 
437
- - `bun run release` — patch version bump, tag, and push; the `v*` tag
438
- triggers `.github/workflows/release.yml`, which verifies (typecheck, test,
439
- build + publint) and publishes.
440
- - `bun run release:minor` — same, minor bump.
464
+ Merge the version bump and changelog into `main`, then wait for successful CI
465
+ on that exact commit before pushing its `v<version>` tag. The tag triggers
466
+ `.github/workflows/release.yml`, which checks the main CI result, builds and
467
+ validates the package and skill documentation, and publishes to npm.
441
468
 
442
469
  One-time setup (already-published package required first):
443
470
 
@@ -449,10 +476,10 @@ One-time setup (already-published package required first):
449
476
 
450
477
  ## Roadmap
451
478
 
452
- See [ROADMAP.md](./ROADMAP.md) — milestones from 0.1.0 through 1.0.0.
479
+ See [ROADMAP.md](https://github.com/ImRLopezAG/cvx-kit/blob/main/ROADMAP.md) — milestones from 0.1.0 through 1.0.0.
453
480
 
454
481
  Optional request context can be added through the existing Effect API adapter's
455
482
  `context` callback. Bind selected registries with `commands.withContext(value)`
456
483
  and consume `ctx.commands.exec(name, input)` through the application's existing
457
484
  function helper names. Keep dependencies local to each feature; explicit execution
458
- and input-only command handlers remain supported. See [optional context injection](./src/docs/command-context.md).
485
+ and input-only command handlers remain supported. See [optional context injection](https://github.com/ImRLopezAG/cvx-kit/blob/main/skills/cvx-kit/references/command-context.md).
@@ -37,7 +37,7 @@ export type ScalarPredicate<Schema extends z.ZodType, Side extends 'input' | 'ou
37
37
  gte?: OrderedValue<Schema>;
38
38
  lt?: OrderedValue<Schema>;
39
39
  lte?: OrderedValue<Schema>;
40
- }) & (NativeField<Schema>['isOptional'] extends 'optional' ? {
40
+ }) & ('optional' extends NativeField<Schema>['isOptional'] ? {
41
41
  exists?: boolean;
42
42
  } : {}));
43
43
  type SelectedFieldFilters<Fields extends FieldSchemas, Selection, Side extends 'input' | 'output'> = { [Key in Extract<keyof Selection, keyof Fields>]?: ScalarPredicate<Fields[Key], Side>; };
@@ -81,6 +81,10 @@ export declare function scalarPredicateSchema(validator: GenericValidator): z.Zo
81
81
  */
82
82
  export interface FilterGrammar {
83
83
  fields: FieldSchemas;
84
+ variants?: {
85
+ discriminator: string;
86
+ options: Record<string, FilterGrammar>;
87
+ };
84
88
  relations: Record<string, {
85
89
  cardinality: 'one' | 'many';
86
90
  target: FilterGrammar;
@@ -95,6 +99,11 @@ export type FilterAst = {
95
99
  } | {
96
100
  kind: 'not';
97
101
  child: FilterAst;
102
+ } | {
103
+ kind: 'variant';
104
+ discriminator: string;
105
+ tag: string;
106
+ child: FilterAst;
98
107
  } | {
99
108
  kind: 'scalar';
100
109
  field: string;
@@ -1,2 +1,2 @@
1
- import{convexToZod as e}from"convex-helpers/server/zod4";import{paginationOptsValidator as t}from"convex/server";import{z as n}from"zod";import{compareValues as r}from"convex/values";const i=100,a=4,o=256,s=100,c=e(t),l=c.strict().extend({numItems:c.shape.numItems.int().positive().max(100),maximumRowsRead:c.shape.maximumRowsRead.unwrap().int().positive().optional(),maximumBytesRead:c.shape.maximumBytesRead.unwrap().int().positive().optional()});function u(e,t){let r={};for(let[n,i]of Object.entries(t)){if(i!==!0||!Object.hasOwn(e,n))throw Error(`Invalid scalar filter selection "${n}"`);r[n]=d(e[n]).optional()}let i=T(r);return l.extend({filters:i.optional(),order:n.enum([`asc`,`desc`]).optional()})}function d(t){C(t);let r=e(t).nonoptional(),i=n.object({eq:r.optional(),in:n.array(r).min(1).max(100).optional()}),a=w(t)?i.extend({gt:r.optional(),gte:r.optional(),lt:r.optional(),lte:r.optional()}):i,o=(t.isOptional===`optional`?a.extend({exists:n.boolean().optional()}):a).strict().refine(e=>Object.values(e).some(e=>e!==void 0),`At least one operator is required`);return n.union([r,o])}const f=/* @__PURE__ */ new WeakMap,p=/* @__PURE__ */ new WeakMap,m=n.core.$constructor(`BoundedFilterObject`,(e,t)=>{n.ZodObject.init(e,t);let r=e._zod.parse,i=t.shape.fields,a=i instanceof n.ZodOptional?i.unwrap():void 0,o=a instanceof n.ZodObject?Object.keys(a.shape):Object.keys(t.shape),s={fields:Object.fromEntries(o.map(e=>[e,t.shape[e]])),relations:Object.fromEntries(Object.entries(t.shape).flatMap(([e,t])=>{let n=f.get(t);return n?[[e,n]]:[]}))};e._zod.parse=(e,t)=>D(e.value,s)?r(e,t):(e.issues.push({code:`custom`,input:e.value,message:`Unsupported filter layout or depth/node budget exceeded`}),e)});function h(e){let t=new m(E(e,0)._zod.def);return p.set(t,e),t}function g(e,t,n,r=[],i){let a=e.parse(t),o=i===void 0?void 0:e.parse({...a,filters:i}),s=_(e),c={kind:`and`,children:[v(a.filters,s),v(o?.filters,s)]},l=x(c),u=[...n].sort((e,t)=>e.name.localeCompare(t.name)||e.fields.join(`\0`).localeCompare(t.fields.join(`\0`))),d=r.find(e=>e.value!==null&&e.value!==void 0),f=null;if(d){if(f=u.find(e=>e.name===d.index)??null,!f||S(f,l.values).length===0)throw Error(`Explicit index "${d.index}" is incompatible with guaranteed filters`)}else{let e=0;for(let t of u){let n=S(t,l.values).length;n>e&&(f=t,e=n)}}return{residual:c,bindings:l.values,unsatisfiable:l.impossible,index:f,prefix:f?S(f,l.values):[]}}function _(e){if(!(e instanceof n.ZodObject))return;let t=e.shape.filters;if(!(t instanceof n.ZodOptional))return;let r=t.unwrap(),i=p.get(r);if(i)return i;if(r instanceof n.ZodObject)return{fields:r.shape,relations:Object.fromEntries(Object.entries(r.shape).flatMap(([e,t])=>{let n=f.get(t);return n?[[e,n]]:[]}))}}function v(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=>v(e,t))});else if(e===`not`)n.push({kind:`not`,child:v(i,t)});else if(e===`fields`)for(let[e,t]of Object.entries(i))n.push(...y(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:v(i,r.target)});else{let t=i;n.push({kind:`relation`,name:e,mode:t.some===void 0?`none`:`some`,predicate:v(t.some??t.none,r.target)})}}else n.push(...y(e,i))}return{kind:`and`,children:n}}function y(e,t){return t===void 0?[]:Object(t)===t?Object.entries(t).filter(([,e])=>e!==void 0).map(([t,n])=>({kind:`scalar`,field:e,operator:t,value:n})):[{kind:`scalar`,field:e,operator:`eq`,value:t}]}function b(e,t){return r(e,t)===0}function x(e){let t=/* @__PURE__ */ new Map;if(e.kind===`scalar`){e.operator===`eq`&&!Array.isArray(e.value)&&t.set(e.field,e.value);let n=Array.isArray(e.value)?e.value:void 0;return e.operator===`in`&&n&&n.every(e=>b(e,n[0]))&&t.set(e.field,n[0]),{values:t,impossible:!1}}if(e.kind===`not`||e.kind===`relation`)return{values:t,impossible:!1};let n=e.children.map(x);if(e.kind===`or`){let e=n;for(let[n,r]of e[0]?.values??[])e.every(e=>e.values.has(n)&&b(e.values.get(n),r))&&t.set(n,r);return{values:t,impossible:e.every(e=>e.impossible)}}let r=n.some(e=>e.impossible);for(let e of n)for(let[n,i]of e.values)t.has(n)&&!b(t.get(n),i)&&(r=!0),t.set(n,i);return{values:t,impossible:r}}function S(e,t){let n=[];for(let r of e.fields){if(!t.has(r))break;n.push({field:r,value:t.get(r)})}return n}function C(e){switch(e.kind){case`id`:case`string`:case`float64`:case`int64`:case`boolean`:case`null`:case`literal`:return;case`union`:for(let t of e.members)C(t);return;default:throw Error(`Unsupported scalar filter kind "${e.kind}"`)}}function w(e){switch(e.kind){case`string`:case`float64`:case`int64`:return!0;case`literal`:return n.union([n.string(),n.number(),n.bigint()]).safeParse(e.value).success;case`union`:return e.members.every(w);default:return!1}}function T(e){return h({fields:e,relations:{}})}function E(e,t){let r=e.fields,i=Object.fromEntries(Object.entries(r).filter(([e])=>![`and`,`or`,`not`,`fields`].includes(e))),a={};for(let[r,i]of Object.entries(e.relations)){if(t===4)continue;let e=E(i.target,t+1);a[r]=(i.cardinality===`one`?e.nullable():n.union([n.object({some:e}).strict(),n.object({none:e}).strict()])).optional(),f.set(a[r],i)}let o=n.object({...i,...a,fields:n.object(r).strict().optional()}).strict();if(t===4)return o;let s=E(e,t+1);return o.extend({and:n.array(s).min(1).max(256).optional(),or:n.array(s).min(1).max(256).optional(),not:s.optional()})}function D(e,t){let n=[{value:e,depth:0,context:`expression`,grammar:t}],r=0;for(;n.length;){let e=n.pop();if(++r>256||e.depth>4)return!1;if(Object(e.value)!==e.value)continue;let t=e.value,i=Array.isArray(t)?t:void 0,a=Object.getPrototypeOf(t);if((i?a!==Array.prototype:a!==Object.prototype&&a!==null)||i&&i.length+r+n.length>256)return!1;let o=Reflect.ownKeys(t);if(o.length-+!!i+r+n.length>256||i&&o.length!==i.length+1)return!1;let s=e.context===`expression`?[...Object.keys(e.grammar.fields),...Object.keys(e.grammar.relations),`and`,`or`,`not`,`fields`]:e.context===`fields`?Object.keys(e.grammar.fields):e.context===`predicate`?[`eq`,`in`,`gt`,`gte`,`lt`,`lte`,`exists`]:e.context===`many`?[`some`,`none`]:[];for(let e of s)if(!Object.hasOwn(t,e)&&e in t)return!1;for(let r of o){if(i&&r===`length`)continue;let a=Object.getOwnPropertyDescriptor(t,r),o=String(r);if(!a.enumerable||!(`value`in a)||o!==r||i&&(!/^(0|[1-9]\d*)$/.test(o)||Number(o)>=i.length))return!1;let s=`value`,c=e.depth,l=e.grammar;if(e.context===`expression`){if(s=`predicate`,o===`fields`)s=`fields`;else if(o===`and`||o===`or`)s=`composition`,c++;else if(o===`not`)s=`expression`,c++;else if(e.grammar.relations[o]){let t=e.grammar.relations[o];s=t.cardinality===`one`?`expression`:`many`,l=t.target,c++}}else e.context===`fields`?s=`predicate`:(e.context===`composition`||e.context===`many`)&&(s=`expression`);n.push({value:a.value,depth:c,context:s,grammar:l})}}return!0}export{a as MAX_FILTER_DEPTH,s as MAX_FILTER_IN_VALUES,o as MAX_FILTER_NODES,i as MAX_PAGE_SIZE,g as compileFilters,l as nativePagination,h as relationFilterSchema,u as scalarPaginationSchema,d as scalarPredicateSchema};
1
+ import{convexToZod as e}from"convex-helpers/server/zod4";import{paginationOptsValidator as t}from"convex/server";import{z as n}from"zod";import{compareValues as r}from"convex/values";const i=100,a=4,o=256,s=100,c=e(t),l=c.strict().extend({numItems:c.shape.numItems.int().positive().max(100),maximumRowsRead:c.shape.maximumRowsRead.unwrap().int().positive().optional(),maximumBytesRead:c.shape.maximumBytesRead.unwrap().int().positive().optional()});function u(e,t){let r={};for(let[n,i]of Object.entries(t)){if(i!==!0||!Object.hasOwn(e,n))throw Error(`Invalid scalar filter selection "${n}"`);r[n]=d(e[n]).optional()}let i=E(r);return l.extend({filters:i.optional(),order:n.enum([`asc`,`desc`]).optional()})}function d(t){w(t);let r=e(t).nonoptional(),i=n.object({eq:r.optional(),in:n.array(r).min(1).max(100).optional()}),a=T(t)?i.extend({gt:r.optional(),gte:r.optional(),lt:r.optional(),lte:r.optional()}):i,o=(t.isOptional===`optional`?a.extend({exists:n.boolean().optional()}):a).strict().refine(e=>Object.values(e).some(e=>e!==void 0),`At least one operator is required`);return n.union([r,o])}const f=/* @__PURE__ */ new WeakMap,p=/* @__PURE__ */ new WeakMap,m=/* @__PURE__ */ new WeakMap,h=n.core.$constructor(`BoundedFilterObject`,(e,t)=>{n.ZodObject.init(e,t);let r=e._zod.parse,i=t.shape.fields,a=i instanceof n.ZodOptional?i.unwrap():void 0,o=a instanceof n.ZodObject?Object.keys(a.shape):Object.keys(t.shape),s={variants:p.get(t.shape.variant),fields:Object.fromEntries(o.map(e=>[e,t.shape[e]])),relations:Object.fromEntries(Object.entries(t.shape).flatMap(([e,t])=>{let n=f.get(t);return n?[[e,n]]:[]}))};e._zod.parse=(e,t)=>O(e.value,s)?r(e,t):(e.issues.push({code:`custom`,input:e.value,message:`Unsupported filter layout or depth/node budget exceeded`}),e)});function g(e){let t=new h(D(e,0)._zod.def);return m.set(t,e),t}function _(e,t,n,r=[],i){let a=e.parse(t),o=i===void 0?void 0:e.parse({...a,filters:i}),s=v(e),c={kind:`and`,children:[y(a.filters,s),y(o?.filters,s)]},l=S(c),u=[...n].sort((e,t)=>e.name.localeCompare(t.name)||e.fields.join(`\0`).localeCompare(t.fields.join(`\0`))),d=r.find(e=>e.value!==null&&e.value!==void 0),f=null;if(d){if(f=u.find(e=>e.name===d.index)??null,!f||C(f,l.values).length===0)throw Error(`Explicit index "${d.index}" is incompatible with guaranteed filters`)}else{let e=0;for(let t of u){let n=C(t,l.values).length;n>e&&(f=t,e=n)}}return{residual:c,bindings:l.values,unsatisfiable:l.impossible,index:f,prefix:f?C(f,l.values):[]}}function v(e){if(!(e instanceof n.ZodObject))return;let t=e.shape.filters;if(!(t instanceof n.ZodOptional))return;let r=t.unwrap(),i=m.get(r);if(i)return i;if(r instanceof n.ZodObject)return{fields:r.shape,variants:p.get(r.shape.variant),relations:Object.fromEntries(Object.entries(r.shape).flatMap(([e,t])=>{let n=f.get(t);return n?[[e,n]]:[]}))}}function y(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=>y(e,t))});else if(e===`variant`&&t?.variants){let e=i;n.push({kind:`variant`,discriminator:t.variants.discriminator,tag:e.tag,child:y(e.filters,t.variants.options[e.tag])})}else if(e===`not`)n.push({kind:`not`,child:y(i,t)});else if(e===`fields`)for(let[e,t]of Object.entries(i))n.push(...b(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:y(i,r.target)});else{let t=i;n.push({kind:`relation`,name:e,mode:t.some===void 0?`none`:`some`,predicate:y(t.some??t.none,r.target)})}}else n.push(...b(e,i))}return{kind:`and`,children:n}}function b(e,t){return t===void 0?[]:Object(t)===t?Object.entries(t).filter(([,e])=>e!==void 0).map(([t,n])=>({kind:`scalar`,field:e,operator:t,value:n})):[{kind:`scalar`,field:e,operator:`eq`,value:t}]}function x(e,t){return r(e,t)===0}function S(e){let t=/* @__PURE__ */ new Map;if(e.kind===`variant`)return S({kind:`and`,children:[{kind:`scalar`,field:e.discriminator,operator:`eq`,value:e.tag},e.child]});if(e.kind===`scalar`){e.operator===`eq`&&!Array.isArray(e.value)&&t.set(e.field,e.value);let n=Array.isArray(e.value)?e.value:void 0;return e.operator===`in`&&n&&n.every(e=>x(e,n[0]))&&t.set(e.field,n[0]),{values:t,impossible:!1}}if(e.kind===`not`||e.kind===`relation`)return{values:t,impossible:!1};let n=e.children.map(S);if(e.kind===`or`){let e=n.filter(e=>!e.impossible);for(let[n,r]of e[0]?.values??[])e.every(e=>e.values.has(n)&&x(e.values.get(n),r))&&t.set(n,r);return{values:t,impossible:e.every(e=>e.impossible)}}let r=n.some(e=>e.impossible);for(let e of n)for(let[n,i]of e.values)t.has(n)&&!x(t.get(n),i)&&(r=!0),t.set(n,i);return{values:t,impossible:r}}function C(e,t){let n=[];for(let r of e.fields){if(!t.has(r))break;n.push({field:r,value:t.get(r)})}return n}function w(e){switch(e.kind){case`id`:case`string`:case`float64`:case`int64`:case`boolean`:case`null`:case`literal`:return;case`union`:for(let t of e.members)w(t);return;default:throw Error(`Unsupported scalar filter kind "${e.kind}"`)}}function T(e){switch(e.kind){case`string`:case`float64`:case`int64`:return!0;case`literal`:return n.union([n.string(),n.number(),n.bigint()]).safeParse(e.value).success;case`union`:return e.members.every(T);default:return!1}}function E(e){return g({fields:e,relations:{}})}function D(e,t){let r=e.fields,i=Object.fromEntries(Object.entries(r).filter(([t])=>![`and`,`or`,`not`,`fields`,...e.variants?[`variant`]:[]].includes(t))),a={};for(let[r,i]of Object.entries(e.relations)){if(t===4)continue;let e=D(i.target,t+1);a[r]=(i.cardinality===`one`?e.nullable():n.union([n.object({some:e}).strict(),n.object({none:e}).strict()])).optional(),f.set(a[r],i)}if(e.variants&&t<4){let r=Object.entries(e.variants.options).map(([e,r])=>n.strictObject({tag:n.literal(e),filters:D(r,t+1)}));r.length&&(a.variant=n.union(r).optional(),p.set(a.variant,e.variants))}let o=n.object({...i,...a,fields:n.object(r).strict().optional()}).strict();if(t===4)return o;let s=D(e,t+1);return o.extend({and:n.array(s).min(1).max(256).optional(),or:n.array(s).min(1).max(256).optional(),not:s.optional()})}function O(e,t){let r=[{value:e,depth:0,context:`expression`,grammar:t}],i=0;for(;r.length;){let e=r.pop();if(++i>256||e.depth>4)return!1;if(Object(e.value)!==e.value)continue;let t=e.value,a=Array.isArray(t)?t:void 0,o=Object.getPrototypeOf(t);if((a?o!==Array.prototype:o!==Object.prototype&&o!==null)||a&&a.length+i+r.length>256)return!1;let s=Reflect.ownKeys(t);if(s.length-+!!a+i+r.length>256||a&&s.length!==a.length+1)return!1;let c;switch(e.context){case`expression`:c=[...Object.keys(e.grammar.fields),...Object.keys(e.grammar.relations),`and`,`or`,`not`,`fields`,...e.grammar.variants?[`variant`]:[]];break;case`variant`:c=[`tag`,`filters`];break;case`fields`:c=Object.keys(e.grammar.fields);break;case`predicate`:c=[`eq`,`in`,`gt`,`gte`,`lt`,`lte`,`exists`];break;case`many`:c=[`some`,`none`];break;default:c=[]}for(let e of c)if(!Object.hasOwn(t,e)&&e in t)return!1;for(let i of s){if(a&&i===`length`)continue;let o=Object.getOwnPropertyDescriptor(t,i),s=String(i);if(!o.enumerable||!(`value`in o)||s!==i||a&&(!/^(0|[1-9]\d*)$/.test(s)||Number(s)>=a.length))return!1;let c=`value`,l=e.depth,u=e.grammar;if(e.context===`expression`){if(c=`predicate`,s===`variant`&&e.grammar.variants)c=`variant`,l++;else if(s===`fields`)c=`fields`;else if(s===`and`||s===`or`)c=`composition`,l++;else if(s===`not`)c=`expression`,l++;else if(e.grammar.relations[s]){let t=e.grammar.relations[s];c=t.cardinality===`one`?`expression`:`many`,u=t.target,l++}}else if(e.context===`variant`&&s===`filters`){let r=Object.getOwnPropertyDescriptor(t,`tag`);if(!r||!(`value`in r)||!n.string().safeParse(r.value).success||!Object.hasOwn(e.grammar.variants?.options??{},r.value))return!1;c=`expression`,u=e.grammar.variants.options[String(r.value)]}else e.context===`fields`?c=`predicate`:(e.context===`composition`||e.context===`many`)&&(c=`expression`);r.push({value:o.value,depth:l,context:c,grammar:u})}}return!0}export{a as MAX_FILTER_DEPTH,s as MAX_FILTER_IN_VALUES,o as MAX_FILTER_NODES,i as MAX_PAGE_SIZE,_ as compileFilters,l as nativePagination,g as relationFilterSchema,u as scalarPaginationSchema,d as scalarPredicateSchema};
2
2
  //# sourceMappingURL=filters.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"filters.mjs","names":["MAX_PAGE_SIZE","MAX_FILTER_DEPTH","MAX_FILTER_NODES","MAX_FILTER_IN_VALUES","nativeOptions","convexToZod","paginationOptsValidator","nativePagination","scalarPaginationSchema","predicates","key","selected","selection","validators","scalarPredicateSchema","filters","boundedFilterSchema","z","assertSupportedScalar","validator","scalar","equality","ranged","supportsRange","explicit","value","entry","relationPredicates","filterGrammars","BoundedFilterObject","inst","def","parse","namespace","namespaceObject","selectedKeys","grammar","schema","relation","withinFilterBudget","payload","ctx","relationFilterSchema","filterNodeSchema","compileFilters","parsed","input","server","where","paginationGrammar","residual","normalizeFilters","guaranteed","guaranteedBindings","sorted","indexes","a","b","condition","conditions","index","prefixBindings","length","candidate","usable","root","cached","name","predicate","children","expression","child","field","normalizeScalar","target","operator","valueEqual","compareValues","left","right","values","ast","choices","branches","viable","branch","impossible","prefix","bindings","member","direct","relations","depth","node","pending","nodes","object","array","prototype","keys","consumed","descriptor","context"],"sources":["../../src/querying/filters.ts"],"sourcesContent":["import { convexToZod, type ConvexValidatorFromZod } from 'convex-helpers/server/zod4'\nimport { paginationOptsValidator } from 'convex/server'\nimport { compareValues, type GenericId, type GenericValidator } from 'convex/values'\nimport { z } from 'zod'\n\n/** Shared hard ceilings; the execution engine may impose lower budgets. */\nexport const MAX_PAGE_SIZE = 100\nexport const MAX_FILTER_DEPTH = 4\nexport const MAX_FILTER_NODES = 256\nexport const MAX_FILTER_IN_VALUES = 100\n\nexport type FieldSchemas = Record<string, z.ZodType>\ntype NativeField<Schema extends z.ZodType> = ConvexValidatorFromZod<Schema, 'required'>\ntype ScalarKind = 'id' | 'string' | 'float64' | 'int64' | 'boolean' | 'null' | 'literal'\ntype SupportedNative<Validator extends GenericValidator> = Validator extends { kind: ScalarKind }\n\t? true\n\t: Validator extends { kind: 'union'; members: infer Members extends GenericValidator[] }\n\t\t? false extends SupportedNative<Members[number]>\n\t\t\t? false\n\t\t\t: true\n\t\t: false\n\nexport type ScalarSelection<Fields extends FieldSchemas> = {\n\t[\n\t\tKey in keyof Fields as false extends SupportedNative<NativeField<Fields[Key]>> ? never : Key\n\t]?: true\n}\nexport type ExactScalarSelection<Fields extends FieldSchemas, Selection> = Selection & {\n\t[Key in keyof Selection]: Key extends keyof ScalarSelection<Fields>\n\t\t? Selection[Key] extends true\n\t\t\t? true\n\t\t\t: never\n\t\t: never\n}\n\ntype NativeValue<Schema extends z.ZodType> = Exclude<NativeField<Schema>['type'], undefined>\ntype FilterInputValue<Value> = Value extends GenericId<string> ? string : Value\ntype FilterValue<Schema extends z.ZodType, Side extends 'input' | 'output'> = Side extends 'input'\n\t? FilterInputValue<NativeValue<Schema>>\n\t: NativeValue<Schema>\ntype RangeKind = 'string' | 'float64' | 'int64' | 'literal'\ntype OrderedNative<Validator extends GenericValidator> = Validator extends { kind: RangeKind }\n\t? Validator['type'] extends string | number | bigint | undefined\n\t\t? true\n\t\t: false\n\t: Validator extends { kind: 'union'; members: infer Members extends GenericValidator[] }\n\t\t? false extends OrderedNative<Members[number]>\n\t\t\t? false\n\t\t\t: true\n\t\t: false\ntype OrderedValue<Schema extends z.ZodType> =\n\tOrderedNative<NativeField<Schema>> extends true ? NativeValue<Schema> : never\n\nexport type ScalarPredicate<Schema extends z.ZodType, Side extends 'input' | 'output'> =\n\t| FilterValue<Schema, Side>\n\t| ({ eq?: FilterValue<Schema, Side>; in?: FilterValue<Schema, Side>[] } & ([\n\t\t\tOrderedValue<Schema>,\n\t ] extends [never]\n\t\t\t? {}\n\t\t\t: {\n\t\t\t\t\tgt?: OrderedValue<Schema>\n\t\t\t\t\tgte?: OrderedValue<Schema>\n\t\t\t\t\tlt?: OrderedValue<Schema>\n\t\t\t\t\tlte?: OrderedValue<Schema>\n\t\t\t\t}) &\n\t\t\t(NativeField<Schema>['isOptional'] extends 'optional' ? { exists?: boolean } : {}))\n\ntype SelectedFieldFilters<\n\tFields extends FieldSchemas,\n\tSelection,\n\tSide extends 'input' | 'output',\n> = {\n\t[Key in Extract<keyof Selection, keyof Fields>]?: ScalarPredicate<Fields[Key], Side>\n}\n\n/** Direct shorthand and explicit fields combine with AND. The fields namespace\n * accepts every selected scalar name, including boolean-control/namespace names.\n */\ntype FilterDepth = readonly [1, 1, 1, 1]\ntype Tail<Depth extends readonly 1[]> = Depth extends readonly [1, ...infer Rest extends 1[]]\n\t? Rest\n\t: []\n\nexport type ScalarFilters<\n\tFields extends FieldSchemas,\n\tSelection,\n\tSide extends 'input' | 'output' = 'output',\n\tDepth extends readonly 1[] = FilterDepth,\n> = Omit<SelectedFieldFilters<Fields, Selection, Side>, 'and' | 'or' | 'not' | 'fields'> & {\n\tfields?: SelectedFieldFilters<Fields, Selection, Side>\n} & (Depth extends readonly []\n\t\t? {}\n\t\t: {\n\t\t\t\tand?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>[]\n\t\t\t\tor?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>[]\n\t\t\t\tnot?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>\n\t\t\t})\n\nconst nativeOptions = convexToZod(paginationOptsValidator)\nexport const nativePagination = nativeOptions.strict().extend({\n\tnumItems: nativeOptions.shape.numItems.int().positive().max(MAX_PAGE_SIZE),\n\tmaximumRowsRead: nativeOptions.shape.maximumRowsRead.unwrap().int().positive().optional(),\n\tmaximumBytesRead: nativeOptions.shape.maximumBytesRead.unwrap().int().positive().optional(),\n})\nexport type NativePagination = z.output<typeof nativePagination>\n/** Preserve the object boundary so pagination options can be registered directly as query args. */\nexport type PaginationOptionsSchema<Filters, InputFilters = Filters> = z.ZodObject<\n\t(typeof nativePagination)['shape'] & {\n\t\tfilters: z.ZodOptional<z.ZodType<Filters, InputFilters>>\n\t\torder: z.ZodOptional<z.ZodEnum<{ asc: 'asc'; desc: 'desc' }>>\n\t},\n\tz.core.$strict\n>\nexport type ScalarPaginationSchema<\n\tFields extends FieldSchemas,\n\tSelection,\n> = PaginationOptionsSchema<\n\tScalarFilters<Fields, Selection>,\n\tScalarFilters<Fields, Selection, 'input'>\n>\n\n/** Scalar-only schema constructor shared by table and future registry boundaries.\n * Convex metadata describes stored INPUT, so storage refinements/transforms never run here.\n */\nexport function scalarPaginationSchema<\n\tFields extends FieldSchemas,\n\tconst Selection extends ScalarSelection<Fields>,\n>(\n\tvalidators: Record<string, GenericValidator>,\n\tselection: ExactScalarSelection<Fields, Selection>,\n): ScalarPaginationSchema<Fields, Selection> {\n\tconst predicates: FieldSchemas = {}\n\tfor (const [key, selected] of Object.entries(selection)) {\n\t\tif (selected !== true || !Object.hasOwn(validators, key)) {\n\t\t\tthrow new Error(`Invalid scalar filter selection \"${key}\"`)\n\t\t}\n\t\tpredicates[key] = scalarPredicateSchema(validators[key]).optional()\n\t}\n\tconst filters = boundedFilterSchema(predicates)\n\tconst result = nativePagination.extend({\n\t\tfilters: filters.optional(),\n\t\torder: z.enum(['asc', 'desc']).optional(),\n\t})\n\t// SAFETY: selected validators generate the same native scalar input/output relationships\n\t// described by ScalarFilters, including converter-registered ID brands.\n\treturn result as ScalarPaginationSchema<Fields, Selection>\n}\n\nexport function scalarPredicateSchema(validator: GenericValidator): z.ZodType {\n\tassertSupportedScalar(validator)\n\tconst scalar = convexToZod(validator).nonoptional()\n\tconst equality = z.object({\n\t\teq: scalar.optional(),\n\t\tin: z.array(scalar).min(1).max(MAX_FILTER_IN_VALUES).optional(),\n\t})\n\tconst ranged = supportsRange(validator)\n\t\t? equality.extend({\n\t\t\t\tgt: scalar.optional(),\n\t\t\t\tgte: scalar.optional(),\n\t\t\t\tlt: scalar.optional(),\n\t\t\t\tlte: scalar.optional(),\n\t\t\t})\n\t\t: equality\n\tconst existence =\n\t\tvalidator.isOptional === 'optional' ? ranged.extend({ exists: z.boolean().optional() }) : ranged\n\tconst explicit = existence\n\t\t.strict()\n\t\t.refine(\n\t\t\t(value) => Object.values(value).some((entry) => entry !== undefined),\n\t\t\t'At least one operator is required',\n\t\t)\n\treturn z.union([scalar, explicit])\n}\n\ntype FilterTree =\n\t| string\n\t| number\n\t| bigint\n\t| boolean\n\t| null\n\t| undefined\n\t| FilterTree[]\n\t| { [key: string]: FilterTree }\n\n// Compose Zod's exported core constructor with its ordinary object initializer.\n// Clones use this constructor again, preserving preflight without replacing the\n// converter-recognized object definition or any native scalar/ID validators.\nconst relationPredicates = new WeakMap<\n\tz.core.$ZodType,\n\t{ cardinality: 'one' | 'many'; target: FilterGrammar }\n>()\nconst 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\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\trelations: Record<string, { cardinality: 'one' | 'many'; target: FilterGrammar }>\n}\n\nexport function relationFilterSchema(grammar: FilterGrammar): z.ZodType {\n\tconst schema = new BoundedFilterObject(filterNodeSchema(grammar, 0)._zod.def)\n\tfilterGrammars.set(schema, grammar)\n\treturn schema\n}\n\nexport type FilterScalar = string | number | bigint | boolean | null\nexport type FilterOperator = 'eq' | 'in' | 'gt' | 'gte' | 'lt' | 'lte' | 'exists'\nexport type FilterAst =\n\t| { kind: 'and' | 'or'; children: FilterAst[] }\n\t| { kind: 'not'; child: FilterAst }\n\t| {\n\t\t\tkind: 'scalar'\n\t\t\tfield: string\n\t\t\toperator: FilterOperator\n\t\t\tvalue: FilterScalar | FilterScalar[]\n\t }\n\t| { kind: 'relation'; name: string; mode: 'one' | 'some' | 'none'; predicate: FilterAst | null }\nexport interface QueryIndex {\n\tname: string\n\tfields: readonly string[]\n}\nexport interface IndexCondition {\n\tvalue: FilterScalar | undefined\n\tindex: string\n}\nexport interface CompiledFilters {\n\tresidual: FilterAst\n\tbindings: ReadonlyMap<string, FilterScalar>\n\tunsatisfiable: boolean\n\tindex: QueryIndex | null\n\tprefix: readonly { field: string; value: FilterScalar }[]\n}\ntype FilterSpec<Schema extends z.ZodType> =\n\tz.input<Schema> extends { filters?: infer Spec } ? Spec : never\nexport type ServerFilter<Fields extends FieldSchemas> = ScalarFilters<\n\tFields,\n\tScalarSelection<Fields>\n>\n\ninterface BindingResult {\n\tvalues: Map<string, FilterScalar>\n\timpossible: boolean\n}\n\n/** Validate first; where is independently validated and ANDed with client predicates.\n * OR remains a residual expression over one traversal, never multiple streams.\n */\nexport function compileFilters<Schema extends z.ZodType>(\n\tschema: Schema,\n\tinput: z.input<Schema>,\n\tindexes: readonly QueryIndex[],\n\tconditions: readonly IndexCondition[] = [],\n\twhere?: FilterSpec<Schema>,\n): CompiledFilters {\n\t// SAFETY: pagination schemas validate filters before this internal AST adapter reads them.\n\tconst parsed = schema.parse(input) as { filters?: FilterTree }\n\t// SAFETY: server filters use the same validation boundary as client filters.\n\tconst server =\n\t\twhere === undefined\n\t\t\t? undefined\n\t\t\t: (schema.parse({ ...parsed, filters: where }) as { filters?: FilterTree })\n\tconst grammar = paginationGrammar(schema)\n\tconst residual: FilterAst = {\n\t\tkind: 'and',\n\t\tchildren: [\n\t\t\tnormalizeFilters(parsed.filters, grammar),\n\t\t\tnormalizeFilters(server?.filters, grammar),\n\t\t],\n\t}\n\tconst guaranteed = guaranteedBindings(residual)\n\tconst sorted = [...indexes].sort(\n\t\t(a, b) =>\n\t\t\ta.name.localeCompare(b.name) || a.fields.join('\\0').localeCompare(b.fields.join('\\0')),\n\t)\n\tconst condition = conditions.find((entry) => entry.value !== null && entry.value !== undefined)\n\tlet index: QueryIndex | null = null\n\tif (condition) {\n\t\tindex = sorted.find((entry) => entry.name === condition.index) ?? null\n\t\tif (!index || prefixBindings(index, guaranteed.values).length === 0)\n\t\t\tthrow new Error(`Explicit index \"${condition.index}\" is incompatible with guaranteed filters`)\n\t} else {\n\t\tlet length = 0\n\t\tfor (const candidate of sorted) {\n\t\t\tconst usable = prefixBindings(candidate, guaranteed.values).length\n\t\t\tif (usable > length) {\n\t\t\t\tindex = candidate\n\t\t\t\tlength = usable\n\t\t\t}\n\t\t}\n\t}\n\treturn {\n\t\tresidual,\n\t\tbindings: guaranteed.values,\n\t\tunsatisfiable: guaranteed.impossible,\n\t\tindex,\n\t\tprefix: index ? prefixBindings(index, guaranteed.values) : [],\n\t}\n}\nfunction paginationGrammar(schema: z.ZodType): FilterGrammar | undefined {\n\tif (!(schema instanceof z.ZodObject)) return undefined\n\tconst filters = schema.shape.filters\n\tif (!(filters instanceof z.ZodOptional)) return undefined\n\tconst root = filters.unwrap()\n\tconst cached = filterGrammars.get(root)\n\tif (cached) return cached\n\tif (!(root instanceof z.ZodObject)) return undefined\n\treturn {\n\t\tfields: root.shape,\n\t\trelations: Object.fromEntries(\n\t\t\tObject.entries(root.shape).flatMap(([name, predicate]) => {\n\t\t\t\t// SAFETY: object shapes contain Zod schemas; metadata only recognizes predicates created by our grammar.\n\t\t\t\tconst relation = relationPredicates.get(predicate as z.core.$ZodType)\n\t\t\t\treturn relation ? [[name, relation]] : []\n\t\t\t}),\n\t\t),\n\t}\n}\nfunction normalizeFilters(value: FilterTree, grammar?: FilterGrammar): FilterAst {\n\tconst children: FilterAst[] = []\n\tif (!value) return { kind: 'and', children }\n\t// SAFETY: the caller parsed a strict filter object; its grammar has no array/primitive expression.\n\tconst expression = value as Record<string, FilterTree>\n\tfor (const [name, predicate] of Object.entries(expression)) {\n\t\tif (predicate === undefined) continue\n\t\tif (name === 'and' || name === 'or') {\n\t\t\t// SAFETY: boolean composition is validated as an array of expressions.\n\t\t\tchildren.push({\n\t\t\t\tkind: name,\n\t\t\t\tchildren: (predicate as FilterTree[]).map((child) => normalizeFilters(child, grammar)),\n\t\t\t})\n\t\t} else if (name === 'not')\n\t\t\tchildren.push({ kind: 'not', child: normalizeFilters(predicate, grammar) })\n\t\telse if (name === 'fields') {\n\t\t\t// SAFETY: fields is a strict selected scalar namespace.\n\t\t\tfor (const [field, scalar] of Object.entries(predicate as Record<string, FilterTree>))\n\t\t\t\tchildren.push(...normalizeScalar(field, scalar))\n\t\t} else if (grammar?.relations[name]) {\n\t\t\tconst relation = grammar.relations[name]\n\t\t\tif (relation.cardinality === 'one')\n\t\t\t\tchildren.push({\n\t\t\t\t\tkind: 'relation',\n\t\t\t\t\tname,\n\t\t\t\t\tmode: 'one',\n\t\t\t\t\tpredicate: predicate === null ? null : normalizeFilters(predicate, relation.target),\n\t\t\t\t})\n\t\t\telse {\n\t\t\t\t// SAFETY: many predicates validate as exactly one some/none target expression.\n\t\t\t\tconst target = predicate as { some?: FilterTree; none?: FilterTree }\n\t\t\t\tchildren.push({\n\t\t\t\t\tkind: 'relation',\n\t\t\t\t\tname,\n\t\t\t\t\tmode: target.some === undefined ? 'none' : 'some',\n\t\t\t\t\tpredicate: normalizeFilters(target.some ?? target.none, relation.target),\n\t\t\t\t})\n\t\t\t}\n\t\t} else children.push(...normalizeScalar(name, predicate))\n\t}\n\treturn { kind: 'and', children }\n}\nfunction normalizeScalar(field: string, predicate: FilterTree): FilterAst[] {\n\tif (predicate === undefined) return []\n\tif (Object(predicate) !== predicate) {\n\t\t// SAFETY: native scalar grammar excludes object/array values and preserves branded ID strings.\n\t\treturn [{ kind: 'scalar', field, operator: 'eq', value: predicate as FilterScalar }]\n\t}\n\t// SAFETY: only selected supported scalar operator objects survive validation.\n\treturn Object.entries(predicate as Record<FilterOperator, FilterScalar | FilterScalar[]>)\n\t\t.filter(([, value]) => value !== undefined)\n\t\t.map(([operator, value]) => ({\n\t\t\tkind: 'scalar',\n\t\t\tfield,\n\t\t\toperator: operator as FilterOperator,\n\t\t\tvalue,\n\t\t}))\n}\nfunction valueEqual(left: FilterScalar, right: FilterScalar): boolean {\n\treturn compareValues(left, right) === 0\n}\nfunction guaranteedBindings(ast: FilterAst): BindingResult {\n\tconst values = new Map<string, FilterScalar>()\n\tif (ast.kind === 'scalar') {\n\t\tif (ast.operator === 'eq' && !Array.isArray(ast.value)) values.set(ast.field, ast.value)\n\t\tconst choices = Array.isArray(ast.value) ? ast.value : undefined\n\t\tif (ast.operator === 'in' && choices && choices.every((value) => valueEqual(value, choices[0])))\n\t\t\tvalues.set(ast.field, choices[0])\n\t\treturn { values, impossible: false }\n\t}\n\tif (ast.kind === 'not' || ast.kind === 'relation') return { values, impossible: false }\n\tconst branches = ast.children.map(guaranteedBindings)\n\tif (ast.kind === 'or') {\n\t\tconst viable = branches\n\t\tfor (const [field, value] of viable[0]?.values ?? [])\n\t\t\tif (\n\t\t\t\tviable.every(\n\t\t\t\t\t(branch) => branch.values.has(field) && valueEqual(branch.values.get(field)!, value),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tvalues.set(field, value)\n\t\treturn { values, impossible: viable.every((branch) => branch.impossible) }\n\t}\n\tlet impossible = branches.some((branch) => branch.impossible)\n\tfor (const branch of branches)\n\t\tfor (const [field, value] of branch.values) {\n\t\t\tif (values.has(field) && !valueEqual(values.get(field)!, value)) impossible = true\n\t\t\tvalues.set(field, value)\n\t\t}\n\treturn { values, impossible }\n}\nfunction prefixBindings(\n\tindex: QueryIndex,\n\tbindings: ReadonlyMap<string, FilterScalar>,\n): { field: string; value: FilterScalar }[] {\n\tconst prefix: { field: string; value: FilterScalar }[] = []\n\tfor (const field of index.fields) {\n\t\tif (!bindings.has(field)) break\n\t\tprefix.push({ field, value: bindings.get(field)! })\n\t}\n\treturn prefix\n}\n\nfunction assertSupportedScalar(validator: GenericValidator): void {\n\tswitch (validator.kind) {\n\t\tcase 'id':\n\t\tcase 'string':\n\t\tcase 'float64':\n\t\tcase 'int64':\n\t\tcase 'boolean':\n\t\tcase 'null':\n\t\tcase 'literal':\n\t\t\treturn\n\t\tcase 'union':\n\t\t\tfor (const member of validator.members) {\n\t\t\t\tassertSupportedScalar(member)\n\t\t\t}\n\t\t\treturn\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported scalar filter kind \"${validator.kind}\"`)\n\t}\n}\n\nfunction supportsRange(validator: GenericValidator): boolean {\n\tswitch (validator.kind) {\n\t\tcase 'string':\n\t\tcase 'float64':\n\t\tcase 'int64':\n\t\t\treturn true\n\t\tcase 'literal':\n\t\t\treturn z.union([z.string(), z.number(), z.bigint()]).safeParse(validator.value).success\n\t\tcase 'union':\n\t\t\treturn validator.members.every(supportsRange)\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n\nfunction boundedFilterSchema(predicates: FieldSchemas): z.ZodType {\n\treturn relationFilterSchema({ fields: predicates, relations: {} })\n}\n\nfunction filterNodeSchema(grammar: FilterGrammar, depth: number): z.ZodObject {\n\tconst predicates = grammar.fields\n\tconst direct = Object.fromEntries(\n\t\tObject.entries(predicates).filter(([key]) => !['and', 'or', 'not', 'fields'].includes(key)),\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)\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\tconst node = z\n\t\t.object({ ...direct, ...relations, fields: z.object(predicates).strict().optional() })\n\t\t.strict()\n\tif (depth === MAX_FILTER_DEPTH) return node\n\tconst child = filterNodeSchema(grammar, depth + 1)\n\treturn node.extend({\n\t\tand: z.array(child).min(1).max(MAX_FILTER_NODES).optional(),\n\t\tor: z.array(child).min(1).max(MAX_FILTER_NODES).optional(),\n\t\tnot: child.optional(),\n\t})\n}\n\nfunction withinFilterBudget(value: FilterTree, grammar: FilterGrammar): boolean {\n\ttype Context = 'expression' | 'fields' | 'predicate' | 'composition' | 'many' | '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\tconst consumed =\n\t\t\tentry.context === 'expression'\n\t\t\t\t? [\n\t\t\t\t\t\t...Object.keys(entry.grammar.fields),\n\t\t\t\t\t\t...Object.keys(entry.grammar.relations),\n\t\t\t\t\t\t'and',\n\t\t\t\t\t\t'or',\n\t\t\t\t\t\t'not',\n\t\t\t\t\t\t'fields',\n\t\t\t\t\t]\n\t\t\t\t: entry.context === 'fields'\n\t\t\t\t\t? Object.keys(entry.grammar.fields)\n\t\t\t\t\t: entry.context === 'predicate'\n\t\t\t\t\t\t? ['eq', 'in', 'gt', 'gte', 'lt', 'lte', 'exists']\n\t\t\t\t\t\t: entry.context === 'many'\n\t\t\t\t\t\t\t? ['some', 'none']\n\t\t\t\t\t\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 === '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 === 'fields') context = 'predicate'\n\t\t\telse if (entry.context === 'composition' || entry.context === 'many') context = 'expression'\n\t\t\tpending.push({ value: descriptor.value, depth, context, grammar: target })\n\t\t}\n\t}\n\treturn true\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"filters.mjs","names":["MAX_PAGE_SIZE","MAX_FILTER_DEPTH","MAX_FILTER_NODES","MAX_FILTER_IN_VALUES","nativeOptions","convexToZod","paginationOptsValidator","nativePagination","scalarPaginationSchema","predicates","key","selected","selection","validators","scalarPredicateSchema","filters","boundedFilterSchema","z","assertSupportedScalar","validator","scalar","equality","ranged","supportsRange","explicit","value","entry","relationPredicates","variantPredicates","filterGrammars","BoundedFilterObject","inst","def","parse","namespace","namespaceObject","selectedKeys","grammar","schema","relation","withinFilterBudget","payload","ctx","relationFilterSchema","filterNodeSchema","compileFilters","parsed","input","server","where","paginationGrammar","residual","normalizeFilters","guaranteed","guaranteedBindings","sorted","indexes","a","b","condition","conditions","index","prefixBindings","length","candidate","usable","root","cached","name","predicate","children","expression","child","scoped","field","normalizeScalar","target","operator","valueEqual","compareValues","left","right","values","ast","choices","branches","viable","branch","impossible","prefix","bindings","member","direct","relations","depth","options","tag","node","pending","nodes","object","array","prototype","keys","consumed","descriptor","context"],"sources":["../../src/querying/filters.ts"],"sourcesContent":["import { convexToZod, type ConvexValidatorFromZod } from 'convex-helpers/server/zod4'\nimport { paginationOptsValidator } from 'convex/server'\nimport { compareValues, type GenericId, type GenericValidator } from 'convex/values'\nimport { z } from 'zod'\n\n/** Shared hard ceilings; the execution engine may impose lower budgets. */\nexport const MAX_PAGE_SIZE = 100\nexport const MAX_FILTER_DEPTH = 4\nexport const MAX_FILTER_NODES = 256\nexport const MAX_FILTER_IN_VALUES = 100\n\nexport type FieldSchemas = Record<string, z.ZodType>\ntype NativeField<Schema extends z.ZodType> = ConvexValidatorFromZod<Schema, 'required'>\ntype ScalarKind = 'id' | 'string' | 'float64' | 'int64' | 'boolean' | 'null' | 'literal'\ntype SupportedNative<Validator extends GenericValidator> = Validator extends { kind: ScalarKind }\n\t? true\n\t: Validator extends { kind: 'union'; members: infer Members extends GenericValidator[] }\n\t\t? false extends SupportedNative<Members[number]>\n\t\t\t? false\n\t\t\t: true\n\t\t: false\n\nexport type ScalarSelection<Fields extends FieldSchemas> = {\n\t[\n\t\tKey in keyof Fields as false extends SupportedNative<NativeField<Fields[Key]>> ? never : Key\n\t]?: true\n}\nexport type ExactScalarSelection<Fields extends FieldSchemas, Selection> = Selection & {\n\t[Key in keyof Selection]: Key extends keyof ScalarSelection<Fields>\n\t\t? Selection[Key] extends true\n\t\t\t? true\n\t\t\t: never\n\t\t: never\n}\n\ntype NativeValue<Schema extends z.ZodType> = Exclude<NativeField<Schema>['type'], undefined>\ntype FilterInputValue<Value> = Value extends GenericId<string> ? string : Value\ntype FilterValue<Schema extends z.ZodType, Side extends 'input' | 'output'> = Side extends 'input'\n\t? FilterInputValue<NativeValue<Schema>>\n\t: NativeValue<Schema>\ntype RangeKind = 'string' | 'float64' | 'int64' | 'literal'\ntype OrderedNative<Validator extends GenericValidator> = Validator extends { kind: RangeKind }\n\t? Validator['type'] extends string | number | bigint | undefined\n\t\t? true\n\t\t: false\n\t: Validator extends { kind: 'union'; members: infer Members extends GenericValidator[] }\n\t\t? false extends OrderedNative<Members[number]>\n\t\t\t? false\n\t\t\t: true\n\t\t: false\ntype OrderedValue<Schema extends z.ZodType> =\n\tOrderedNative<NativeField<Schema>> extends true ? NativeValue<Schema> : never\n\nexport type ScalarPredicate<Schema extends z.ZodType, Side extends 'input' | 'output'> =\n\t| FilterValue<Schema, Side>\n\t| ({ eq?: FilterValue<Schema, Side>; in?: FilterValue<Schema, Side>[] } & ([\n\t\t\tOrderedValue<Schema>,\n\t ] extends [never]\n\t\t\t? {}\n\t\t\t: {\n\t\t\t\t\tgt?: OrderedValue<Schema>\n\t\t\t\t\tgte?: OrderedValue<Schema>\n\t\t\t\t\tlt?: OrderedValue<Schema>\n\t\t\t\t\tlte?: OrderedValue<Schema>\n\t\t\t\t}) &\n\t\t\t('optional' extends NativeField<Schema>['isOptional'] ? { exists?: boolean } : {}))\n\ntype SelectedFieldFilters<\n\tFields extends FieldSchemas,\n\tSelection,\n\tSide extends 'input' | 'output',\n> = {\n\t[Key in Extract<keyof Selection, keyof Fields>]?: ScalarPredicate<Fields[Key], Side>\n}\n\n/** Direct shorthand and explicit fields combine with AND. The fields namespace\n * accepts every selected scalar name, including boolean-control/namespace names.\n */\ntype FilterDepth = readonly [1, 1, 1, 1]\ntype Tail<Depth extends readonly 1[]> = Depth extends readonly [1, ...infer Rest extends 1[]]\n\t? Rest\n\t: []\n\nexport type ScalarFilters<\n\tFields extends FieldSchemas,\n\tSelection,\n\tSide extends 'input' | 'output' = 'output',\n\tDepth extends readonly 1[] = FilterDepth,\n> = Omit<SelectedFieldFilters<Fields, Selection, Side>, 'and' | 'or' | 'not' | 'fields'> & {\n\tfields?: SelectedFieldFilters<Fields, Selection, Side>\n} & (Depth extends readonly []\n\t\t? {}\n\t\t: {\n\t\t\t\tand?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>[]\n\t\t\t\tor?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>[]\n\t\t\t\tnot?: ScalarFilters<Fields, Selection, Side, Tail<Depth>>\n\t\t\t})\n\nconst nativeOptions = convexToZod(paginationOptsValidator)\nexport const nativePagination = nativeOptions.strict().extend({\n\tnumItems: nativeOptions.shape.numItems.int().positive().max(MAX_PAGE_SIZE),\n\tmaximumRowsRead: nativeOptions.shape.maximumRowsRead.unwrap().int().positive().optional(),\n\tmaximumBytesRead: nativeOptions.shape.maximumBytesRead.unwrap().int().positive().optional(),\n})\nexport type NativePagination = z.output<typeof nativePagination>\n/** Preserve the object boundary so pagination options can be registered directly as query args. */\nexport type PaginationOptionsSchema<Filters, InputFilters = Filters> = z.ZodObject<\n\t(typeof nativePagination)['shape'] & {\n\t\tfilters: z.ZodOptional<z.ZodType<Filters, InputFilters>>\n\t\torder: z.ZodOptional<z.ZodEnum<{ asc: 'asc'; desc: 'desc' }>>\n\t},\n\tz.core.$strict\n>\nexport type ScalarPaginationSchema<\n\tFields extends FieldSchemas,\n\tSelection,\n> = PaginationOptionsSchema<\n\tScalarFilters<Fields, Selection>,\n\tScalarFilters<Fields, Selection, 'input'>\n>\n\n/** Scalar-only schema constructor shared by table and future registry boundaries.\n * Convex metadata describes stored INPUT, so storage refinements/transforms never run here.\n */\nexport function scalarPaginationSchema<\n\tFields extends FieldSchemas,\n\tconst Selection extends ScalarSelection<Fields>,\n>(\n\tvalidators: Record<string, GenericValidator>,\n\tselection: ExactScalarSelection<Fields, Selection>,\n): ScalarPaginationSchema<Fields, Selection> {\n\tconst predicates: FieldSchemas = {}\n\tfor (const [key, selected] of Object.entries(selection)) {\n\t\tif (selected !== true || !Object.hasOwn(validators, key)) {\n\t\t\tthrow new Error(`Invalid scalar filter selection \"${key}\"`)\n\t\t}\n\t\tpredicates[key] = scalarPredicateSchema(validators[key]).optional()\n\t}\n\tconst filters = boundedFilterSchema(predicates)\n\tconst result = nativePagination.extend({\n\t\tfilters: filters.optional(),\n\t\torder: z.enum(['asc', 'desc']).optional(),\n\t})\n\t// SAFETY: selected validators generate the same native scalar input/output relationships\n\t// described by ScalarFilters, including converter-registered ID brands.\n\treturn result as ScalarPaginationSchema<Fields, Selection>\n}\n\nexport function scalarPredicateSchema(validator: GenericValidator): z.ZodType {\n\tassertSupportedScalar(validator)\n\tconst scalar = convexToZod(validator).nonoptional()\n\tconst equality = z.object({\n\t\teq: scalar.optional(),\n\t\tin: z.array(scalar).min(1).max(MAX_FILTER_IN_VALUES).optional(),\n\t})\n\tconst ranged = supportsRange(validator)\n\t\t? equality.extend({\n\t\t\t\tgt: scalar.optional(),\n\t\t\t\tgte: scalar.optional(),\n\t\t\t\tlt: scalar.optional(),\n\t\t\t\tlte: scalar.optional(),\n\t\t\t})\n\t\t: equality\n\tconst existence =\n\t\tvalidator.isOptional === 'optional' ? ranged.extend({ exists: z.boolean().optional() }) : ranged\n\tconst explicit = existence\n\t\t.strict()\n\t\t.refine(\n\t\t\t(value) => Object.values(value).some((entry) => entry !== undefined),\n\t\t\t'At least one operator is required',\n\t\t)\n\treturn z.union([scalar, explicit])\n}\n\ntype FilterTree =\n\t| string\n\t| number\n\t| bigint\n\t| boolean\n\t| null\n\t| undefined\n\t| FilterTree[]\n\t| { [key: string]: FilterTree }\n\n// Compose Zod's exported core constructor with its ordinary object initializer.\n// Clones use this constructor again, preserving preflight without replacing the\n// converter-recognized object definition or any native scalar/ID validators.\nconst relationPredicates = new WeakMap<\n\tz.core.$ZodType,\n\t{ cardinality: 'one' | 'many'; target: FilterGrammar }\n>()\nconst variantPredicates = new WeakMap<z.core.$ZodType, NonNullable<FilterGrammar['variants']>>()\nconst filterGrammars = new WeakMap<z.core.$ZodType, FilterGrammar>()\n\nconst BoundedFilterObject = z.core.$constructor<z.ZodObject>('BoundedFilterObject', (inst, def) => {\n\tz.ZodObject.init(inst, def)\n\tconst parse = inst._zod.parse\n\tconst namespace = def.shape.fields\n\tconst namespaceObject = namespace instanceof z.ZodOptional ? namespace.unwrap() : undefined\n\tconst selectedKeys =\n\t\tnamespaceObject instanceof z.ZodObject\n\t\t\t? Object.keys(namespaceObject.shape)\n\t\t\t: Object.keys(def.shape)\n\tconst grammar: FilterGrammar = {\n\t\tvariants: variantPredicates.get(def.shape.variant),\n\t\tfields: Object.fromEntries(selectedKeys.map((key) => [key, def.shape[key]])),\n\t\trelations: Object.fromEntries(\n\t\t\tObject.entries(def.shape).flatMap(([key, schema]) => {\n\t\t\t\tconst relation = relationPredicates.get(schema)\n\t\t\t\treturn relation ? [[key, relation]] : []\n\t\t\t}),\n\t\t),\n\t}\n\t// Zod refinements run after parsing. Guard parse itself so both the deferred\n\t// no-check run and the refinement run retain the same pre-child boundary.\n\tinst._zod.parse = (payload, ctx) => {\n\t\t// SAFETY: preflight examines layout and budgets only; Zod still validates values.\n\t\tif (!withinFilterBudget(payload.value as FilterTree, grammar)) {\n\t\t\tpayload.issues.push({\n\t\t\t\tcode: 'custom',\n\t\t\t\tinput: payload.value,\n\t\t\t\tmessage: 'Unsupported filter layout or depth/node budget exceeded',\n\t\t\t})\n\t\t\treturn payload\n\t\t}\n\t\treturn parse(payload, ctx)\n\t}\n})\n\n/** Each boolean child and relation target expression consumes one of four edges.\n * some/none wrappers and fields namespaces consume nodes, but no extra depth.\n */\nexport interface FilterGrammar {\n\tfields: FieldSchemas\n\tvariants?: { discriminator: string; options: Record<string, FilterGrammar> }\n\trelations: Record<string, { cardinality: 'one' | 'many'; target: FilterGrammar }>\n}\n\nexport function relationFilterSchema(grammar: FilterGrammar): z.ZodType {\n\tconst schema = new BoundedFilterObject(filterNodeSchema(grammar, 0)._zod.def)\n\tfilterGrammars.set(schema, grammar)\n\treturn schema\n}\n\nexport type FilterScalar = string | number | bigint | boolean | null\nexport type FilterOperator = 'eq' | 'in' | 'gt' | 'gte' | 'lt' | 'lte' | 'exists'\nexport type FilterAst =\n\t| { kind: 'and' | 'or'; children: FilterAst[] }\n\t| { kind: 'not'; child: FilterAst }\n\t| { kind: 'variant'; discriminator: string; tag: string; child: FilterAst }\n\t| {\n\t\t\tkind: 'scalar'\n\t\t\tfield: string\n\t\t\toperator: FilterOperator\n\t\t\tvalue: FilterScalar | FilterScalar[]\n\t }\n\t| { kind: 'relation'; name: string; mode: 'one' | 'some' | 'none'; predicate: FilterAst | null }\nexport interface QueryIndex {\n\tname: string\n\tfields: readonly string[]\n}\nexport interface IndexCondition {\n\tvalue: FilterScalar | undefined\n\tindex: string\n}\nexport interface CompiledFilters {\n\tresidual: FilterAst\n\tbindings: ReadonlyMap<string, FilterScalar>\n\tunsatisfiable: boolean\n\tindex: QueryIndex | null\n\tprefix: readonly { field: string; value: FilterScalar }[]\n}\ntype FilterSpec<Schema extends z.ZodType> =\n\tz.input<Schema> extends { filters?: infer Spec } ? Spec : never\nexport type ServerFilter<Fields extends FieldSchemas> = ScalarFilters<\n\tFields,\n\tScalarSelection<Fields>\n>\n\ninterface BindingResult {\n\tvalues: Map<string, FilterScalar>\n\timpossible: boolean\n}\n\n/** Validate first; where is independently validated and ANDed with client predicates.\n * OR remains a residual expression over one traversal, never multiple streams.\n */\nexport function compileFilters<Schema extends z.ZodType>(\n\tschema: Schema,\n\tinput: z.input<Schema>,\n\tindexes: readonly QueryIndex[],\n\tconditions: readonly IndexCondition[] = [],\n\twhere?: FilterSpec<Schema>,\n): CompiledFilters {\n\t// SAFETY: pagination schemas validate filters before this internal AST adapter reads them.\n\tconst parsed = schema.parse(input) as { filters?: FilterTree }\n\t// SAFETY: server filters use the same validation boundary as client filters.\n\tconst server =\n\t\twhere === undefined\n\t\t\t? undefined\n\t\t\t: (schema.parse({ ...parsed, filters: where }) as { filters?: FilterTree })\n\tconst grammar = paginationGrammar(schema)\n\tconst residual: FilterAst = {\n\t\tkind: 'and',\n\t\tchildren: [\n\t\t\tnormalizeFilters(parsed.filters, grammar),\n\t\t\tnormalizeFilters(server?.filters, grammar),\n\t\t],\n\t}\n\tconst guaranteed = guaranteedBindings(residual)\n\tconst sorted = [...indexes].sort(\n\t\t(a, b) =>\n\t\t\ta.name.localeCompare(b.name) || a.fields.join('\\0').localeCompare(b.fields.join('\\0')),\n\t)\n\tconst condition = conditions.find((entry) => entry.value !== null && entry.value !== undefined)\n\tlet index: QueryIndex | null = null\n\tif (condition) {\n\t\tindex = sorted.find((entry) => entry.name === condition.index) ?? null\n\t\tif (!index || prefixBindings(index, guaranteed.values).length === 0)\n\t\t\tthrow new Error(`Explicit index \"${condition.index}\" is incompatible with guaranteed filters`)\n\t} else {\n\t\tlet length = 0\n\t\tfor (const candidate of sorted) {\n\t\t\tconst usable = prefixBindings(candidate, guaranteed.values).length\n\t\t\tif (usable > length) {\n\t\t\t\tindex = candidate\n\t\t\t\tlength = usable\n\t\t\t}\n\t\t}\n\t}\n\treturn {\n\t\tresidual,\n\t\tbindings: guaranteed.values,\n\t\tunsatisfiable: guaranteed.impossible,\n\t\tindex,\n\t\tprefix: index ? prefixBindings(index, guaranteed.values) : [],\n\t}\n}\nfunction paginationGrammar(schema: z.ZodType): FilterGrammar | undefined {\n\tif (!(schema instanceof z.ZodObject)) return undefined\n\tconst filters = schema.shape.filters\n\tif (!(filters instanceof z.ZodOptional)) return undefined\n\tconst root = filters.unwrap()\n\tconst cached = filterGrammars.get(root)\n\tif (cached) return cached\n\tif (!(root instanceof z.ZodObject)) return undefined\n\treturn {\n\t\tfields: root.shape,\n\t\tvariants: variantPredicates.get(root.shape.variant),\n\t\trelations: Object.fromEntries(\n\t\t\tObject.entries(root.shape).flatMap(([name, predicate]) => {\n\t\t\t\t// SAFETY: object shapes contain Zod schemas; metadata only recognizes predicates created by our grammar.\n\t\t\t\tconst relation = relationPredicates.get(predicate as z.core.$ZodType)\n\t\t\t\treturn relation ? [[name, relation]] : []\n\t\t\t}),\n\t\t),\n\t}\n}\nfunction normalizeFilters(value: FilterTree, grammar?: FilterGrammar): FilterAst {\n\tconst children: FilterAst[] = []\n\tif (!value) return { kind: 'and', children }\n\t// SAFETY: the caller parsed a strict filter object; its grammar has no array/primitive expression.\n\tconst expression = value as Record<string, FilterTree>\n\tfor (const [name, predicate] of Object.entries(expression)) {\n\t\tif (predicate === undefined) continue\n\t\tif (name === 'and' || name === 'or') {\n\t\t\t// SAFETY: boolean composition is validated as an array of expressions.\n\t\t\tchildren.push({\n\t\t\t\tkind: name,\n\t\t\t\tchildren: (predicate as FilterTree[]).map((child) => normalizeFilters(child, grammar)),\n\t\t\t})\n\t\t} else if (name === 'variant' && grammar?.variants) {\n\t\t\t// SAFETY: strict branch grammar validates the literal tag and its selected target filters.\n\t\t\tconst scoped = predicate as { tag: string; filters: FilterTree }\n\t\t\tchildren.push({\n\t\t\t\tkind: 'variant',\n\t\t\t\tdiscriminator: grammar.variants.discriminator,\n\t\t\t\ttag: scoped.tag,\n\t\t\t\tchild: normalizeFilters(scoped.filters, grammar.variants.options[scoped.tag]),\n\t\t\t})\n\t\t} else if (name === 'not')\n\t\t\tchildren.push({ kind: 'not', child: normalizeFilters(predicate, grammar) })\n\t\telse if (name === 'fields') {\n\t\t\t// SAFETY: fields is a strict selected scalar namespace.\n\t\t\tfor (const [field, scalar] of Object.entries(predicate as Record<string, FilterTree>))\n\t\t\t\tchildren.push(...normalizeScalar(field, scalar))\n\t\t} else if (grammar?.relations[name]) {\n\t\t\tconst relation = grammar.relations[name]\n\t\t\tif (relation.cardinality === 'one')\n\t\t\t\tchildren.push({\n\t\t\t\t\tkind: 'relation',\n\t\t\t\t\tname,\n\t\t\t\t\tmode: 'one',\n\t\t\t\t\tpredicate: predicate === null ? null : normalizeFilters(predicate, relation.target),\n\t\t\t\t})\n\t\t\telse {\n\t\t\t\t// SAFETY: many predicates validate as exactly one some/none target expression.\n\t\t\t\tconst target = predicate as { some?: FilterTree; none?: FilterTree }\n\t\t\t\tchildren.push({\n\t\t\t\t\tkind: 'relation',\n\t\t\t\t\tname,\n\t\t\t\t\tmode: target.some === undefined ? 'none' : 'some',\n\t\t\t\t\tpredicate: normalizeFilters(target.some ?? target.none, relation.target),\n\t\t\t\t})\n\t\t\t}\n\t\t} else children.push(...normalizeScalar(name, predicate))\n\t}\n\treturn { kind: 'and', children }\n}\nfunction normalizeScalar(field: string, predicate: FilterTree): FilterAst[] {\n\tif (predicate === undefined) return []\n\tif (Object(predicate) !== predicate) {\n\t\t// SAFETY: native scalar grammar excludes object/array values and preserves branded ID strings.\n\t\treturn [{ kind: 'scalar', field, operator: 'eq', value: predicate as FilterScalar }]\n\t}\n\t// SAFETY: only selected supported scalar operator objects survive validation.\n\treturn Object.entries(predicate as Record<FilterOperator, FilterScalar | FilterScalar[]>)\n\t\t.filter(([, value]) => value !== undefined)\n\t\t.map(([operator, value]) => ({\n\t\t\tkind: 'scalar',\n\t\t\tfield,\n\t\t\toperator: operator as FilterOperator,\n\t\t\tvalue,\n\t\t}))\n}\nfunction valueEqual(left: FilterScalar, right: FilterScalar): boolean {\n\treturn compareValues(left, right) === 0\n}\nfunction guaranteedBindings(ast: FilterAst): BindingResult {\n\tconst values = new Map<string, FilterScalar>()\n\tif (ast.kind === 'variant')\n\t\treturn guaranteedBindings({\n\t\t\tkind: 'and',\n\t\t\tchildren: [\n\t\t\t\t{ kind: 'scalar', field: ast.discriminator, operator: 'eq', value: ast.tag },\n\t\t\t\tast.child,\n\t\t\t],\n\t\t})\n\tif (ast.kind === 'scalar') {\n\t\tif (ast.operator === 'eq' && !Array.isArray(ast.value)) values.set(ast.field, ast.value)\n\t\tconst choices = Array.isArray(ast.value) ? ast.value : undefined\n\t\tif (ast.operator === 'in' && choices && choices.every((value) => valueEqual(value, choices[0])))\n\t\t\tvalues.set(ast.field, choices[0])\n\t\treturn { values, impossible: false }\n\t}\n\tif (ast.kind === 'not' || ast.kind === 'relation') return { values, impossible: false }\n\tconst branches = ast.children.map(guaranteedBindings)\n\tif (ast.kind === 'or') {\n\t\tconst viable = branches.filter((branch) => !branch.impossible)\n\t\tfor (const [field, value] of viable[0]?.values ?? [])\n\t\t\tif (\n\t\t\t\tviable.every(\n\t\t\t\t\t(branch) => branch.values.has(field) && valueEqual(branch.values.get(field)!, value),\n\t\t\t\t)\n\t\t\t)\n\t\t\t\tvalues.set(field, value)\n\t\treturn { values, impossible: viable.every((branch) => branch.impossible) }\n\t}\n\tlet impossible = branches.some((branch) => branch.impossible)\n\tfor (const branch of branches)\n\t\tfor (const [field, value] of branch.values) {\n\t\t\tif (values.has(field) && !valueEqual(values.get(field)!, value)) impossible = true\n\t\t\tvalues.set(field, value)\n\t\t}\n\treturn { values, impossible }\n}\nfunction prefixBindings(\n\tindex: QueryIndex,\n\tbindings: ReadonlyMap<string, FilterScalar>,\n): { field: string; value: FilterScalar }[] {\n\tconst prefix: { field: string; value: FilterScalar }[] = []\n\tfor (const field of index.fields) {\n\t\tif (!bindings.has(field)) break\n\t\tprefix.push({ field, value: bindings.get(field)! })\n\t}\n\treturn prefix\n}\n\nfunction assertSupportedScalar(validator: GenericValidator): void {\n\tswitch (validator.kind) {\n\t\tcase 'id':\n\t\tcase 'string':\n\t\tcase 'float64':\n\t\tcase 'int64':\n\t\tcase 'boolean':\n\t\tcase 'null':\n\t\tcase 'literal':\n\t\t\treturn\n\t\tcase 'union':\n\t\t\tfor (const member of validator.members) {\n\t\t\t\tassertSupportedScalar(member)\n\t\t\t}\n\t\t\treturn\n\t\tdefault:\n\t\t\tthrow new Error(`Unsupported scalar filter kind \"${validator.kind}\"`)\n\t}\n}\n\nfunction supportsRange(validator: GenericValidator): boolean {\n\tswitch (validator.kind) {\n\t\tcase 'string':\n\t\tcase 'float64':\n\t\tcase 'int64':\n\t\t\treturn true\n\t\tcase 'literal':\n\t\t\treturn z.union([z.string(), z.number(), z.bigint()]).safeParse(validator.value).success\n\t\tcase 'union':\n\t\t\treturn validator.members.every(supportsRange)\n\t\tdefault:\n\t\t\treturn false\n\t}\n}\n\nfunction boundedFilterSchema(predicates: FieldSchemas): z.ZodType {\n\treturn relationFilterSchema({ fields: predicates, relations: {} })\n}\n\nfunction filterNodeSchema(grammar: FilterGrammar, depth: number): z.ZodObject {\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)\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) }),\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\tif (depth === MAX_FILTER_DEPTH) return node\n\tconst child = filterNodeSchema(grammar, depth + 1)\n\treturn node.extend({\n\t\tand: z.array(child).min(1).max(MAX_FILTER_NODES).optional(),\n\t\tor: z.array(child).min(1).max(MAX_FILTER_NODES).optional(),\n\t\tnot: child.optional(),\n\t})\n}\n\nfunction withinFilterBudget(value: FilterTree, grammar: FilterGrammar): boolean {\n\ttype Context =\n\t\t| 'expression'\n\t\t| 'fields'\n\t\t| 'predicate'\n\t\t| 'composition'\n\t\t| 'many'\n\t\t| 'variant'\n\t\t| 'value'\n\tinterface Entry {\n\t\tvalue: FilterTree\n\t\tdepth: number\n\t\tcontext: Context\n\t\tgrammar: FilterGrammar\n\t}\n\tconst pending: Entry[] = [{ value, depth: 0, context: 'expression', grammar }]\n\tlet nodes = 0\n\twhile (pending.length) {\n\t\tconst entry = pending.pop()!\n\t\tif (++nodes > MAX_FILTER_NODES || entry.depth > MAX_FILTER_DEPTH) return false\n\t\tif (Object(entry.value) !== entry.value) continue\n\t\t// SAFETY: boxing excludes null and primitives; descriptors below avoid invoking getters.\n\t\tconst object = entry.value as FilterTree[] | Record<string, FilterTree>\n\t\tconst array = Array.isArray(object) ? object : undefined\n\t\tconst prototype = Object.getPrototypeOf(object)\n\t\tif (\n\t\t\tarray ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null\n\t\t)\n\t\t\treturn false\n\t\tif (array && array.length + nodes + pending.length > MAX_FILTER_NODES) return false\n\t\tconst keys = Reflect.ownKeys(object)\n\t\tif (\n\t\t\tkeys.length - (array ? 1 : 0) + nodes + pending.length > MAX_FILTER_NODES ||\n\t\t\t(array && keys.length !== array.length + 1)\n\t\t)\n\t\t\treturn false\n\t\tlet consumed: string[]\n\t\tswitch (entry.context) {\n\t\t\tcase 'expression':\n\t\t\t\tconsumed = [\n\t\t\t\t\t...Object.keys(entry.grammar.fields),\n\t\t\t\t\t...Object.keys(entry.grammar.relations),\n\t\t\t\t\t'and',\n\t\t\t\t\t'or',\n\t\t\t\t\t'not',\n\t\t\t\t\t'fields',\n\t\t\t\t\t...(entry.grammar.variants ? ['variant'] : []),\n\t\t\t\t]\n\t\t\t\tbreak\n\t\t\tcase 'variant':\n\t\t\t\tconsumed = ['tag', 'filters']\n\t\t\t\tbreak\n\t\t\tcase 'fields':\n\t\t\t\tconsumed = Object.keys(entry.grammar.fields)\n\t\t\t\tbreak\n\t\t\tcase 'predicate':\n\t\t\t\tconsumed = ['eq', 'in', 'gt', 'gte', 'lt', 'lte', 'exists']\n\t\t\t\tbreak\n\t\t\tcase 'many':\n\t\t\t\tconsumed = ['some', 'none']\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tconsumed = []\n\t\t}\n\t\tfor (const key of consumed) if (!Object.hasOwn(object, key) && key in object) return false\n\t\tfor (const key of keys) {\n\t\t\tif (array && key === 'length') continue\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(object, key)!\n\t\t\tconst name = String(key)\n\t\t\tif (!descriptor.enumerable || !('value' in descriptor) || name !== key) return false\n\t\t\tif (array && (!/^(0|[1-9]\\d*)$/.test(name) || Number(name) >= array.length)) return false\n\t\t\tlet context: Context = 'value'\n\t\t\tlet depth = entry.depth\n\t\t\tlet target = entry.grammar\n\t\t\tif (entry.context === 'expression') {\n\t\t\t\tcontext = 'predicate'\n\t\t\t\tif (name === 'variant' && entry.grammar.variants) {\n\t\t\t\t\tcontext = 'variant'\n\t\t\t\t\tdepth++\n\t\t\t\t} else if (name === 'fields') context = 'fields'\n\t\t\t\telse if (name === 'and' || name === 'or') {\n\t\t\t\t\tcontext = 'composition'\n\t\t\t\t\tdepth++\n\t\t\t\t} else if (name === 'not') {\n\t\t\t\t\tcontext = 'expression'\n\t\t\t\t\tdepth++\n\t\t\t\t} else if (entry.grammar.relations[name]) {\n\t\t\t\t\tconst relation = entry.grammar.relations[name]\n\t\t\t\t\tcontext = relation.cardinality === 'one' ? 'expression' : 'many'\n\t\t\t\t\ttarget = relation.target\n\t\t\t\t\tdepth++\n\t\t\t\t}\n\t\t\t} else if (entry.context === 'variant' && name === 'filters') {\n\t\t\t\tconst tag = Object.getOwnPropertyDescriptor(object, 'tag')\n\t\t\t\tif (\n\t\t\t\t\t!tag ||\n\t\t\t\t\t!('value' in tag) ||\n\t\t\t\t\t!z.string().safeParse(tag.value).success ||\n\t\t\t\t\t!Object.hasOwn(entry.grammar.variants?.options ?? {}, tag.value)\n\t\t\t\t)\n\t\t\t\t\treturn false\n\t\t\t\tcontext = 'expression'\n\t\t\t\ttarget = entry.grammar.variants!.options[String(tag.value)]\n\t\t\t} else if (entry.context === 'fields') context = 'predicate'\n\t\t\telse if (entry.context === 'composition' || entry.context === 'many') context = 'expression'\n\t\t\tpending.push({ value: descriptor.value, depth, context, grammar: target })\n\t\t}\n\t}\n\treturn true\n}\n"],"mappings":"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"}
@@ -18,6 +18,7 @@ export declare function createPlanner(schema: SchemaDefinition<GenericSchema, bo
18
18
  SplitRequired: "SplitRequired";
19
19
  }>>>;
20
20
  }, z.core.$strip>;
21
+ validateDocument: (table: string, row: Record<string, import('convex/values').Value>) => void;
21
22
  assertUnchanged: () => void;
22
23
  beforeLeaf(table: string): void;
23
24
  complete(table: string, document: Record<string, Value>, permitted: boolean, residual: FilterAst): Record<string, unknown> | null;
@@ -1,2 +1,2 @@
1
- import{compileFilters as e}from"./filters.mjs";import{getRelationExecutor as t,getRelationMetadata as n}from"../relations.mjs";import{compareValues as r}from"convex/values";function i(r,i){let o=t(i),s=()=>o.assertSchema(r.tables);s();let c=n(i),l=new Map(Object.keys(c.tables).map(e=>[e,o.pagination(e)]));return{metadata:c,page:o.page,assertUnchanged:s,beforeLeaf(e){o.assertNativeTable(e,r.tables[e])},complete(e,t,n,i){return o.project(r.tables,e,t,()=>n&&a(i,t))},plan(t,n,r,i){s();let a=l.get(t);if(!a)throw Error(`Unknown query table`);let o=a.parse(n),u=i.length===1?i[0]:i.length?{and:i}:void 0,d=e(a,o,c.tables[t].indexes,r,u),f=e(a,o,[]).residual,p=e(a,{...o,filters:u},[]).residual;return{...d,args:o,clientIdentity:f,serverIdentity:p}}}}function a(e,t){switch(e.kind){case`and`:return e.children.every(e=>a(e,t));case`or`:return e.children.some(e=>a(e,t));case`not`:return!a(e.child,t);case`relation`:throw Error(`Relation execution requires U6`);case`scalar`:{let n=t[e.field];if(e.operator===`exists`)return n!==void 0===e.value;if(e.operator===`in`)return Array.isArray(e.value)&&e.value.some(e=>r(n,e)===0);if(Array.isArray(e.value))throw Error(`Invalid scalar predicate`);let i=r(n,e.value);switch(e.operator){case`eq`:return i===0;case`gt`:return i>0;case`gte`:return i>=0;case`lt`:return i<0;case`lte`:return i<=0}}}}export{i as createPlanner,a as scalarMatches};
1
+ import{compileFilters as e}from"./filters.mjs";import{getRelationExecutor as t,getRelationMetadata as n}from"../relations.mjs";import{compareValues as r}from"convex/values";function i(r,i){let o=t(i),s=()=>o.assertSchema(r.tables);s();let c=n(i),l=new Map(Object.keys(c.tables).map(e=>[e,o.pagination(e)]));return{metadata:c,page:o.page,validateDocument:o.validateDocument,assertUnchanged:s,beforeLeaf(e){o.assertNativeTable(e,r.tables[e])},complete(e,t,n,i){return o.project(r.tables,e,t,()=>n&&a(i,t))},plan(t,n,r,i){s();let a=l.get(t);if(!a)throw Error(`Unknown query table`);let o=a.parse(n),u=i.length===1?i[0]:i.length?{and:i}:void 0,d=e(a,o,c.tables[t].indexes,r,u),f=e(a,o,[]).residual,p=e(a,{...o,filters:u},[]).residual;return{...d,args:o,clientIdentity:f,serverIdentity:p}}}}function a(e,t){switch(e.kind){case`and`:return e.children.every(e=>a(e,t));case`or`:return e.children.some(e=>a(e,t));case`variant`:return t[e.discriminator]===e.tag&&a(e.child,t);case`not`:return!a(e.child,t);case`relation`:throw Error(`Relation execution requires U6`);case`scalar`:{let n=t[e.field];if(e.operator===`exists`)return n!==void 0===e.value;if(e.operator===`in`)return Array.isArray(e.value)&&e.value.some(e=>r(n,e)===0);if(Array.isArray(e.value))throw Error(`Invalid scalar predicate`);let i=r(n,e.value);switch(e.operator){case`eq`:return i===0;case`gt`:return i>0;case`gte`:return i>=0;case`lt`:return i<0;case`lte`:return i<=0}}}}export{i as createPlanner,a as scalarMatches};
2
2
  //# sourceMappingURL=planner.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"planner.mjs","names":["createPlanner","owned","getRelationExecutor","relations","assertUnchanged","schema","metadata","getRelationMetadata","inputs","table","document","permitted","scalarMatches","residual","validator","args","input","where","constraints","compiled","compileFilters","conditions","client","server","ast","child","actual","compareValues","value","compared"],"sources":["../../src/querying/planner.ts"],"sourcesContent":["import type { z } from 'zod'\nimport type { SchemaDefinition, GenericSchema } from 'convex/server'\nimport { compareValues, type Value } from 'convex/values'\nimport {\n\tgetRelationExecutor,\n\tgetRelationMetadata,\n\ttype RelationRegistryIdentity,\n} from '../relations'\nimport { compileFilters, type FilterAst, type IndexCondition } from './filters'\n\nexport type QueryInput = z.input<ReturnType<ReturnType<typeof getRelationExecutor>['pagination']>>\nexport type StoredConstraint = NonNullable<QueryInput['filters']>\n\nexport function createPlanner(\n\tschema: SchemaDefinition<GenericSchema, boolean>,\n\trelations: RelationRegistryIdentity,\n) {\n\tconst owned = getRelationExecutor(relations)\n\tconst assertUnchanged = () => owned.assertSchema(schema.tables)\n\tassertUnchanged()\n\tconst metadata = getRelationMetadata(relations)\n\tconst inputs = new Map(\n\t\tObject.keys(metadata.tables).map((table) => [table, owned.pagination(table)]),\n\t)\n\treturn {\n\t\tmetadata,\n\t\tpage: owned.page,\n\t\tassertUnchanged,\n\t\tbeforeLeaf(table: string) {\n\t\t\towned.assertNativeTable(table, schema.tables[table])\n\t\t},\n\t\tcomplete(\n\t\t\ttable: string,\n\t\t\tdocument: Record<string, Value>,\n\t\t\tpermitted: boolean,\n\t\t\tresidual: FilterAst,\n\t\t) {\n\t\t\treturn owned.project(\n\t\t\t\tschema.tables,\n\t\t\t\ttable,\n\t\t\t\tdocument,\n\t\t\t\t() => permitted && scalarMatches(residual, document),\n\t\t\t)\n\t\t},\n\t\tplan(\n\t\t\ttable: string,\n\t\t\tinput: QueryInput,\n\t\t\tconditions: readonly IndexCondition[],\n\t\t\tconstraints: readonly StoredConstraint[],\n\t\t) {\n\t\t\tassertUnchanged()\n\t\t\tconst validator = inputs.get(table)\n\t\t\tif (!validator) throw new Error('Unknown query table')\n\t\t\tconst args = validator.parse(input)\n\t\t\t// Server constraints are independently validated against stored fields,\n\t\t\t// not the endpoint's public input/filter mask. Client values cannot replace them.\n\t\t\tconst where =\n\t\t\t\tconstraints.length === 1\n\t\t\t\t\t? constraints[0]\n\t\t\t\t\t: constraints.length\n\t\t\t\t\t\t? { and: constraints }\n\t\t\t\t\t\t: undefined\n\t\t\tconst compiled = compileFilters(\n\t\t\t\tvalidator,\n\t\t\t\targs,\n\t\t\t\tmetadata.tables[table].indexes,\n\t\t\t\tconditions,\n\t\t\t\twhere,\n\t\t\t)\n\t\t\tconst client = compileFilters(validator, args, []).residual\n\t\t\tconst server = compileFilters(validator, { ...args, filters: where }, []).residual\n\t\t\t// SAFETY: normalized AST nodes contain only validated native scalar Values.\n\t\t\treturn { ...compiled, args, clientIdentity: client as Value, serverIdentity: server as Value }\n\t\t},\n\t}\n}\n\n/** Native scalar ordering/equality, including missing vs null, without coercion. */\nexport function scalarMatches(\n\tast: FilterAst,\n\tdocument: Record<string, Value | undefined>,\n): boolean {\n\tswitch (ast.kind) {\n\t\tcase 'and':\n\t\t\treturn ast.children.every((child) => scalarMatches(child, document))\n\t\tcase 'or':\n\t\t\treturn ast.children.some((child) => scalarMatches(child, document))\n\t\tcase 'not':\n\t\t\treturn !scalarMatches(ast.child, document)\n\t\tcase 'relation':\n\t\t\tthrow new Error('Relation execution requires U6')\n\t\tcase 'scalar': {\n\t\t\tconst actual = document[ast.field]\n\t\t\tif (ast.operator === 'exists') return (actual !== undefined) === ast.value\n\t\t\tif (ast.operator === 'in')\n\t\t\t\treturn (\n\t\t\t\t\tArray.isArray(ast.value) && ast.value.some((value) => compareValues(actual, value) === 0)\n\t\t\t\t)\n\t\t\tif (Array.isArray(ast.value)) throw new Error('Invalid scalar predicate')\n\t\t\tconst compared = compareValues(actual, ast.value)\n\t\t\tswitch (ast.operator) {\n\t\t\t\tcase 'eq':\n\t\t\t\t\treturn compared === 0\n\t\t\t\tcase 'gt':\n\t\t\t\t\treturn compared > 0\n\t\t\t\tcase 'gte':\n\t\t\t\t\treturn compared >= 0\n\t\t\t\tcase 'lt':\n\t\t\t\t\treturn compared < 0\n\t\t\t\tcase 'lte':\n\t\t\t\t\treturn compared <= 0\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":"6KAaA,SAAgBA,EACf,EACA,EACC,CACD,IAAMC,EAAQC,EAAoBC,CAAS,EACrCC,MAAwBH,EAAM,aAAaI,EAAO,MAAM,EAC9DD,EAAgB,EAChB,IAAME,EAAWC,EAAoBJ,CAAS,EACxCK,EAAS,IAAI,IAClB,OAAO,KAAKF,EAAS,MAAM,CAAC,CAAC,IAAK,GAAU,CAACG,EAAOR,EAAM,WAAWQ,CAAK,CAAC,CAAC,CAC7E,EACA,MAAO,CACN,WACA,KAAMR,EAAM,KACZ,kBACA,WAAW,EAAe,CACzBA,EAAM,kBAAkBQ,EAAOJ,EAAO,OAAOI,EAAM,CACpD,EACA,SACC,EACA,EACA,EACA,EACC,CACD,OAAOR,EAAM,QACZI,EAAO,OACPI,EACAC,MACMC,GAAaC,EAAcC,EAAUH,CAAQ,CACpD,CACD,EACA,KACC,EACA,EACA,EACA,EACC,CACDN,EAAgB,EAChB,IAAMU,EAAYN,EAAO,IAAIC,CAAK,EAClC,GAAI,CAACK,EAAW,MAAU,MAAM,qBAAqB,EACrD,IAAMC,EAAOD,EAAU,MAAME,CAAK,EAG5BC,EACLC,EAAY,SAAW,EACpBA,EAAY,GACZA,EAAY,OACX,CAAE,IAAKA,CAAY,EACnB,IAAA,GACCC,EAAWC,EAChBN,EACAC,EACAT,EAAS,OAAOG,EAAM,CAAC,QACvBY,EACAJ,CACD,EACMK,EAASF,EAAeN,EAAWC,EAAM,CAAC,CAAC,CAAC,CAAC,SAC7CQ,EAASH,EAAeN,EAAW,CAAE,GAAGC,EAAM,QAASE,CAAM,EAAG,CAAC,CAAC,CAAC,CAAC,SAE1E,MAAO,CAAE,GAAGE,EAAU,OAAM,eAAgBG,EAAiB,eAAgBC,CAAgB,CAC9F,CACD,CACD,CAGA,SAAgBX,EACf,EACA,EACU,CACV,OAAQY,EAAI,KAAZ,CACC,IAAK,MACJ,OAAOA,EAAI,SAAS,MAAO,GAAUZ,EAAca,EAAOf,CAAQ,CAAC,EACpE,IAAK,KACJ,OAAOc,EAAI,SAAS,KAAM,GAAUZ,EAAca,EAAOf,CAAQ,CAAC,EACnE,IAAK,MACJ,MAAO,CAACE,EAAcY,EAAI,MAAOd,CAAQ,EAC1C,IAAK,WACJ,MAAU,MAAM,gCAAgC,EACjD,IAAK,SAAU,CACd,IAAMgB,EAAShB,EAASc,EAAI,OAC5B,GAAIA,EAAI,WAAa,SAAU,OAAQE,IAAW,IAAA,KAAeF,EAAI,MACrE,GAAIA,EAAI,WAAa,KACpB,OACC,MAAM,QAAQA,EAAI,KAAK,GAAKA,EAAI,MAAM,KAAM,GAAUG,EAAcD,EAAQE,CAAK,IAAM,CAAC,EAE1F,GAAI,MAAM,QAAQJ,EAAI,KAAK,EAAG,MAAU,MAAM,0BAA0B,EACxE,IAAMK,EAAWF,EAAcD,EAAQF,EAAI,KAAK,EAChD,OAAQA,EAAI,SAAZ,CACC,IAAK,KACJ,OAAOK,IAAa,EACrB,IAAK,KACJ,OAAOA,EAAW,EACnB,IAAK,MACJ,OAAOA,GAAY,EACpB,IAAK,KACJ,OAAOA,EAAW,EACnB,IAAK,MACJ,OAAOA,GAAY,CACrB,CACD,CACD,CACD"}
1
+ {"version":3,"file":"planner.mjs","names":["createPlanner","owned","getRelationExecutor","relations","assertUnchanged","schema","metadata","getRelationMetadata","inputs","table","document","permitted","scalarMatches","residual","validator","args","input","where","constraints","compiled","compileFilters","conditions","client","server","ast","child","actual","compareValues","value","compared"],"sources":["../../src/querying/planner.ts"],"sourcesContent":["import type { z } from 'zod'\nimport type { SchemaDefinition, GenericSchema } from 'convex/server'\nimport { compareValues, type Value } from 'convex/values'\nimport {\n\tgetRelationExecutor,\n\tgetRelationMetadata,\n\ttype RelationRegistryIdentity,\n} from '../relations'\nimport { compileFilters, type FilterAst, type IndexCondition } from './filters'\n\nexport type QueryInput = z.input<ReturnType<ReturnType<typeof getRelationExecutor>['pagination']>>\nexport type StoredConstraint = NonNullable<QueryInput['filters']>\n\nexport function createPlanner(\n\tschema: SchemaDefinition<GenericSchema, boolean>,\n\trelations: RelationRegistryIdentity,\n) {\n\tconst owned = getRelationExecutor(relations)\n\tconst assertUnchanged = () => owned.assertSchema(schema.tables)\n\tassertUnchanged()\n\tconst metadata = getRelationMetadata(relations)\n\tconst inputs = new Map(\n\t\tObject.keys(metadata.tables).map((table) => [table, owned.pagination(table)]),\n\t)\n\treturn {\n\t\tmetadata,\n\t\tpage: owned.page,\n\t\tvalidateDocument: owned.validateDocument,\n\t\tassertUnchanged,\n\t\tbeforeLeaf(table: string) {\n\t\t\towned.assertNativeTable(table, schema.tables[table])\n\t\t},\n\t\tcomplete(\n\t\t\ttable: string,\n\t\t\tdocument: Record<string, Value>,\n\t\t\tpermitted: boolean,\n\t\t\tresidual: FilterAst,\n\t\t) {\n\t\t\treturn owned.project(\n\t\t\t\tschema.tables,\n\t\t\t\ttable,\n\t\t\t\tdocument,\n\t\t\t\t() => permitted && scalarMatches(residual, document),\n\t\t\t)\n\t\t},\n\t\tplan(\n\t\t\ttable: string,\n\t\t\tinput: QueryInput,\n\t\t\tconditions: readonly IndexCondition[],\n\t\t\tconstraints: readonly StoredConstraint[],\n\t\t) {\n\t\t\tassertUnchanged()\n\t\t\tconst validator = inputs.get(table)\n\t\t\tif (!validator) throw new Error('Unknown query table')\n\t\t\tconst args = validator.parse(input)\n\t\t\t// Server constraints are independently validated against stored fields,\n\t\t\t// not the endpoint's public input/filter mask. Client values cannot replace them.\n\t\t\tconst where =\n\t\t\t\tconstraints.length === 1\n\t\t\t\t\t? constraints[0]\n\t\t\t\t\t: constraints.length\n\t\t\t\t\t\t? { and: constraints }\n\t\t\t\t\t\t: undefined\n\t\t\tconst compiled = compileFilters(\n\t\t\t\tvalidator,\n\t\t\t\targs,\n\t\t\t\tmetadata.tables[table].indexes,\n\t\t\t\tconditions,\n\t\t\t\twhere,\n\t\t\t)\n\t\t\tconst client = compileFilters(validator, args, []).residual\n\t\t\tconst server = compileFilters(validator, { ...args, filters: where }, []).residual\n\t\t\t// SAFETY: normalized AST nodes contain only validated native scalar Values.\n\t\t\treturn { ...compiled, args, clientIdentity: client as Value, serverIdentity: server as Value }\n\t\t},\n\t}\n}\n\n/** Native scalar ordering/equality, including missing vs null, without coercion. */\nexport function scalarMatches(\n\tast: FilterAst,\n\tdocument: Record<string, Value | undefined>,\n): boolean {\n\tswitch (ast.kind) {\n\t\tcase 'and':\n\t\t\treturn ast.children.every((child) => scalarMatches(child, document))\n\t\tcase 'or':\n\t\t\treturn ast.children.some((child) => scalarMatches(child, document))\n\t\tcase 'variant':\n\t\t\treturn document[ast.discriminator] === ast.tag && scalarMatches(ast.child, document)\n\t\tcase 'not':\n\t\t\treturn !scalarMatches(ast.child, document)\n\t\tcase 'relation':\n\t\t\tthrow new Error('Relation execution requires U6')\n\t\tcase 'scalar': {\n\t\t\tconst actual = document[ast.field]\n\t\t\tif (ast.operator === 'exists') return (actual !== undefined) === ast.value\n\t\t\tif (ast.operator === 'in')\n\t\t\t\treturn (\n\t\t\t\t\tArray.isArray(ast.value) && ast.value.some((value) => compareValues(actual, value) === 0)\n\t\t\t\t)\n\t\t\tif (Array.isArray(ast.value)) throw new Error('Invalid scalar predicate')\n\t\t\tconst compared = compareValues(actual, ast.value)\n\t\t\tswitch (ast.operator) {\n\t\t\t\tcase 'eq':\n\t\t\t\t\treturn compared === 0\n\t\t\t\tcase 'gt':\n\t\t\t\t\treturn compared > 0\n\t\t\t\tcase 'gte':\n\t\t\t\t\treturn compared >= 0\n\t\t\t\tcase 'lt':\n\t\t\t\t\treturn compared < 0\n\t\t\t\tcase 'lte':\n\t\t\t\t\treturn compared <= 0\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":"6KAaA,SAAgBA,EACf,EACA,EACC,CACD,IAAMC,EAAQC,EAAoBC,CAAS,EACrCC,MAAwBH,EAAM,aAAaI,EAAO,MAAM,EAC9DD,EAAgB,EAChB,IAAME,EAAWC,EAAoBJ,CAAS,EACxCK,EAAS,IAAI,IAClB,OAAO,KAAKF,EAAS,MAAM,CAAC,CAAC,IAAK,GAAU,CAACG,EAAOR,EAAM,WAAWQ,CAAK,CAAC,CAAC,CAC7E,EACA,MAAO,CACN,WACA,KAAMR,EAAM,KACZ,iBAAkBA,EAAM,iBACxB,kBACA,WAAW,EAAe,CACzBA,EAAM,kBAAkBQ,EAAOJ,EAAO,OAAOI,EAAM,CACpD,EACA,SACC,EACA,EACA,EACA,EACC,CACD,OAAOR,EAAM,QACZI,EAAO,OACPI,EACAC,MACMC,GAAaC,EAAcC,EAAUH,CAAQ,CACpD,CACD,EACA,KACC,EACA,EACA,EACA,EACC,CACDN,EAAgB,EAChB,IAAMU,EAAYN,EAAO,IAAIC,CAAK,EAClC,GAAI,CAACK,EAAW,MAAU,MAAM,qBAAqB,EACrD,IAAMC,EAAOD,EAAU,MAAME,CAAK,EAG5BC,EACLC,EAAY,SAAW,EACpBA,EAAY,GACZA,EAAY,OACX,CAAE,IAAKA,CAAY,EACnB,IAAA,GACCC,EAAWC,EAChBN,EACAC,EACAT,EAAS,OAAOG,EAAM,CAAC,QACvBY,EACAJ,CACD,EACMK,EAASF,EAAeN,EAAWC,EAAM,CAAC,CAAC,CAAC,CAAC,SAC7CQ,EAASH,EAAeN,EAAW,CAAE,GAAGC,EAAM,QAASE,CAAM,EAAG,CAAC,CAAC,CAAC,CAAC,SAE1E,MAAO,CAAE,GAAGE,EAAU,OAAM,eAAgBG,EAAiB,eAAgBC,CAAgB,CAC9F,CACD,CACD,CAGA,SAAgBX,EACf,EACA,EACU,CACV,OAAQY,EAAI,KAAZ,CACC,IAAK,MACJ,OAAOA,EAAI,SAAS,MAAO,GAAUZ,EAAca,EAAOf,CAAQ,CAAC,EACpE,IAAK,KACJ,OAAOc,EAAI,SAAS,KAAM,GAAUZ,EAAca,EAAOf,CAAQ,CAAC,EACnE,IAAK,UACJ,OAAOA,EAASc,EAAI,iBAAmBA,EAAI,KAAOZ,EAAcY,EAAI,MAAOd,CAAQ,EACpF,IAAK,MACJ,MAAO,CAACE,EAAcY,EAAI,MAAOd,CAAQ,EAC1C,IAAK,WACJ,MAAU,MAAM,gCAAgC,EACjD,IAAK,SAAU,CACd,IAAMgB,EAAShB,EAASc,EAAI,OAC5B,GAAIA,EAAI,WAAa,SAAU,OAAQE,IAAW,IAAA,KAAeF,EAAI,MACrE,GAAIA,EAAI,WAAa,KACpB,OACC,MAAM,QAAQA,EAAI,KAAK,GAAKA,EAAI,MAAM,KAAM,GAAUG,EAAcD,EAAQE,CAAK,IAAM,CAAC,EAE1F,GAAI,MAAM,QAAQJ,EAAI,KAAK,EAAG,MAAU,MAAM,0BAA0B,EACxE,IAAMK,EAAWF,EAAcD,EAAQF,EAAI,KAAK,EAChD,OAAQA,EAAI,SAAZ,CACC,IAAK,KACJ,OAAOK,IAAa,EACrB,IAAK,KACJ,OAAOA,EAAW,EACnB,IAAK,MACJ,OAAOA,GAAY,EACpB,IAAK,KACJ,OAAOA,EAAW,EACnB,IAAK,MACJ,OAAOA,GAAY,CACrB,CACD,CACD,CACD"}
@@ -9,7 +9,7 @@ import { GenericId, Value } from "convex/values";
9
9
  export interface OutputSelection {
10
10
  [name: string]: true | OutputSelection;
11
11
  }
12
- type Edge = RelationEdge<string, string, 'one' | 'many'>;
12
+ type Edge = RelationEdge<string, string, 'one' | 'many', any, any>;
13
13
  export type RelationDocument = Record<string, Value> & {
14
14
  _id: GenericId<string>;
15
15
  _creationTime: number;
@@ -1,2 +1,2 @@
1
- import{scalarMatches as e}from"./planner.mjs";import{CursorError as t}from"./cursors.mjs";import{executeStream as n,rethrowQueryError as r}from"./execution.mjs";import{z as i}from"zod";import{EmptyStream as a,getIndexFields as o,stream as s}from"convex-helpers/server/stream";var c=class extends Error{code;constructor(e){super(e),this.code=e,this.name=`RelationshipError`}};function l(e,t){for(let n of Object.values(e)){if(t>=4)throw new c(`INVALID_RELATION`);n!==!0&&l(n,t+1)}}function u(u,d,f,p,m){let h=f.metadata,g=i.object({cursor:i.string(),ancestors:i.array(i.string()).max(3)}).strict(),_=e=>`base`in e?e.base:e;function v(e,t){f.assertUnchanged();let n=h.declarations[e]?.[t];if(!n)throw new c(`INVALID_RELATION`);return n}function y(e,t){let n=t===`_id`?{name:`by_id`,fields:[`_id`]}:h.tables[e].indexes.find(e=>e.fields[0]===t);if(!n)throw new c(`INVALID_RELATION`);return n}function b(e,t,n,r){let i=t===`_id`?[{name:`by_id`,fields:[`_id`]}]:h.tables[e].indexes,a=e=>{let t=1;for(let r of e.fields.slice(1)){if(!n.bindings.has(r))break;t++}return t},o=r.find(e=>e.value!==null&&e.value!==void 0);if(o){let e=i.find(e=>e.name===o.index&&e.fields[0]===t);if(!e)throw Error(`Explicit index "${o.index}" is incompatible with declared relationship lookup`);return e}let s=i.filter(e=>e.fields[0]===t).sort((e,t)=>a(t)-a(e)||e.name.localeCompare(t.name)||e.fields.join(`\0`).localeCompare(t.fields.join(`\0`)));if(!s.length)throw new c(`INVALID_RELATION`);return s[0]}function x(e,t,n){let r=[{field:e.fields[0],value:t}];for(let t of e.fields.slice(1)){if(!n.bindings.has(t))break;r.push({field:t,value:n.bindings.get(t)})}return r}async function S(e,t){if(f.beforeLeaf(e),t==null||!d.db.normalizeId(e,t))return null;let n=await d.db.get(e,t);return f.assertUnchanged(),n}async function*C(e,t,n,r){if(d.expand(r,0),n==null)return;if(t===`_id`){let t=await S(e,n);t&&(d.expand(r),yield t);return}f.beforeLeaf(e);let i=y(e,t),a=d.leafQuery(e).withIndex(i.name,e=>e.eq(t,n))[Symbol.asyncIterator]();try{for(;;){f.assertUnchanged(),f.beforeLeaf(e);let t=await a.next();if(f.assertUnchanged(),t.done)return;let n=await d.authorize(e,t.value);f.assertUnchanged(),n&&(d.expand(r),yield t.value)}}finally{await a.return?.()}}async function*w(e,t,n){d.expand(n,0);let r=t[_(e.from).field];if(r!=null){if(`base`in e.from&&`base`in e.to)for await(let t of C(e.from.junction.table,e.from.junction.field,r,n))yield*C(e.target,e.to.base.field,t[e.to.junction.field],n);else yield*C(e.target,_(e.to).field,r,n)}}async function T(e,t,n){let r=null;for await(let i of w(e,t,n)){if(r)throw new c(`AMBIGUOUS_RELATION`);r=i}return r}async function E(t,n,r,i=0){if(d.expand(i,0),f.assertUnchanged(),r.kind===`scalar`)return e(r,n);if(r.kind===`not`)return!await E(t,n,r.child,i);if(r.kind===`and`||r.kind===`or`){for(let e of r.children){let a=await E(t,n,e,i);if(r.kind===`and`&&!a)return!1;if(r.kind===`or`&&a)return!0}return r.kind===`and`}if(r.kind!==`relation`)throw new c(`INVALID_RELATION`);let a=v(t,r.name);if(r.mode===`one`){let e=await T(a,n,i+1);return r.predicate===null?e===null:e!==null&&await E(a.target,e,r.predicate,i+1)}for await(let e of w(a,n,i+1))if(await E(a.target,e,r.predicate,i+1))return r.mode===`some`;return r.mode===`none`}async function D(e,t,n,r,i=[],a=0,o=[]){d.expand(a,0);let s=f.complete(e,t,!0,{kind:`and`,children:[]});for(let[c,l]of Object.entries(n)){let n=v(e,c),u=l===!0?{}:l;if(n.cardinality===`one`){let e=await T(n,t,a+1);s[c]=e?await D(n.target,e,u,r,[...i,c],a+1,[...o,t._id]):null}else s[c]=await k(e,t,c,u,{numItems:r.numItems,cursor:null,order:r.order},[],[],[...i,c],a+1,[...o,t._id])}return s}function O(e,n,r,i,a,o,s,u){f.page(v(e,n).target,r),l(r,s.length);let h=v(e,n);if(h.cardinality!==`many`)throw new c(`INVALID_RELATION`);let x=f.plan(h.target,i,[],o),S=h.from,C=h.to,w=`base`in S&&`base`in C?{table:S.junction.table,field:S.junction.field,targetField:C.base.field,targetReference:C.junction.field}:null,T=w?w.table:h.target,E=w?w.field:_(C).field,D=b(h.target,w?w.targetField:E,x,a),O=w?y(T,E):D,k={...m,table:T,index:O.name,order:x.args.order??`asc`,clientFilters:x.clientIdentity,serverFilters:{predicates:x.serverIdentity,selection:r,scope:{...d.scope},rootTable:m.table,lookupIndex:D.name},relationPath:s,parent:u};function A(e){try{return g.parse(JSON.parse(p.open(e,k)))}catch{throw new t}}return{declared:h,plan:x,through:w,sourceTable:T,field:E,index:O,targetIndex:D,binding:k,open:A}}async function k(e,t,r,i,c,l=[],m=[],h=[r],g=1,v=[],y=O(e,r,i,c,l,m,h,v[0]??t._id),b){d.expand(g,0);let{declared:C,plan:w,through:T,sourceTable:k,field:A,index:j,targetIndex:M,binding:N}=y,P=t?.[_(C.from).field],F={...w.args,...b??{cursor:null}},I={...w,index:j,prefix:P==null?[]:T?[{field:A,value:P}]:x(j,P,w),unsatisfiable:P==null||w.unsatisfiable},L=T&&T.targetField!==`_id`,R=await n(u,d,k,I,f,p,N,F,async(e,t)=>{if(!t)return null;d.expand(g);let n;return n=T&&!L?await S(C.target,e[T.targetReference]):e,!n||!await E(C.target,n,w.residual,g)?null:D(C.target,n,i,w.args,h,g,v)},e=>p.seal(JSON.stringify({cursor:e,ancestors:v.slice(1)}),N),L?(e,t)=>{let n=u,r=o(C.target,M.name,n);return e.flatMap(async e=>{d.expand(g);let i=await d.authorize(k,e);f.assertUnchanged();let o=e[T.targetReference];return!i||o==null?new a(w.args.order??`asc`,r):s(t,n).query(C.target).withIndex(M.name,e=>{let t=e,n=x(M,o,w);for(let e of n)t=t.eq(e.field,e.value);return t}).order(w.args.order??`asc`)},r)}:void 0,L?C.target:k);return f.assertUnchanged(),f.page(C.target,i).parse(R)}async function A(e,n,i,a,o,s,l){if(!i.length||i.length>4||!d.db.normalizeId(e,n))throw new c(`INVALID_RELATION`);let u=e,f=i.map(e=>{let t=v(u,e);return u=t.target,t}),p=f.at(-1),m=O(p.source,i.at(-1),a,o,s,l,i,n),h=o.cursor===null?null:m.open(o.cursor),g=o.endCursor==null?null:m.open(o.endCursor),_=h?.ancestors??g?.ancestors??[];if(_.length!==i.length-1||h&&g&&JSON.stringify(h.ancestors)!==JSON.stringify(g.ancestors)||_.some((e,t)=>!d.db.normalizeId(f[t].target,e)))throw new t;try{let t=await S(e,n);for(let e=0;t&&e<_.length;e++){let n=f[e],r=null;if(n.cardinality===`one`){let i=await T(n,t,e+1);i?._id===_[e]&&(r=i)}else for await(let i of w(n,t,e+1))if(i._id===_[e]){r=i;break}t=r}return await k(p.source,t,i.at(-1),a,o,s,l,i,i.length,[n,..._],m,{cursor:h?.cursor??null,endCursor:g?.cursor??o.endCursor})}catch(e){r(e)}}return{matches:E,output:D,continuation:A,edge:v}}export{c as RelationshipError,l as assertRelationshipDepth,u as relationshipExecutor};
1
+ import{scalarMatches as e}from"./planner.mjs";import{CursorError as t}from"./cursors.mjs";import{executeStream as n,rethrowQueryError as r}from"./execution.mjs";import{z as i}from"zod";import{EmptyStream as a,getIndexFields as o,stream as s}from"convex-helpers/server/stream";var c=class extends Error{code;constructor(e){super(e),this.code=e,this.name=`RelationshipError`}};function l(e,t){for(let n of Object.values(e)){if(t>=4)throw new c(`INVALID_RELATION`);n!==!0&&l(n,t+1)}}function u(u,d,f,p,m){let h=f.metadata,g=i.object({cursor:i.string(),ancestors:i.array(i.string()).max(3)}).strict(),_=e=>`base`in e?e.base:e;function v(e,t){f.assertUnchanged();let n=h.declarations[e]?.[t];if(!n)throw new c(`INVALID_RELATION`);return n}let y=(e,t)=>!t||e[t.discriminator]===t.tag;function b(e,t,n){let r=t===`_id`?{name:`by_id`,fields:[`_id`]}:h.tables[e].indexes.find(e=>{let r=e.fields.indexOf(t);return r>=0&&e.fields.slice(0,r).every(e=>e===n?.discriminator)});if(!r)throw new c(`INVALID_RELATION`);return r}function x(e,t,n,r,i){let a=t===`_id`?[{name:`by_id`,fields:[`_id`]}]:h.tables[e].indexes,o=e=>e===t||e===i?.discriminator||n.bindings.has(e),s=e=>e.fields.includes(t)&&e.fields.slice(0,e.fields.indexOf(t)).every(o),l=e=>{let t=0;for(let n of e.fields){if(!o(n))break;t++}return t},u=r.find(e=>e.value!==null&&e.value!==void 0);if(u){let e=a.find(e=>e.name===u.index&&s(e));if(!e)throw Error(`Explicit index "${u.index}" is incompatible with declared relationship lookup`);return e}let d=a.filter(s).sort((e,t)=>l(t)-l(e)||e.name.localeCompare(t.name)||e.fields.join(`\0`).localeCompare(t.fields.join(`\0`)));if(!d.length)throw new c(`INVALID_RELATION`);return d[0]}function S(e,t,n,r,i){let a=[];for(let o of e.fields)if(o===t)a.push({field:o,value:n});else if(o===i?.discriminator)a.push({field:o,value:i.tag});else if(r?.bindings.has(o))a.push({field:o,value:r.bindings.get(o)});else break;return a}async function C(e,t){if(f.beforeLeaf(e),t==null||!d.db.normalizeId(e,t))return null;let n=await d.db.get(e,t);return f.assertUnchanged(),n&&f.validateDocument(e,n),n}async function*w(e,t,n,r,i){if(d.expand(r,0),n==null)return;if(t===`_id`){let t=await C(e,n);t&&y(t,i)&&(d.expand(r),yield t);return}f.beforeLeaf(e);let a=b(e,t,i),o=d.leafQuery(e).withIndex(a.name,e=>{let r=e;for(let e of S(a,t,n,void 0,i))r=r.eq(e.field,e.value);return r})[Symbol.asyncIterator]();try{for(;;){f.assertUnchanged(),f.beforeLeaf(e);let t=await o.next();if(f.assertUnchanged(),t.done)return;let n=await d.authorize(e,t.value);f.assertUnchanged(),n&&(f.validateDocument(e,t.value),y(t.value,i)&&(d.expand(r),yield t.value))}}finally{await o.return?.()}}async function*T(e,t,n){if(d.expand(n,0),!y(t,e.sourceGuard))return;let r=t[_(e.from).field];if(r!=null){if(`base`in e.from&&`base`in e.to)for await(let t of w(e.from.junction.table,e.from.junction.field,r,n,e.junctionGuard))yield*w(e.target,e.to.base.field,t[e.to.junction.field],n,e.targetGuard);else yield*w(e.target,_(e.to).field,r,n,e.targetGuard)}}async function E(e,t,n){let r=null;for await(let i of T(e,t,n)){if(r)throw new c(`AMBIGUOUS_RELATION`);r=i}return r}async function D(t,n,r,i=0){if(d.expand(i,0),f.assertUnchanged(),f.validateDocument(t,n),r.kind===`variant`)return n[r.discriminator]===r.tag&&D(t,n,r.child,i);if(r.kind===`scalar`)return e(r,n);if(r.kind===`not`)return!await D(t,n,r.child,i);if(r.kind===`and`||r.kind===`or`){for(let e of r.children){let a=await D(t,n,e,i);if(r.kind===`and`&&!a)return!1;if(r.kind===`or`&&a)return!0}return r.kind===`and`}if(r.kind!==`relation`)throw new c(`INVALID_RELATION`);let a=v(t,r.name);if(r.mode===`one`){let e=await E(a,n,i+1);return r.predicate===null?e===null:e!==null&&await D(a.target,e,r.predicate,i+1)}for await(let e of T(a,n,i+1))if(await D(a.target,e,r.predicate,i+1))return r.mode===`some`;return r.mode===`none`}async function O(e,t,n,r,i=[],a=0,o=[]){d.expand(a,0);let s=f.complete(e,t,!0,{kind:`and`,children:[]});for(let[c,l]of Object.entries(n)){let n=v(e,c),u=l===!0?{}:l;if(n.cardinality===`one`){let e=await E(n,t,a+1);s[c]=e?await O(n.target,e,u,r,[...i,c],a+1,[...o,t._id]):null}else s[c]=await A(e,t,c,u,{numItems:r.numItems,cursor:null,order:r.order},[],[],[...i,c],a+1,[...o,t._id])}return s}function k(e,n,r,i,a,o,s,u){f.page(v(e,n).target,r),l(r,s.length);let h=v(e,n);if(h.cardinality!==`many`)throw new c(`INVALID_RELATION`);let y=h.targetGuard?{fields:{[h.targetGuard.discriminator]:h.targetGuard.tag}}:void 0,S=f.plan(h.target,i,[],y?[...o,y]:o),C=h.from,w=h.to,T=`base`in C&&`base`in w?{table:C.junction.table,field:C.junction.field,targetField:w.base.field,targetReference:w.junction.field}:null,E=T?T.table:h.target,D=T?T.field:_(w).field,O=x(h.target,T?T.targetField:D,S,a,h.targetGuard),k=T?b(E,D,h.junctionGuard):O,A={...m,table:E,index:k.name,order:S.args.order??`asc`,clientFilters:S.clientIdentity,serverFilters:{predicates:S.serverIdentity,selection:r,scope:{...d.scope},rootTable:m.table,lookupIndex:O.name,guards:{source:h.sourceGuard?{...h.sourceGuard}:null,target:h.targetGuard?{...h.targetGuard}:null,junction:h.junctionGuard?{...h.junctionGuard}:null}},relationPath:s,parent:u};function j(e){try{return g.parse(JSON.parse(p.open(e,A)))}catch{throw new t}}return{declared:h,plan:S,through:T,sourceTable:E,field:D,index:k,targetIndex:O,binding:A,open:j}}async function A(e,t,r,i,c,l=[],m=[],h=[r],g=1,v=[],b=k(e,r,i,c,l,m,h,v[0]??t._id),x){d.expand(g,0);let{declared:w,plan:T,through:E,sourceTable:A,field:j,index:M,targetIndex:N,binding:P}=b,F=t&&y(t,w.sourceGuard)?t[_(w.from).field]:void 0,I={...T.args,...x??{cursor:null}},L={...T,index:M,prefix:F==null?[]:E?S(M,j,F,void 0,w.junctionGuard):S(M,j,F,T,w.targetGuard),unsatisfiable:F==null||T.unsatisfiable},R=E&&E.targetField!==`_id`,z=await n(u,d,A,L,f,p,P,I,async(e,t)=>{if(t&&f.validateDocument(R?w.target:A,e),!t||E&&!R&&!y(e,w.junctionGuard))return null;d.expand(g);let n;return n=E&&!R?await C(w.target,e[E.targetReference]):e,!n||!y(n,w.targetGuard)||!await D(w.target,n,T.residual,g)?null:O(w.target,n,i,T.args,h,g,v)},e=>p.seal(JSON.stringify({cursor:e,ancestors:v.slice(1)}),P),R?(e,t)=>{let n=u,r=o(w.target,N.name,n);return e.flatMap(async e=>{d.expand(g);let i=await d.authorize(A,e);f.assertUnchanged(),i&&f.validateDocument(A,e);let o=e[E.targetReference];return!i||!y(e,w.junctionGuard)||o==null?new a(T.args.order??`asc`,r):s(t,n).query(w.target).withIndex(N.name,e=>{let t=e,n=S(N,E.targetField,o,T,w.targetGuard);for(let e of n)t=t.eq(e.field,e.value);return t}).order(T.args.order??`asc`)},r)}:void 0,R?w.target:A);return f.assertUnchanged(),f.page(w.target,i).parse(z)}async function j(e,n,i,a,o,s,l){if(!i.length||i.length>4||!d.db.normalizeId(e,n))throw new c(`INVALID_RELATION`);let u=e,f=i.map(e=>{let t=v(u,e);return u=t.target,t}),p=f.at(-1),m=k(p.source,i.at(-1),a,o,s,l,i,n),h=o.cursor===null?null:m.open(o.cursor),g=o.endCursor==null?null:m.open(o.endCursor),_=h?.ancestors??g?.ancestors??[];if(_.length!==i.length-1||h&&g&&JSON.stringify(h.ancestors)!==JSON.stringify(g.ancestors)||_.some((e,t)=>!d.db.normalizeId(f[t].target,e)))throw new t;try{let t=await C(e,n);for(let e=0;t&&e<_.length;e++){let n=f[e],r=null;if(n.cardinality===`one`){let i=await E(n,t,e+1);i?._id===_[e]&&(r=i)}else for await(let i of T(n,t,e+1))if(i._id===_[e]){r=i;break}t=r}return await A(p.source,t,i.at(-1),a,o,s,l,i,i.length,[n,..._],m,{cursor:h?.cursor??null,endCursor:g?.cursor??o.endCursor})}catch(e){r(e)}}return{matches:D,output:O,continuation:j,edge:v}}export{c as RelationshipError,l as assertRelationshipDepth,u as relationshipExecutor};
2
2
  //# sourceMappingURL=relationships.mjs.map