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.
- package/README.md +67 -40
- package/dist/querying/filters.d.mts +10 -1
- package/dist/querying/filters.mjs +1 -1
- package/dist/querying/filters.mjs.map +1 -1
- package/dist/querying/planner.d.mts +1 -0
- package/dist/querying/planner.mjs +1 -1
- package/dist/querying/planner.mjs.map +1 -1
- package/dist/querying/relationships.d.mts +1 -1
- package/dist/querying/relationships.mjs +1 -1
- package/dist/querying/relationships.mjs.map +1 -1
- package/dist/querying.d.mts +9 -8
- package/dist/querying.mjs.map +1 -1
- package/dist/relations-public.d.mts +2 -2
- package/dist/relations.d.mts +74 -25
- package/dist/relations.mjs +1 -1
- package/dist/relations.mjs.map +1 -1
- package/dist/zod-table.d.mts +59 -2
- package/dist/zod-table.mjs +1 -1
- package/dist/zod-table.mjs.map +1 -1
- package/package.json +12 -3
- package/AGENTS.md +0 -47
- package/CHANGELOG.md +0 -337
- package/ROADMAP.md +0 -126
- package/docs/agent-tools.md +0 -41
- package/docs/approvals.md +0 -201
- package/docs/architecture.md +0 -124
- package/docs/auth.md +0 -443
- package/docs/command-context.md +0 -150
- package/docs/commands-legacy.md +0 -513
- package/docs/commands.md +0 -173
- package/docs/conventions.md +0 -345
- package/docs/crud.md +0 -66
- package/docs/domain-contracts.md +0 -148
- package/docs/domain-operations.md +0 -615
- package/docs/effect.md +0 -403
- package/docs/llms.md +0 -324
- package/docs/maintainability.md +0 -124
- package/docs/migration.md +0 -351
- package/docs/oxlint.md +0 -260
- package/docs/querying.md +0 -329
- package/docs/tenancy.md +0 -176
- package/docs/triggers.md +0 -77
- package/docs/upgrading.md +0 -287
- package/docs/webhooks.md +0 -58
- package/docs/zod-table.md +0 -278
- package/scripts/convex-config-bridge.mjs +0 -44
- package/scripts/convex-smoke-env.mjs +0 -33
- package/scripts/extract-changelog.mjs +0 -29
- package/scripts/owned-process.mjs +0 -140
- package/scripts/owned-process.test.mjs +0 -107
- package/scripts/smoke-packed-components.mjs +0 -341
- package/scripts/smoke-packed-effect.mjs +0 -439
- package/scripts/smoke-packed-querying.mjs +0 -298
- package/scripts/test-packed-auth.mjs +0 -72
- package/scripts/test-packed-effect.mjs +0 -482
- package/scripts/test-packed-ids.mjs +0 -150
- package/scripts/test-packed-oxlint.mjs +0 -167
- 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
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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](
|
|
89
|
-
[DTO parameters](
|
|
90
|
-
[consumer rule reference](
|
|
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
|
|
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](
|
|
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
|
-
|
|
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](
|
|
328
|
-
and [authenticated builders](
|
|
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](
|
|
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`](
|
|
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](
|
|
394
|
-
and the [migration from the initial 0.1.4 plugin](
|
|
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`](
|
|
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](
|
|
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
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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](
|
|
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](
|
|
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']
|
|
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=
|
|
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}
|
|
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
|