kitcn 0.31.0 → 0.32.0
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/CHANGELOG.md +95 -0
- package/dist/aggregate/index.d.ts +11 -6
- package/dist/aggregate/index.js +2 -1
- package/dist/auth/generated/index.d.ts +1 -1
- package/dist/auth/index.d.ts +16 -16
- package/dist/auth/index.js +2 -2
- package/dist/{capabilities-DPB-JKlm.d.ts → capabilities-CD-Ij91k.d.ts} +62 -10
- package/dist/cli.mjs +1 -1
- package/dist/{generated-contract-disabled-CTUQ9nxL.d.ts → generated-contract-disabled-PdNGvNYP.d.ts} +40 -40
- package/dist/{index-utils-C1DyktHe.js → index-utils-DvK7P6Q1.js} +32 -9
- package/dist/{local-env-DGSkmXQk.mjs → local-env-CYYSNf_o.mjs} +86 -72
- package/dist/orm/aggregate-index/index.d.ts +1 -1
- package/dist/orm/aggregate-index/index.js +31 -34
- package/dist/orm/index.d.ts +2 -2
- package/dist/orm/index.js +327 -132
- package/dist/orm/migrations/index.d.ts +2 -2
- package/dist/{query-context-DdSg3fuk.js → query-context-D4z1CnDH.js} +12 -3
- package/dist/{runtime-BcvcfaP8.js → runtime-B-8HKSIE.js} +21 -40
- package/dist/schema-BFP_awgP.js +67 -0
- package/dist/{schema-hL2FWCE7.js → schema-Bh7AmJwY.js} +6 -22
- package/dist/watcher.mjs +1 -1
- package/dist/{where-clause-compiler-Dddhscyy.d.ts → where-clause-compiler-FDJTqLDC.d.ts} +100 -96
- package/package.json +1 -1
- package/skills/kitcn/references/features/orm.md +34 -4
- package/dist/id-BVdWER1U.js +0 -37
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,100 @@
|
|
|
1
1
|
# kitcn
|
|
2
2
|
|
|
3
|
+
## 0.32.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#425](https://github.com/udecode/kitcn/pull/425) [`495eb0b`](https://github.com/udecode/kitcn/commit/495eb0b2bd285484250ca69d75de135287531bbb) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Breaking changes
|
|
8
|
+
|
|
9
|
+
- Cursor pages for an index-union filter with no `orderBy` are now in the order of the index the read walks, grouped by the probed value, instead of creation order. Add `orderBy` to keep newest-first paging.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
// Before
|
|
13
|
+
const page = await db.query.users.withIndex("by_status").findMany({
|
|
14
|
+
where: { status: { in: ["active", "pending"] } },
|
|
15
|
+
cursor: null,
|
|
16
|
+
limit: 20,
|
|
17
|
+
maxScan: 500,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// After
|
|
21
|
+
const page = await db.query.users.withIndex("by_status").findMany({
|
|
22
|
+
where: { status: { in: ["active", "pending"] } },
|
|
23
|
+
orderBy: { createdAt: "desc" },
|
|
24
|
+
cursor: null,
|
|
25
|
+
limit: 20,
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Features
|
|
30
|
+
|
|
31
|
+
- Page `in`, `notIn`, `ne`, and same-field equality `OR` filters from one index range per value instead of scanning the table. Cursor pagination over these filters no longer needs `maxScan`, and `orderBy` sorts across the whole result rather than per value.
|
|
32
|
+
|
|
33
|
+
## Patches
|
|
34
|
+
|
|
35
|
+
- Fix `select()` composition and `endCursor` pagination reading a whole index instead of the compiled index ranges when the filter is an index union.
|
|
36
|
+
- Keep no-`orderBy` cursor direction consistent when `endCursor` routes an index union through the advanced stream path.
|
|
37
|
+
- Prevent `endCursor` narrowing from reopening disjoint equality ranges and duplicating merged-stream rows.
|
|
38
|
+
- Fix cursor pagination with a residual post-filter reading a whole index instead of the compiled index ranges when the filter is an index union.
|
|
39
|
+
- Fall back to a bounded scan when the probed index cannot supply the requested `orderBy` or the union is wider than 64 ranges.
|
|
40
|
+
|
|
41
|
+
### Patch Changes
|
|
42
|
+
|
|
43
|
+
- [#426](https://github.com/udecode/kitcn/pull/426) [`466623c`](https://github.com/udecode/kitcn/commit/466623c8b581c38a066fa078309d25cfab166ea7) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Patches
|
|
44
|
+
|
|
45
|
+
- Fix a multi-field `orderBy` reading the whole table even when a declared
|
|
46
|
+
compound index already produces that exact order. `orderBy: [asc(type),
|
|
47
|
+
asc(numLikes)]` with `limit: 5` against an index on `(type, numLikes)` now
|
|
48
|
+
reads 5 documents instead of every row, at any table size — previously the
|
|
49
|
+
read cost was the same whether you asked for 5 rows or 50. The same bound
|
|
50
|
+
applies to relations: `with: { posts: { orderBy: { numLikes: 'asc' },
|
|
51
|
+
limit: 2 } }` now reads 2 children per parent instead of all of them.
|
|
52
|
+
- Prefer an index that supplies more of the requested sort when several serve
|
|
53
|
+
the filter equally well, so `(orgId, createdAt, title)` is chosen over
|
|
54
|
+
`(orgId, createdAt)` for a sort on both `createdAt` and `title`. An index with
|
|
55
|
+
another unrequested key after `title` is not treated as an exact sort because
|
|
56
|
+
that key would change which tied rows survive `limit`.
|
|
57
|
+
- Stop warning that secondary `orderBy` fields are unstable across pages when
|
|
58
|
+
the index carries the whole sort. A Convex cursor is the index key, so those
|
|
59
|
+
pages are stable. The warning still fires — with corrected wording — when no
|
|
60
|
+
index serves the full sort and the extra fields really are dropped.
|
|
61
|
+
- Sorts that mix directions, skip an index key, or run over a column that can
|
|
62
|
+
be missing or null keep using the post-fetch sort, so row order and null
|
|
63
|
+
placement are unchanged.
|
|
64
|
+
- Use Convex value ordering for non-null post-fetch values, including UTF-8
|
|
65
|
+
strings, signed zero, and NaN, so an index-backed top-k and its post-fetch
|
|
66
|
+
fallback select the same rows.
|
|
67
|
+
- Preserve the existing implicit creation-time tie order when an
|
|
68
|
+
equality-pinned leading sort field points opposite to the moving fields.
|
|
69
|
+
Add `createdAt` in the moving direction to make that sort index-bounded.
|
|
70
|
+
|
|
71
|
+
## 0.31.1
|
|
72
|
+
|
|
73
|
+
### Patch Changes
|
|
74
|
+
|
|
75
|
+
- [#424](https://github.com/udecode/kitcn/pull/424) [`0e3a221`](https://github.com/udecode/kitcn/commit/0e3a2214e8e1037b323e603f19c2df89b0d95602) Thanks [@MikeyZhang75](https://github.com/MikeyZhang75)! - ## Patches
|
|
76
|
+
|
|
77
|
+
- Stop `aggregateBackfill` rewriting the stored state it is about to delete.
|
|
78
|
+
Clearing a `rankIndex` walked the btree once per member — a root-to-leaf
|
|
79
|
+
descent plus a patch on every node along the way — and then dropped the whole
|
|
80
|
+
tree regardless, so all of that work was thrown away. Clearing an
|
|
81
|
+
`aggregateIndex` did the same thing with buckets, decrementing each one down to
|
|
82
|
+
zero before deleting it. Both now delete their member rows outright and let the
|
|
83
|
+
existing tree and bucket sweeps reclaim the rest. Clearing 120 rank members
|
|
84
|
+
across three partitions went from 201 btree node writes to 15, one per node.
|
|
85
|
+
- Fix a `rankIndex` clear crashing with `Unexpected field 'deletionStack'` once a
|
|
86
|
+
partition holds more than a few hundred rows. The rank storage tables now
|
|
87
|
+
reuse the same definitions the btree writes to, so a large tree can persist its
|
|
88
|
+
traversal state and resume across mutations. Run
|
|
89
|
+
`npx convex dev` (or your usual codegen) to pick the schema up.
|
|
90
|
+
- Report real work from a clear chunk instead of a fixed guess, so
|
|
91
|
+
`aggregateBackfill --batch-size` now bounds a clear by the documents it
|
|
92
|
+
actually touches. Multi-partition rank indexes no longer schedule far more
|
|
93
|
+
chunks than the remaining work needs.
|
|
94
|
+
- Allow an app to declare `aggregateStorageTables` from `kitcn/aggregate`
|
|
95
|
+
alongside a table that also declares a `rankIndex`. `defineSchema` used to
|
|
96
|
+
reject that combination as a duplicate table name.
|
|
97
|
+
|
|
3
98
|
## 0.31.0
|
|
4
99
|
|
|
5
100
|
### Minor Changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Vn as ConvexTextBuilderInitial, Zt as ConvexTableWithColumns } from "../capabilities-
|
|
2
|
-
import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-
|
|
1
|
+
import { Vn as ConvexTextBuilderInitial, Zt as ConvexTableWithColumns } from "../capabilities-CD-Ij91k.js";
|
|
2
|
+
import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-FDJTqLDC.js";
|
|
3
3
|
import * as convex_values0 from "convex/values";
|
|
4
4
|
import { GenericId, Infer, Value } from "convex/values";
|
|
5
5
|
import { DocumentByName, GenericDataModel, GenericDatabaseReader, GenericDatabaseWriter, TableNamesInDataModel } from "convex/server";
|
|
@@ -136,6 +136,10 @@ declare const aggregateStorageTables: {
|
|
|
136
136
|
//#endregion
|
|
137
137
|
//#region src/aggregate-core/btree.d.ts
|
|
138
138
|
type Key$2 = Value;
|
|
139
|
+
type DeleteTreesResult = {
|
|
140
|
+
/** True once this aggregate owns no tree row at all. */done: boolean; /** Documents this call wrote, so callers can charge a real work budget. */
|
|
141
|
+
documents: number;
|
|
142
|
+
};
|
|
139
143
|
//#endregion
|
|
140
144
|
//#region src/aggregate-core/positions.d.ts
|
|
141
145
|
type Bound$1<K extends Key$2, ID extends string> = {
|
|
@@ -240,11 +244,12 @@ declare class Aggregate<K extends Key, ID extends string, TNamespace extends Val
|
|
|
240
244
|
rootLazy?: boolean;
|
|
241
245
|
}, TNamespace>): Promise<void>;
|
|
242
246
|
/**
|
|
243
|
-
* Deletes up to `limit` namespace
|
|
244
|
-
* once this aggregate owns no trees
|
|
245
|
-
*
|
|
247
|
+
* Deletes up to `limit` nodes from one namespace tree without recreating it,
|
|
248
|
+
* reporting `done` once this aggregate owns no trees and how many documents
|
|
249
|
+
* the call wrote. Callers drain every namespace across several mutations
|
|
250
|
+
* instead of walking them all in one, and charge their budget by `documents`.
|
|
246
251
|
*/
|
|
247
|
-
deleteTrees(ctx: RunMutationCtx, limit: number): Promise<
|
|
252
|
+
deleteTrees(ctx: RunMutationCtx, limit: number): Promise<DeleteTreesResult>;
|
|
248
253
|
makeRootLazy(ctx: RunMutationCtx, namespace: TNamespace): Promise<void>;
|
|
249
254
|
paginateNamespaces(ctx: RunQueryCtx, cursor?: string, pageSize?: number): Promise<{
|
|
250
255
|
cursor: string;
|
package/dist/aggregate/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { n as TableAggregate$1,
|
|
1
|
+
import { n as TableAggregate$1, t as DirectAggregate$1 } from "../runtime-B-8HKSIE.js";
|
|
2
|
+
import { i as aggregateStorageTables } from "../schema-BFP_awgP.js";
|
|
2
3
|
|
|
3
4
|
//#region src/aggregate/index.ts
|
|
4
5
|
const wrapTriggerFactory = (methodName, factory) => ((...args) => {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as GenericAuthTriggerHandlers, S as GenericAuthTriggerChange, T as defineAuth, b as GenericAuthBeforeResult, i as getGeneratedAuthDisabledReason, n as GeneratedAuthDisabledReasonKind, r as createDisabledAuthRuntime, t as AuthRuntime, w as GenericAuthTriggers, x as GenericAuthDefinition, y as BetterAuthOptionsWithoutDatabase } from "../../generated-contract-disabled-
|
|
1
|
+
import { C as GenericAuthTriggerHandlers, S as GenericAuthTriggerChange, T as defineAuth, b as GenericAuthBeforeResult, i as getGeneratedAuthDisabledReason, n as GeneratedAuthDisabledReasonKind, r as createDisabledAuthRuntime, t as AuthRuntime, w as GenericAuthTriggers, x as GenericAuthDefinition, y as BetterAuthOptionsWithoutDatabase } from "../../generated-contract-disabled-PdNGvNYP.js";
|
|
2
2
|
export { type AuthRuntime, BetterAuthOptionsWithoutDatabase, type GeneratedAuthDisabledReasonKind, GenericAuthBeforeResult, GenericAuthDefinition, GenericAuthTriggerChange, GenericAuthTriggerHandlers, GenericAuthTriggers, createDisabledAuthRuntime, defineAuth, getGeneratedAuthDisabledReason };
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as QueryCtxWithPreferredOrmQueryTable, n as LookupByIdResultByCtx, t as DocByCtx } from "../query-context-BkNjkDCk.js";
|
|
2
2
|
import { t as GetAuth } from "../types-ex0-J-SC.js";
|
|
3
3
|
import { t as GenericCtx } from "../context-utils-Cbv4r0AA.js";
|
|
4
|
-
import { C as GenericAuthTriggerHandlers, S as GenericAuthTriggerChange, T as defineAuth, _ as updateManyHandler, a as AuthFunctions, b as GenericAuthBeforeResult, c as consumeOneHandler, d as createHandler, f as deleteManyHandler, g as incrementOneHandler, h as findOneHandler, i as getGeneratedAuthDisabledReason, l as countHandler, m as findManyHandler, n as GeneratedAuthDisabledReasonKind, o as Triggers, p as deleteOneHandler, r as createDisabledAuthRuntime, s as createClient, t as AuthRuntime, u as createApi, v as updateOneHandler, w as GenericAuthTriggers, x as GenericAuthDefinition, y as BetterAuthOptionsWithoutDatabase } from "../generated-contract-disabled-
|
|
4
|
+
import { C as GenericAuthTriggerHandlers, S as GenericAuthTriggerChange, T as defineAuth, _ as updateManyHandler, a as AuthFunctions, b as GenericAuthBeforeResult, c as consumeOneHandler, d as createHandler, f as deleteManyHandler, g as incrementOneHandler, h as findOneHandler, i as getGeneratedAuthDisabledReason, l as countHandler, m as findManyHandler, n as GeneratedAuthDisabledReasonKind, o as Triggers, p as deleteOneHandler, r as createDisabledAuthRuntime, s as createClient, t as AuthRuntime, u as createApi, v as updateOneHandler, w as GenericAuthTriggers, x as GenericAuthDefinition, y as BetterAuthOptionsWithoutDatabase } from "../generated-contract-disabled-PdNGvNYP.js";
|
|
5
5
|
import * as convex_values0 from "convex/values";
|
|
6
6
|
import { Infer } from "convex/values";
|
|
7
7
|
import { AuthConfig, DocumentByName, GenericDataModel, GenericMutationCtx, GenericQueryCtx, GenericSchema, PaginationOptions, PaginationResult, SchemaDefinition, TableNamesInDataModel } from "convex/server";
|
|
@@ -109,28 +109,28 @@ type AdapterPaginationOptions = PaginationOptions & {
|
|
|
109
109
|
};
|
|
110
110
|
declare const adapterWhereValidator: convex_values0.VObject<{
|
|
111
111
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
112
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
113
112
|
connector?: "AND" | "OR" | undefined;
|
|
114
|
-
|
|
113
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
115
114
|
field: string;
|
|
115
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
116
116
|
}, {
|
|
117
117
|
connector: convex_values0.VUnion<"AND" | "OR" | undefined, [convex_values0.VLiteral<"AND", "required">, convex_values0.VLiteral<"OR", "required">], "optional", never>;
|
|
118
118
|
field: convex_values0.VString<string, "required">;
|
|
119
119
|
mode: convex_values0.VUnion<"sensitive" | "insensitive" | undefined, [convex_values0.VLiteral<"sensitive", "required">, convex_values0.VLiteral<"insensitive", "required">], "optional", never>;
|
|
120
|
-
operator: convex_values0.VUnion<"
|
|
120
|
+
operator: convex_values0.VUnion<"lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined, [convex_values0.VLiteral<"lt", "required">, convex_values0.VLiteral<"lte", "required">, convex_values0.VLiteral<"gt", "required">, convex_values0.VLiteral<"gte", "required">, convex_values0.VLiteral<"eq", "required">, convex_values0.VLiteral<"in", "required">, convex_values0.VLiteral<"not_in", "required">, convex_values0.VLiteral<"ne", "required">, convex_values0.VLiteral<"contains", "required">, convex_values0.VLiteral<"starts_with", "required">, convex_values0.VLiteral<"ends_with", "required">], "optional", never>;
|
|
121
121
|
value: convex_values0.VUnion<string | number | boolean | string[] | number[] | null, [convex_values0.VString<string, "required">, convex_values0.VFloat64<number, "required">, convex_values0.VBoolean<boolean, "required">, convex_values0.VArray<string[], convex_values0.VString<string, "required">, "required">, convex_values0.VArray<number[], convex_values0.VFloat64<number, "required">, "required">, convex_values0.VNull<null, "required">], "required", never>;
|
|
122
|
-
}, "required", "mode" | "
|
|
122
|
+
}, "required", "mode" | "connector" | "field" | "operator" | "value">;
|
|
123
123
|
declare const adapterArgsValidator: convex_values0.VObject<{
|
|
124
|
-
limit?: number | undefined;
|
|
125
124
|
where?: {
|
|
126
125
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
127
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
128
126
|
connector?: "AND" | "OR" | undefined;
|
|
129
|
-
|
|
127
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
130
128
|
field: string;
|
|
129
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
131
130
|
}[] | undefined;
|
|
132
|
-
|
|
131
|
+
limit?: number | undefined;
|
|
133
132
|
offset?: number | undefined;
|
|
133
|
+
select?: string[] | undefined;
|
|
134
134
|
sortBy?: {
|
|
135
135
|
field: string;
|
|
136
136
|
direction: "asc" | "desc";
|
|
@@ -150,24 +150,24 @@ declare const adapterArgsValidator: convex_values0.VObject<{
|
|
|
150
150
|
}, "optional", "field" | "direction">;
|
|
151
151
|
where: convex_values0.VArray<{
|
|
152
152
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
153
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
154
153
|
connector?: "AND" | "OR" | undefined;
|
|
155
|
-
|
|
154
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
156
155
|
field: string;
|
|
156
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
157
157
|
}[] | undefined, convex_values0.VObject<{
|
|
158
158
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
159
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
160
159
|
connector?: "AND" | "OR" | undefined;
|
|
161
|
-
|
|
160
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
162
161
|
field: string;
|
|
162
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
163
163
|
}, {
|
|
164
164
|
connector: convex_values0.VUnion<"AND" | "OR" | undefined, [convex_values0.VLiteral<"AND", "required">, convex_values0.VLiteral<"OR", "required">], "optional", never>;
|
|
165
165
|
field: convex_values0.VString<string, "required">;
|
|
166
166
|
mode: convex_values0.VUnion<"sensitive" | "insensitive" | undefined, [convex_values0.VLiteral<"sensitive", "required">, convex_values0.VLiteral<"insensitive", "required">], "optional", never>;
|
|
167
|
-
operator: convex_values0.VUnion<"
|
|
167
|
+
operator: convex_values0.VUnion<"lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined, [convex_values0.VLiteral<"lt", "required">, convex_values0.VLiteral<"lte", "required">, convex_values0.VLiteral<"gt", "required">, convex_values0.VLiteral<"gte", "required">, convex_values0.VLiteral<"eq", "required">, convex_values0.VLiteral<"in", "required">, convex_values0.VLiteral<"not_in", "required">, convex_values0.VLiteral<"ne", "required">, convex_values0.VLiteral<"contains", "required">, convex_values0.VLiteral<"starts_with", "required">, convex_values0.VLiteral<"ends_with", "required">], "optional", never>;
|
|
168
168
|
value: convex_values0.VUnion<string | number | boolean | string[] | number[] | null, [convex_values0.VString<string, "required">, convex_values0.VFloat64<number, "required">, convex_values0.VBoolean<boolean, "required">, convex_values0.VArray<string[], convex_values0.VString<string, "required">, "required">, convex_values0.VArray<number[], convex_values0.VFloat64<number, "required">, "required">, convex_values0.VNull<null, "required">], "required", never>;
|
|
169
|
-
}, "required", "mode" | "
|
|
170
|
-
}, "required", "
|
|
169
|
+
}, "required", "mode" | "connector" | "field" | "operator" | "value">, "optional">;
|
|
170
|
+
}, "required", "model" | "where" | "limit" | "offset" | "select" | "sortBy" | "sortBy.field" | "sortBy.direction">;
|
|
171
171
|
declare const hasUniqueFields: (betterAuthSchema: BetterAuthDBSchema, model: string, input: Record<string, any>) => boolean;
|
|
172
172
|
declare const checkUniqueFields: <Schema extends SchemaDefinition<any, any>>(ctx: GenericQueryCtx<GenericDataModel>, schema: Schema, betterAuthSchema: BetterAuthDBSchema, table: string, input: Record<string, any>, doc?: Record<string, any>) => Promise<void>;
|
|
173
173
|
declare const selectFields: <T extends TableNamesInDataModel<GenericDataModel>, D extends DocumentByName<GenericDataModel, T>>(doc: D | null, select?: string[]) => D | null;
|
package/dist/auth/index.js
CHANGED
|
@@ -3,8 +3,8 @@ import { r as partial } from "../validators-CIoUYCqO.js";
|
|
|
3
3
|
import { i as defineAuth, n as createDisabledAuthRuntime, r as getGeneratedAuthDisabledReason, t as DEFAULT_AUTH_DEFINITION_PATH } from "../generated-contract-disabled-RyPYlwq0.js";
|
|
4
4
|
import { n as createGeneratedFunctionReference, o as isQueryCtx, s as isRunMutationCtx } from "../api-entry-Buvjl9hn.js";
|
|
5
5
|
import { i as customQuery, n as customCtx, r as customMutation } from "../customFunctions-X_B4kET8.js";
|
|
6
|
-
import { C as eq, o as getAggregateIndexes } from "../index-utils-
|
|
7
|
-
import { o as mergedStream, s as stream, t as getByIdWithOrmQueryFallback, u as unsetToken } from "../query-context-
|
|
6
|
+
import { C as eq, o as getAggregateIndexes } from "../index-utils-DvK7P6Q1.js";
|
|
7
|
+
import { o as mergedStream, s as stream, t as getByIdWithOrmQueryFallback, u as unsetToken } from "../query-context-D4z1CnDH.js";
|
|
8
8
|
import { n as convex } from "../convex-plugin-AbNQl1Sg.js";
|
|
9
9
|
import { v } from "convex/values";
|
|
10
10
|
import { internalActionGeneric, internalMutationGeneric, internalQueryGeneric, paginationOptsValidator } from "convex/server";
|
|
@@ -2341,12 +2341,16 @@ type ClearIndexChunkResult = {
|
|
|
2341
2341
|
* reports whether anything is left. Callers drive it to completion across
|
|
2342
2342
|
* transactions, so clearing a large index never has to fit in one mutation.
|
|
2343
2343
|
*
|
|
2344
|
-
*
|
|
2345
|
-
*
|
|
2346
|
-
*
|
|
2347
|
-
*
|
|
2348
|
-
*
|
|
2349
|
-
*
|
|
2344
|
+
* Member rows are deleted outright rather than run back through the delta
|
|
2345
|
+
* machinery. The bucket and extrema branches below drop every row this index
|
|
2346
|
+
* owns whatever it holds, so folding a removal delta into a bucket first would
|
|
2347
|
+
* only rewrite a document this same clear is about to delete.
|
|
2348
|
+
*
|
|
2349
|
+
* Branch order is load-bearing: buckets and extrema are swept only once no
|
|
2350
|
+
* members are left, and the loop re-checks members afterwards. The intermediate
|
|
2351
|
+
* "members gone, buckets still populated" state is safe because `setCountState`
|
|
2352
|
+
* refuses to leave CLEARING while a bucket or extrema row survives, and the
|
|
2353
|
+
* CLEARING write barrier keeps concurrent writers out of the index.
|
|
2350
2354
|
*/
|
|
2351
2355
|
declare const clearCountIndexChunk: (db: GenericDatabaseWriter<any>, tableName: string, indexName: string, batchSize: number) => Promise<ClearIndexChunkResult>;
|
|
2352
2356
|
declare const listCountStates: (db: GenericDatabaseReader<any> | GenericDatabaseWriter<any>) => Promise<CountState[]>;
|
|
@@ -2392,10 +2396,20 @@ declare const ensureRankAllowedForRls: (tableConfig: TableRelationalConfig, rlsM
|
|
|
2392
2396
|
declare const compileRankPlan: (tableConfig: TableRelationalConfig, indexName: string, where: unknown) => RankQueryPlan;
|
|
2393
2397
|
declare const ensureRankIndexReady: (db: GenericDatabaseReader<any> | GenericDatabaseWriter<any>, tableName: string, indexName: string) => Promise<void>;
|
|
2394
2398
|
/**
|
|
2395
|
-
* Removes at most `batchSize`
|
|
2396
|
-
*
|
|
2397
|
-
*
|
|
2398
|
-
*
|
|
2399
|
+
* Removes at most `batchSize` documents of a rank index's stored state and
|
|
2400
|
+
* reports whether anything is left. Callers drive it to completion across
|
|
2401
|
+
* transactions, so clearing a large index never has to fit in one mutation.
|
|
2402
|
+
*
|
|
2403
|
+
* Member rows are dropped without touching the btree. The tree branch below
|
|
2404
|
+
* deletes every node whatever it contains, so removing a member key first would
|
|
2405
|
+
* only buy a root-to-leaf descent plus an aggregate patch per level on nodes
|
|
2406
|
+
* this same clear is about to delete.
|
|
2407
|
+
*
|
|
2408
|
+
* Branch order is load-bearing: a member delete recreates the tree it lands on,
|
|
2409
|
+
* so the members must be gone before the first node is dropped. The
|
|
2410
|
+
* intermediate "members gone, tree still full" state is safe because
|
|
2411
|
+
* `setCountState` refuses to leave CLEARING while a tree row survives, and the
|
|
2412
|
+
* CLEARING write barrier keeps concurrent writers out of the index.
|
|
2399
2413
|
*/
|
|
2400
2414
|
declare const clearRankIndexChunk: (db: GenericDatabaseWriter<any>, tableName: string, indexName: string, batchSize: number) => Promise<{
|
|
2401
2415
|
done: boolean;
|
|
@@ -2953,6 +2967,17 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
2953
2967
|
private _normalizeOrderByValue;
|
|
2954
2968
|
private _normalizeOrderBy;
|
|
2955
2969
|
private _orderBySpecs;
|
|
2970
|
+
/**
|
|
2971
|
+
* Whether rows can be missing this sort column or hold null in it.
|
|
2972
|
+
*
|
|
2973
|
+
* Only the declared validator can answer: a column that is not `.notNull()`
|
|
2974
|
+
* compiles to `v.optional(v.union(v.null(), T))`, so both absent and null are
|
|
2975
|
+
* legal stored values — and `timestamp().notNull().defaultNow()` on
|
|
2976
|
+
* `createdAt` is deliberately emitted as optional for migration safety, so
|
|
2977
|
+
* reading `config.notNull` alone would answer wrong for it. Anything the
|
|
2978
|
+
* lookup cannot resolve counts as nullable, which only costs a pushdown.
|
|
2979
|
+
*/
|
|
2980
|
+
private _isNullableOrderField;
|
|
2956
2981
|
private _resolveNonPaginatedLimit;
|
|
2957
2982
|
private _compareByOrderSpecs;
|
|
2958
2983
|
/**
|
|
@@ -3043,6 +3068,33 @@ declare class GelRelationalQuery<TSchema extends TablesRelationalConfig, TTableC
|
|
|
3043
3068
|
* the field produces that order.
|
|
3044
3069
|
*/
|
|
3045
3070
|
private _findStreamOrderIndex;
|
|
3071
|
+
/**
|
|
3072
|
+
* The compiled index union, as one ordered stream.
|
|
3073
|
+
*
|
|
3074
|
+
* Each probe is its own index range, so the union is only usable where the
|
|
3075
|
+
* merged order is the order the read has to produce. `mergedStream` orders by
|
|
3076
|
+
* a *suffix* of the index key, and a suffix may only drop key components the
|
|
3077
|
+
* probes all pin to a single value — so the requested field has to sit inside
|
|
3078
|
+
* the pinned run or immediately after it. Returns null when it does not, and
|
|
3079
|
+
* the caller falls back to the plan's plain index range or a bounded scan.
|
|
3080
|
+
*/
|
|
3081
|
+
private _buildProbeUnionStream;
|
|
3082
|
+
/**
|
|
3083
|
+
* The read the compiled plan describes, as a stream, with nothing filtered
|
|
3084
|
+
* yet.
|
|
3085
|
+
*
|
|
3086
|
+
* Precedence: the compiled index union, then the compiled index range, then
|
|
3087
|
+
* the caller's pinned `.withIndex(...)`, then an index that supplies the
|
|
3088
|
+
* requested order, then a full scan. The first two rungs can only ever refine
|
|
3089
|
+
* what the caller pinned — `_toConvexQuery` discards a compiled plan that
|
|
3090
|
+
* would displace a caller's index or its bounds before it gets here.
|
|
3091
|
+
*
|
|
3092
|
+
* `probeUnion` tells the caller the read is bounded by index ranges rather
|
|
3093
|
+
* than by scan length, which is what makes a scan budget unnecessary. A
|
|
3094
|
+
* rejected union stays unanchored; its original predicate remains in
|
|
3095
|
+
* `queryConfig.postFilters` for the caller to apply while pulling the scan.
|
|
3096
|
+
*/
|
|
3097
|
+
private _buildPlanStream;
|
|
3046
3098
|
private _buildBasePipelineStream;
|
|
3047
3099
|
/**
|
|
3048
3100
|
* Stream equivalent of the `db.query(...)` chain, used when a post-fetch
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { C as Columns, D as TableName, E as RlsPolicies, O as createSystemFields, S as getRankIndexes, T as OrmSchemaExtensions, _ as loadEsbuild, a as generateMeta, b as getAggregateIndexes, c as resolveSchemaDefaultExport, f as createProjectJiti, g as loadDotenv, h as loadClackPrompts, i as resolveConfiguredBackend, l as schemaDeclaresAggregateIndexes, m as loadBabelParser, n as withLocalCodegenEnv, o as getConvexConfig, p as CRPC_BUILDER_STUB_SOURCE, r as loadCliConfig, s as collectSchemaTables, t as getLocalBackendEnvVars, u as logger, v as highlighter, w as EnableRLS, x as getIndexes, y as isColorEnabled } from "./local-env-
|
|
2
|
+
import { C as Columns, D as TableName, E as RlsPolicies, O as createSystemFields, S as getRankIndexes, T as OrmSchemaExtensions, _ as loadEsbuild, a as generateMeta, b as getAggregateIndexes, c as resolveSchemaDefaultExport, f as createProjectJiti, g as loadDotenv, h as loadClackPrompts, i as resolveConfiguredBackend, l as schemaDeclaresAggregateIndexes, m as loadBabelParser, n as withLocalCodegenEnv, o as getConvexConfig, p as CRPC_BUILDER_STUB_SOURCE, r as loadCliConfig, s as collectSchemaTables, t as getLocalBackendEnvVars, u as logger, v as highlighter, w as EnableRLS, x as getIndexes, y as isColorEnabled } from "./local-env-CYYSNf_o.mjs";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import fs, { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import path, { basename, delimiter, dirname, extname, isAbsolute, join, posix, relative, resolve } from "node:path";
|
package/dist/{generated-contract-disabled-CTUQ9nxL.d.ts → generated-contract-disabled-PdNGvNYP.d.ts}
RENAMED
|
@@ -199,20 +199,20 @@ declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel e
|
|
|
199
199
|
count: convex_server0.RegisteredQuery<"internal", {
|
|
200
200
|
where?: {
|
|
201
201
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
202
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
203
202
|
connector?: "AND" | "OR" | undefined;
|
|
204
|
-
|
|
203
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
205
204
|
field: string;
|
|
205
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
206
206
|
}[] | undefined;
|
|
207
207
|
model: string;
|
|
208
208
|
}, Promise<number | null>>;
|
|
209
209
|
consumeOne: convex_server0.RegisteredMutation<"internal", {
|
|
210
210
|
input: {
|
|
211
211
|
where?: {
|
|
212
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
213
212
|
connector?: "AND" | "OR" | undefined;
|
|
214
|
-
|
|
213
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
215
214
|
field: string;
|
|
215
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
216
216
|
}[] | undefined;
|
|
217
217
|
model: string;
|
|
218
218
|
} | {
|
|
@@ -235,26 +235,26 @@ declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel e
|
|
|
235
235
|
};
|
|
236
236
|
}, Promise<any>>;
|
|
237
237
|
deleteMany: convex_server0.RegisteredMutation<"internal", {
|
|
238
|
-
paginationOpts: {
|
|
239
|
-
id?: number;
|
|
240
|
-
endCursor?: string | null;
|
|
241
|
-
maximumRowsRead?: number;
|
|
242
|
-
maximumBytesRead?: number;
|
|
243
|
-
numItems: number;
|
|
244
|
-
cursor: string | null;
|
|
245
|
-
};
|
|
246
238
|
input: {
|
|
247
239
|
where?: {
|
|
248
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
249
240
|
connector?: "AND" | "OR" | undefined;
|
|
250
|
-
|
|
241
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
251
242
|
field: string;
|
|
243
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
252
244
|
}[] | undefined;
|
|
253
245
|
model: string;
|
|
254
246
|
} | {
|
|
255
247
|
where?: any[] | undefined;
|
|
256
248
|
model: string;
|
|
257
249
|
};
|
|
250
|
+
paginationOpts: {
|
|
251
|
+
id?: number;
|
|
252
|
+
endCursor?: string | null;
|
|
253
|
+
maximumRowsRead?: number;
|
|
254
|
+
maximumBytesRead?: number;
|
|
255
|
+
numItems: number;
|
|
256
|
+
cursor: string | null;
|
|
257
|
+
};
|
|
258
258
|
}, Promise<{
|
|
259
259
|
count: number;
|
|
260
260
|
ids: any[];
|
|
@@ -266,10 +266,10 @@ declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel e
|
|
|
266
266
|
deleteOne: convex_server0.RegisteredMutation<"internal", {
|
|
267
267
|
input: {
|
|
268
268
|
where?: {
|
|
269
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
270
269
|
connector?: "AND" | "OR" | undefined;
|
|
271
|
-
|
|
270
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
272
271
|
field: string;
|
|
272
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
273
273
|
}[] | undefined;
|
|
274
274
|
model: string;
|
|
275
275
|
} | {
|
|
@@ -278,20 +278,21 @@ declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel e
|
|
|
278
278
|
};
|
|
279
279
|
}, Promise<Record<string, unknown> | undefined>>;
|
|
280
280
|
findMany: convex_server0.RegisteredQuery<"internal", {
|
|
281
|
-
limit?: number | undefined;
|
|
282
281
|
join?: any;
|
|
283
282
|
where?: {
|
|
284
283
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
285
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
286
284
|
connector?: "AND" | "OR" | undefined;
|
|
287
|
-
|
|
285
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
288
286
|
field: string;
|
|
287
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
289
288
|
}[] | undefined;
|
|
289
|
+
limit?: number | undefined;
|
|
290
290
|
offset?: number | undefined;
|
|
291
291
|
sortBy?: {
|
|
292
292
|
field: string;
|
|
293
293
|
direction: "asc" | "desc";
|
|
294
294
|
} | undefined;
|
|
295
|
+
model: string;
|
|
295
296
|
paginationOpts: {
|
|
296
297
|
id?: number;
|
|
297
298
|
endCursor?: string | null;
|
|
@@ -300,16 +301,15 @@ declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel e
|
|
|
300
301
|
numItems: number;
|
|
301
302
|
cursor: string | null;
|
|
302
303
|
};
|
|
303
|
-
model: string;
|
|
304
304
|
}, Promise<PaginationResult<convex_server0.GenericDocument>>>;
|
|
305
305
|
findOne: convex_server0.RegisteredQuery<"internal", {
|
|
306
306
|
join?: any;
|
|
307
307
|
where?: {
|
|
308
308
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
309
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
310
309
|
connector?: "AND" | "OR" | undefined;
|
|
311
|
-
|
|
310
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
312
311
|
field: string;
|
|
312
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
313
313
|
}[] | undefined;
|
|
314
314
|
select?: string[] | undefined;
|
|
315
315
|
model: string;
|
|
@@ -319,10 +319,10 @@ declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel e
|
|
|
319
319
|
input: {
|
|
320
320
|
where?: {
|
|
321
321
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
322
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
323
322
|
connector?: "AND" | "OR" | undefined;
|
|
324
|
-
|
|
323
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
325
324
|
field: string;
|
|
325
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
326
326
|
}[] | undefined;
|
|
327
327
|
set?: Record<string, any> | undefined;
|
|
328
328
|
model: string;
|
|
@@ -331,31 +331,31 @@ declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel e
|
|
|
331
331
|
}, Promise<any>>;
|
|
332
332
|
rotateKeys: convex_server0.RegisteredAction<"internal", {}, Promise<unknown>>;
|
|
333
333
|
updateMany: convex_server0.RegisteredMutation<"internal", {
|
|
334
|
-
paginationOpts: {
|
|
335
|
-
id?: number;
|
|
336
|
-
endCursor?: string | null;
|
|
337
|
-
maximumRowsRead?: number;
|
|
338
|
-
maximumBytesRead?: number;
|
|
339
|
-
numItems: number;
|
|
340
|
-
cursor: string | null;
|
|
341
|
-
};
|
|
342
334
|
input: {
|
|
343
335
|
where?: {
|
|
344
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
345
336
|
connector?: "AND" | "OR" | undefined;
|
|
346
|
-
|
|
337
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
347
338
|
field: string;
|
|
339
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
348
340
|
}[] | undefined;
|
|
341
|
+
model: string;
|
|
349
342
|
update: {
|
|
350
343
|
[x: string]: unknown;
|
|
351
344
|
[x: number]: unknown;
|
|
352
345
|
[x: symbol]: unknown;
|
|
353
346
|
};
|
|
354
|
-
model: string;
|
|
355
347
|
} | {
|
|
356
348
|
where?: any[] | undefined;
|
|
357
|
-
update: any;
|
|
358
349
|
model: string;
|
|
350
|
+
update: any;
|
|
351
|
+
};
|
|
352
|
+
paginationOpts: {
|
|
353
|
+
id?: number;
|
|
354
|
+
endCursor?: string | null;
|
|
355
|
+
maximumRowsRead?: number;
|
|
356
|
+
maximumBytesRead?: number;
|
|
357
|
+
numItems: number;
|
|
358
|
+
cursor: string | null;
|
|
359
359
|
};
|
|
360
360
|
}, Promise<{
|
|
361
361
|
count: number;
|
|
@@ -368,21 +368,21 @@ declare const createApi: <Schema extends SchemaDefinition<any, any>, DataModel e
|
|
|
368
368
|
updateOne: convex_server0.RegisteredMutation<"internal", {
|
|
369
369
|
input: {
|
|
370
370
|
where?: {
|
|
371
|
-
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
372
371
|
connector?: "AND" | "OR" | undefined;
|
|
373
|
-
|
|
372
|
+
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
374
373
|
field: string;
|
|
374
|
+
value: string | number | boolean | string[] | number[] | null;
|
|
375
375
|
}[] | undefined;
|
|
376
|
+
model: string;
|
|
376
377
|
update: {
|
|
377
378
|
[x: string]: unknown;
|
|
378
379
|
[x: number]: unknown;
|
|
379
380
|
[x: symbol]: unknown;
|
|
380
381
|
};
|
|
381
|
-
model: string;
|
|
382
382
|
} | {
|
|
383
383
|
where?: any[] | undefined;
|
|
384
|
-
update: any;
|
|
385
384
|
model: string;
|
|
385
|
+
update: any;
|
|
386
386
|
};
|
|
387
387
|
}, Promise<any>>;
|
|
388
388
|
};
|
|
@@ -458,10 +458,11 @@ const hasColumnPrefix = (index, columns) => {
|
|
|
458
458
|
*
|
|
459
459
|
* Convex walks an index in full key order, so once a leading run of fields is
|
|
460
460
|
* pinned to a single value by `eq`, the remainder of the scan is already sorted
|
|
461
|
-
* by the next index field — and by `_creationTime`
|
|
462
|
-
*
|
|
463
|
-
*
|
|
464
|
-
* the exact page and nothing has to be
|
|
461
|
+
* by the next index field, then the one after that — and by `_creationTime`
|
|
462
|
+
* once every declared field is consumed, because Convex appends it as the
|
|
463
|
+
* implicit trailing key. When that sequence is the one the caller asked to sort
|
|
464
|
+
* by, `.order(dir).take(n)` returns the exact page and nothing has to be
|
|
465
|
+
* collected and sorted afterwards.
|
|
465
466
|
*
|
|
466
467
|
* This is the single owner of that decision. The top-level query planner and
|
|
467
468
|
* the relation loader both call it, so a bound one of them can push into the
|
|
@@ -472,12 +473,34 @@ const hasColumnPrefix = (index, columns) => {
|
|
|
472
473
|
*/
|
|
473
474
|
function resolveIndexOrderPushdown(params) {
|
|
474
475
|
const { indexFields, pinnedEqCount, orderSpecs } = params;
|
|
475
|
-
if (!indexFields || orderSpecs.length
|
|
476
|
-
const primary = orderSpecs[0];
|
|
476
|
+
if (!indexFields || orderSpecs.length === 0) return null;
|
|
477
477
|
const eqCount = Math.min(Math.max(pinnedEqCount, 0), indexFields.length);
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
478
|
+
const pinnedFields = new Set(indexFields.slice(0, eqCount));
|
|
479
|
+
/** Next unpinned index key the scan will sort by. */
|
|
480
|
+
let cursor = eqCount;
|
|
481
|
+
/** Direction every unpinned spec has to agree on. */
|
|
482
|
+
let direction = null;
|
|
483
|
+
let consumedCreationTime = false;
|
|
484
|
+
let hasNullableSpec = false;
|
|
485
|
+
for (const spec of orderSpecs) {
|
|
486
|
+
if (pinnedFields.has(spec.field)) continue;
|
|
487
|
+
if (direction === null) direction = spec.direction;
|
|
488
|
+
else if (direction !== spec.direction) return null;
|
|
489
|
+
if (spec.nullable !== false) hasNullableSpec = true;
|
|
490
|
+
if (consumedCreationTime) return null;
|
|
491
|
+
if (cursor >= indexFields.length) {
|
|
492
|
+
if (spec.field !== INTERNAL_CREATION_TIME_FIELD) return null;
|
|
493
|
+
consumedCreationTime = true;
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
496
|
+
if (spec.field !== indexFields[cursor]) return null;
|
|
497
|
+
cursor += 1;
|
|
498
|
+
}
|
|
499
|
+
if (orderSpecs.length > 1 && direction !== null && cursor < indexFields.length) return null;
|
|
500
|
+
const primary = orderSpecs[0];
|
|
501
|
+
if (direction !== null && pinnedFields.has(primary.field) && primary.direction !== direction && !consumedCreationTime) return null;
|
|
502
|
+
if (orderSpecs.length > 1 && hasNullableSpec) return null;
|
|
503
|
+
return direction ?? orderSpecs[0].direction;
|
|
481
504
|
}
|
|
482
505
|
function findRelationIndex(table, columns, relationName, targetTableName, strict = true, allowFullScan = false, orderSpecs = []) {
|
|
483
506
|
const indexes = getIndexes(table);
|