@vexcms/better-auth 0.0.19 → 0.1.0-alpha.2
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/LICENSE +202 -0
- package/dist/adapter.d.ts +72 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/convex/adapter.d.ts +13 -0
- package/dist/convex/adapter.d.ts.map +1 -0
- package/dist/convex/db.d.ts +117 -0
- package/dist/convex/db.d.ts.map +1 -0
- package/dist/convex/getAuth.d.ts +27 -0
- package/dist/convex/getAuth.d.ts.map +1 -0
- package/dist/convex/index.d.ts +117 -0
- package/dist/convex/index.d.ts.map +1 -0
- package/dist/convex/types.d.ts +103 -0
- package/dist/convex/types.d.ts.map +1 -0
- package/dist/convex/utils.d.ts +51 -0
- package/dist/convex/utils.d.ts.map +1 -0
- package/dist/index.d.ts +35 -41
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2261 -139
- package/dist/index.js.map +1 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -0
- package/package.json +36 -11
package/dist/index.js
CHANGED
|
@@ -1,172 +1,2294 @@
|
|
|
1
|
-
// src/
|
|
1
|
+
// src/adapter.ts
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
array,
|
|
4
|
+
checkbox,
|
|
5
|
+
date,
|
|
6
|
+
defineCollection,
|
|
7
|
+
number,
|
|
8
|
+
select,
|
|
9
|
+
text
|
|
4
10
|
} from "@vexcms/core";
|
|
5
11
|
import { getAuthTables } from "better-auth/db";
|
|
6
|
-
var
|
|
7
|
-
user:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
12
|
+
var AUTH_COLLECTION_TYPES = {
|
|
13
|
+
user: "user",
|
|
14
|
+
session: "session",
|
|
15
|
+
account: "account",
|
|
16
|
+
verification: "verification"
|
|
17
|
+
};
|
|
18
|
+
var EDITABLE_FIELDS = /* @__PURE__ */ new Set([
|
|
19
|
+
"name",
|
|
20
|
+
"email",
|
|
21
|
+
"image",
|
|
22
|
+
"role",
|
|
23
|
+
"banned",
|
|
24
|
+
"banReason",
|
|
25
|
+
"banExpires"
|
|
26
|
+
]);
|
|
27
|
+
var HIDDEN_FIELDS = /* @__PURE__ */ new Set([
|
|
28
|
+
"hashedPassword",
|
|
29
|
+
"password",
|
|
30
|
+
"twoFactorSecret",
|
|
31
|
+
"twoFactorBackupCodes",
|
|
32
|
+
"token",
|
|
33
|
+
"secret",
|
|
34
|
+
"code"
|
|
35
|
+
]);
|
|
36
|
+
function betterAuthAdapter(props) {
|
|
37
|
+
const tables = getAuthTables(props?.config ?? {});
|
|
38
|
+
const collections = [];
|
|
39
|
+
const authCollections = [
|
|
40
|
+
{ type: AUTH_COLLECTION_TYPES.user, slug: props?.config?.user?.modelName },
|
|
41
|
+
{
|
|
42
|
+
type: AUTH_COLLECTION_TYPES.session,
|
|
43
|
+
slug: props?.config?.session?.modelName
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: AUTH_COLLECTION_TYPES.account,
|
|
47
|
+
slug: props?.config?.account?.modelName
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: AUTH_COLLECTION_TYPES.verification,
|
|
51
|
+
slug: props?.config?.verification?.modelName
|
|
52
|
+
}
|
|
53
|
+
];
|
|
54
|
+
for (const [tableKey, tableDef] of Object.entries(tables)) {
|
|
55
|
+
const slug = tableDef.modelName ?? tableKey;
|
|
56
|
+
const fields = {};
|
|
57
|
+
addAuthCollectionFields({
|
|
58
|
+
attributes: tableDef.fields,
|
|
59
|
+
slug,
|
|
60
|
+
fields,
|
|
61
|
+
config: props?.config,
|
|
62
|
+
extractId: true
|
|
63
|
+
});
|
|
64
|
+
const authCollectionType = authCollections.find(
|
|
65
|
+
(ac) => ac.slug === slug
|
|
66
|
+
)?.type;
|
|
67
|
+
if (authCollectionType) {
|
|
68
|
+
switch (authCollectionType) {
|
|
69
|
+
case "user": {
|
|
70
|
+
const additionalFields = props?.config?.user?.additionalFields;
|
|
71
|
+
addAuthCollectionFields({
|
|
72
|
+
attributes: additionalFields,
|
|
73
|
+
slug,
|
|
74
|
+
fields,
|
|
75
|
+
config: props?.config
|
|
76
|
+
});
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
case "session": {
|
|
80
|
+
const additionalFields = props?.config?.session?.additionalFields;
|
|
81
|
+
addAuthCollectionFields({
|
|
82
|
+
attributes: additionalFields,
|
|
83
|
+
slug,
|
|
84
|
+
fields,
|
|
85
|
+
config: props?.config
|
|
86
|
+
});
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case "account": {
|
|
90
|
+
const additionalFields = props?.config?.account?.additionalFields;
|
|
91
|
+
addAuthCollectionFields({
|
|
92
|
+
attributes: additionalFields,
|
|
93
|
+
slug,
|
|
94
|
+
fields,
|
|
95
|
+
config: props?.config
|
|
96
|
+
});
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case "verification": {
|
|
100
|
+
const additionalFields = props?.config?.verification?.additionalFields;
|
|
101
|
+
addAuthCollectionFields({
|
|
102
|
+
attributes: additionalFields,
|
|
103
|
+
slug,
|
|
104
|
+
fields,
|
|
105
|
+
config: props?.config
|
|
106
|
+
});
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
default:
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const isProtected = slug !== "user" && slug !== "users";
|
|
114
|
+
collections.push(
|
|
115
|
+
defineCollection({
|
|
116
|
+
slug,
|
|
117
|
+
fields,
|
|
118
|
+
meta: isProtected ? { protected: true } : void 0
|
|
119
|
+
})
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
const userSlug = tables.user?.modelName ?? "user";
|
|
123
|
+
return {
|
|
124
|
+
name: "better-auth",
|
|
125
|
+
collections,
|
|
126
|
+
userCollection: userSlug
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function addAuthCollectionFields(props) {
|
|
130
|
+
if (!props.attributes) return;
|
|
131
|
+
for (const [fieldName, attr] of Object.entries(props.attributes)) {
|
|
132
|
+
if (props.extractId === true && fieldName === "id") continue;
|
|
133
|
+
const field = betterAuthAttrToVexField(
|
|
134
|
+
fieldName,
|
|
135
|
+
attr,
|
|
136
|
+
props.slug,
|
|
137
|
+
props?.config
|
|
138
|
+
);
|
|
139
|
+
if (field) props.fields[fieldName] = field;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function betterAuthAttrToVexField(fieldName, attr, tableSlug, authOptions) {
|
|
143
|
+
if (fieldName === "id") return null;
|
|
144
|
+
const isUserTable = tableSlug === authOptions?.user?.modelName || tableSlug === "user" || tableSlug === "users";
|
|
145
|
+
const isEditable = isUserTable && EDITABLE_FIELDS.has(fieldName);
|
|
146
|
+
const isHidden = HIDDEN_FIELDS.has(fieldName);
|
|
147
|
+
const admin = {};
|
|
148
|
+
if (!isEditable) admin.readOnly = true;
|
|
149
|
+
if (isHidden) admin.hidden = true;
|
|
150
|
+
const baseOptions = {
|
|
151
|
+
defaultValue: void 0
|
|
152
|
+
};
|
|
153
|
+
if (Object.keys(admin).length > 0) baseOptions.admin = admin;
|
|
154
|
+
if (!isEditable) baseOptions.meta = { locked: true };
|
|
155
|
+
if (attr.required) baseOptions.required = attr.required;
|
|
156
|
+
if (attr.defaultValue !== void 0 && typeof attr.defaultValue !== "function") {
|
|
157
|
+
baseOptions.defaultValue = attr.defaultValue;
|
|
158
|
+
}
|
|
159
|
+
if (attr.unique || attr.index) {
|
|
160
|
+
baseOptions.index = `by_${fieldName}`;
|
|
161
|
+
}
|
|
162
|
+
if (attr.references) {
|
|
163
|
+
return text(baseOptions);
|
|
164
|
+
}
|
|
165
|
+
if (Array.isArray(attr.type)) {
|
|
166
|
+
return select({
|
|
167
|
+
options: attr.type.map((v2) => ({ value: v2, label: v2 })),
|
|
168
|
+
...baseOptions
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
switch (attr.type) {
|
|
172
|
+
case "string":
|
|
173
|
+
return text(baseOptions);
|
|
174
|
+
case "string[]":
|
|
175
|
+
return array({
|
|
176
|
+
...baseOptions,
|
|
177
|
+
items: text(baseOptions)
|
|
178
|
+
});
|
|
179
|
+
case "boolean":
|
|
180
|
+
return checkbox(baseOptions);
|
|
181
|
+
case "number":
|
|
182
|
+
return number(baseOptions);
|
|
183
|
+
case "number[]":
|
|
184
|
+
return array({
|
|
185
|
+
...baseOptions,
|
|
186
|
+
items: number(baseOptions)
|
|
187
|
+
});
|
|
188
|
+
case "date":
|
|
189
|
+
return date(baseOptions);
|
|
190
|
+
case "json":
|
|
191
|
+
return text(baseOptions);
|
|
192
|
+
default:
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/convex/adapter.ts
|
|
198
|
+
import { anyApi } from "convex/server";
|
|
199
|
+
import {
|
|
200
|
+
createAdapterFactory
|
|
201
|
+
} from "better-auth/adapters";
|
|
202
|
+
import { getAuthTables as getAuthTables2 } from "better-auth/db";
|
|
203
|
+
var parseWhere = (where) => {
|
|
204
|
+
return where?.map((where2) => {
|
|
205
|
+
if (where2.value instanceof Date) {
|
|
206
|
+
return { ...where2, value: where2.value.getTime() };
|
|
207
|
+
}
|
|
208
|
+
return where2;
|
|
209
|
+
}) ?? [];
|
|
210
|
+
};
|
|
211
|
+
function convexAdapter(ctx, config = {}) {
|
|
212
|
+
return createAdapterFactory({
|
|
213
|
+
adapter: ({ options }) => {
|
|
214
|
+
options.telemetry = { enabled: false };
|
|
215
|
+
const betterAuthSchema = getAuthTables2(options);
|
|
216
|
+
const betterAuthSchemaJson = JSON.stringify(betterAuthSchema);
|
|
217
|
+
return {
|
|
218
|
+
id: "convex",
|
|
219
|
+
options: { isRunMutationCtx: "runMutation" in ctx },
|
|
220
|
+
// Use anyApi to reference db operations, call via ctx.runMutation/ctx.runQuery
|
|
221
|
+
create: async ({ data, model, select: select2 }) => {
|
|
222
|
+
return await ctx.runMutation(anyApi.auth.db.dbCreate, {
|
|
223
|
+
betterAuthSchema: betterAuthSchemaJson,
|
|
224
|
+
data,
|
|
225
|
+
model,
|
|
226
|
+
select: select2
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
findOne: async ({ model, select: select2, where }) => {
|
|
230
|
+
return await ctx.runQuery(anyApi.auth.db.dbFindOne, {
|
|
231
|
+
betterAuthSchema: betterAuthSchemaJson,
|
|
232
|
+
model,
|
|
233
|
+
select: select2,
|
|
234
|
+
where: parseWhere(where)
|
|
235
|
+
});
|
|
236
|
+
},
|
|
237
|
+
findMany: async ({ limit, model, sortBy, where }) => {
|
|
238
|
+
return await ctx.runQuery(anyApi.auth.db.dbFindMany, {
|
|
239
|
+
betterAuthSchema: betterAuthSchemaJson,
|
|
240
|
+
limit,
|
|
241
|
+
model,
|
|
242
|
+
sortBy,
|
|
243
|
+
where: parseWhere(where)
|
|
244
|
+
});
|
|
245
|
+
},
|
|
246
|
+
count: async ({ model, where }) => {
|
|
247
|
+
return await ctx.runQuery(anyApi.auth.db.dbCount, {
|
|
248
|
+
betterAuthSchema: betterAuthSchemaJson,
|
|
249
|
+
model,
|
|
250
|
+
where: parseWhere(where)
|
|
251
|
+
});
|
|
252
|
+
},
|
|
253
|
+
update: async ({ model, update: update2, where }) => {
|
|
254
|
+
return await ctx.runMutation(anyApi.auth.db.dbUpdate, {
|
|
255
|
+
betterAuthSchema: betterAuthSchemaJson,
|
|
256
|
+
model,
|
|
257
|
+
update: update2,
|
|
258
|
+
where: parseWhere(where)
|
|
259
|
+
});
|
|
260
|
+
},
|
|
261
|
+
updateMany: async ({ model, update: update2, where }) => {
|
|
262
|
+
return await ctx.runMutation(anyApi.auth.db.dbUpdateMany, {
|
|
263
|
+
betterAuthSchema: betterAuthSchemaJson,
|
|
264
|
+
model,
|
|
265
|
+
update: update2,
|
|
266
|
+
where: parseWhere(where)
|
|
267
|
+
});
|
|
268
|
+
},
|
|
269
|
+
delete: async ({ model, where }) => {
|
|
270
|
+
return await ctx.runMutation(anyApi.auth.db.dbDelete, {
|
|
271
|
+
betterAuthSchema: betterAuthSchemaJson,
|
|
272
|
+
model,
|
|
273
|
+
where: parseWhere(where)
|
|
274
|
+
});
|
|
275
|
+
},
|
|
276
|
+
deleteMany: async ({ model, where }) => {
|
|
277
|
+
return await ctx.runMutation(anyApi.auth.db.dbDeleteMany, {
|
|
278
|
+
betterAuthSchema: betterAuthSchemaJson,
|
|
279
|
+
model,
|
|
280
|
+
where: parseWhere(where)
|
|
281
|
+
});
|
|
282
|
+
},
|
|
283
|
+
transaction: async () => {
|
|
284
|
+
throw new Error("Transactions not supported");
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
config: {
|
|
289
|
+
adapterId: "convex",
|
|
290
|
+
adapterName: "Convex Adapter",
|
|
291
|
+
customTransformInput: ({ data, fieldAttributes }) => data && fieldAttributes.type === "date" ? new Date(data).getTime() : data,
|
|
292
|
+
customTransformOutput: ({ data, fieldAttributes }) => data && fieldAttributes.type === "date" ? new Date(data).getTime() : data,
|
|
293
|
+
debugLogs: config.debugLogs ?? false,
|
|
294
|
+
disableIdGeneration: true,
|
|
295
|
+
mapKeysTransformInput: { id: "_id" },
|
|
296
|
+
mapKeysTransformOutput: { _id: "id" },
|
|
297
|
+
supportsArrays: true,
|
|
298
|
+
supportsDates: false,
|
|
299
|
+
supportsJSON: true,
|
|
300
|
+
supportsNumericIds: false,
|
|
301
|
+
transaction: false,
|
|
302
|
+
usePlural: false
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// src/convex/db.ts
|
|
308
|
+
import { v } from "convex/values";
|
|
309
|
+
|
|
310
|
+
// ../../node_modules/.pnpm/convex-helpers@0.1.120_@standard-schema+spec@1.1.0_convex@1.44.0_react@19.2.7__hono@4.1_fda37119e0b134c2babc8f4bd1faf097/node_modules/convex-helpers/index.js
|
|
311
|
+
async function asyncMap(list, asyncTransform) {
|
|
312
|
+
const promises = [];
|
|
313
|
+
let index = 0;
|
|
314
|
+
list = await list;
|
|
315
|
+
for (const item of list) {
|
|
316
|
+
promises.push(asyncTransform(item, index));
|
|
317
|
+
index += 1;
|
|
318
|
+
}
|
|
319
|
+
return Promise.all(promises);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ../../node_modules/.pnpm/convex-helpers@0.1.120_@standard-schema+spec@1.1.0_convex@1.44.0_react@19.2.7__hono@4.1_fda37119e0b134c2babc8f4bd1faf097/node_modules/convex-helpers/server/stream.js
|
|
323
|
+
import { convexToJson, compareValues, jsonToConvex, getDocumentSize } from "convex/values";
|
|
324
|
+
var MAX_DOCUMENT_SCAN_LEN = 32e3;
|
|
325
|
+
var SOFT_MAX_SCAN_LEN = MAX_DOCUMENT_SCAN_LEN / 2;
|
|
326
|
+
function makeExclusive(boundType) {
|
|
327
|
+
if (boundType === "gt" || boundType === "gte") {
|
|
328
|
+
return "gt";
|
|
329
|
+
}
|
|
330
|
+
return "lt";
|
|
331
|
+
}
|
|
332
|
+
function splitRange(indexFields, order, startBound, endBound, startBoundType, endBoundType) {
|
|
333
|
+
const commonPrefix2 = [];
|
|
334
|
+
while (startBound.length > 0 && endBound.length > 0 && compareValues(startBound[0], endBound[0]) === 0) {
|
|
335
|
+
const indexField = indexFields[0];
|
|
336
|
+
indexFields = indexFields.slice(1);
|
|
337
|
+
const eqBound = startBound[0];
|
|
338
|
+
startBound = startBound.slice(1);
|
|
339
|
+
endBound = endBound.slice(1);
|
|
340
|
+
commonPrefix2.push(["eq", indexField, eqBound]);
|
|
341
|
+
}
|
|
342
|
+
const makeCompare = (boundType, key) => {
|
|
343
|
+
const range = commonPrefix2.slice();
|
|
344
|
+
let i = 0;
|
|
345
|
+
for (; i < key.length - 1; i++) {
|
|
346
|
+
range.push(["eq", indexFields[i], key[i]]);
|
|
347
|
+
}
|
|
348
|
+
if (i < key.length) {
|
|
349
|
+
range.push([boundType, indexFields[i], key[i]]);
|
|
350
|
+
}
|
|
351
|
+
return range;
|
|
352
|
+
};
|
|
353
|
+
const startRanges = [];
|
|
354
|
+
while (startBound.length > 1) {
|
|
355
|
+
startRanges.push(makeCompare(startBoundType, startBound));
|
|
356
|
+
startBoundType = makeExclusive(startBoundType);
|
|
357
|
+
startBound = startBound.slice(0, -1);
|
|
358
|
+
}
|
|
359
|
+
const endRanges = [];
|
|
360
|
+
while (endBound.length > 1) {
|
|
361
|
+
endRanges.push(makeCompare(endBoundType, endBound));
|
|
362
|
+
endBoundType = makeExclusive(endBoundType);
|
|
363
|
+
endBound = endBound.slice(0, -1);
|
|
364
|
+
}
|
|
365
|
+
endRanges.reverse();
|
|
366
|
+
let middleRange;
|
|
367
|
+
if (endBound.length === 0) {
|
|
368
|
+
middleRange = makeCompare(startBoundType, startBound);
|
|
369
|
+
} else if (startBound.length === 0) {
|
|
370
|
+
middleRange = makeCompare(endBoundType, endBound);
|
|
371
|
+
} else {
|
|
372
|
+
const startValue = startBound[0];
|
|
373
|
+
const endValue = endBound[0];
|
|
374
|
+
middleRange = commonPrefix2.slice();
|
|
375
|
+
middleRange.push([startBoundType, indexFields[0], startValue]);
|
|
376
|
+
middleRange.push([endBoundType, indexFields[0], endValue]);
|
|
377
|
+
}
|
|
378
|
+
const ranges = [...startRanges, middleRange, ...endRanges];
|
|
379
|
+
if (order === "desc") {
|
|
380
|
+
ranges.reverse();
|
|
381
|
+
}
|
|
382
|
+
return ranges;
|
|
383
|
+
}
|
|
384
|
+
function rangeToQuery(range) {
|
|
385
|
+
return (q) => {
|
|
386
|
+
for (const [boundType, field, value] of range) {
|
|
387
|
+
q = q[boundType](field, value);
|
|
388
|
+
}
|
|
389
|
+
return q;
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function getIndexFields(table, index, schema) {
|
|
393
|
+
const indexDescriptor = String(index ?? "by_creation_time");
|
|
394
|
+
if (indexDescriptor === "by_creation_time") {
|
|
395
|
+
return ["_creationTime", "_id"];
|
|
396
|
+
}
|
|
397
|
+
if (indexDescriptor === "by_id") {
|
|
398
|
+
return ["_id"];
|
|
399
|
+
}
|
|
400
|
+
if (!schema) {
|
|
401
|
+
throw new Error("schema is required to infer index fields");
|
|
402
|
+
}
|
|
403
|
+
const tableInfo = schema.tables[table];
|
|
404
|
+
const indexInfo = tableInfo.indexes.find((index2) => index2.indexDescriptor === indexDescriptor);
|
|
405
|
+
if (!indexInfo) {
|
|
406
|
+
throw new Error(`Index ${indexDescriptor} not found in table ${table}`);
|
|
407
|
+
}
|
|
408
|
+
const fields = indexInfo.fields.slice();
|
|
409
|
+
fields.push("_creationTime");
|
|
410
|
+
fields.push("_id");
|
|
411
|
+
return fields;
|
|
412
|
+
}
|
|
413
|
+
function getIndexKey(doc, indexFields) {
|
|
414
|
+
const key = [];
|
|
415
|
+
for (const field of indexFields) {
|
|
416
|
+
let obj = doc;
|
|
417
|
+
for (const subfield of field.split(".")) {
|
|
418
|
+
obj = obj[subfield];
|
|
419
|
+
}
|
|
420
|
+
key.push(obj);
|
|
421
|
+
}
|
|
422
|
+
return key;
|
|
423
|
+
}
|
|
424
|
+
function stream(db, schema) {
|
|
425
|
+
return new StreamDatabaseReader(db, schema);
|
|
426
|
+
}
|
|
427
|
+
var QueryStream = class {
|
|
428
|
+
/// Methods for creating new streams as modifications of the current stream.
|
|
429
|
+
/**
|
|
430
|
+
* Create a new stream with a TypeScript filter applied.
|
|
431
|
+
*
|
|
432
|
+
* This is similar to `db.query(tableName).filter(predicate)`, but it's more
|
|
433
|
+
* general because it can call arbitrary TypeScript code, including more
|
|
434
|
+
* database queries.
|
|
435
|
+
*
|
|
436
|
+
* All documents filtered out are still considered "read" from the database;
|
|
437
|
+
* they are just excluded from the output stream.
|
|
438
|
+
*
|
|
439
|
+
* In contrast to `filter` from convex-helpers/server/filter, this filterWith
|
|
440
|
+
* is applied *before* any pagination. That means if the filter excludes a lot
|
|
441
|
+
* of documents, the `.paginate()` method will read a lot of documents until
|
|
442
|
+
* it gets as many documents as it wants. If you run into issues with reading
|
|
443
|
+
* too much data, you can pass `maximumRowsRead` to `paginate()`.
|
|
444
|
+
*/
|
|
445
|
+
filterWith(predicate) {
|
|
446
|
+
const order = this.getOrder();
|
|
447
|
+
return new FlatMapStream(this, async (doc) => {
|
|
448
|
+
const filtered = await predicate(doc) ? doc : null;
|
|
449
|
+
return new SingletonStream(filtered, order, [], [], []);
|
|
450
|
+
}, []);
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Create a new stream where each element is the result of applying the mapper
|
|
454
|
+
* function to the elements of the original stream.
|
|
455
|
+
*
|
|
456
|
+
* Similar to how [1, 2, 3].map(x => x * 2) => [2, 4, 6]
|
|
457
|
+
*/
|
|
458
|
+
map(mapper) {
|
|
459
|
+
const order = this.getOrder();
|
|
460
|
+
return new FlatMapStream(this, async (doc) => {
|
|
461
|
+
const mapped = await mapper(doc);
|
|
462
|
+
return new SingletonStream(mapped, order, [], [], []);
|
|
463
|
+
}, []);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Similar to flatMap on an array, but iterate over a stream, and the for each
|
|
467
|
+
* element, iterate over the stream created by the mapper function.
|
|
468
|
+
*
|
|
469
|
+
* Ordered by the original stream order, then the mapped stream. Similar to
|
|
470
|
+
* how ["a", "b"].flatMap(letter => [letter, letter]) => ["a", "a", "b", "b"]
|
|
471
|
+
*
|
|
472
|
+
* @param mapper A function that takes a document and returns a new stream.
|
|
473
|
+
* @param mappedIndexFields The index fields of the streams created by mapper.
|
|
474
|
+
* @returns A stream of documents returned by the mapper streams,
|
|
475
|
+
* grouped by the documents in the original stream.
|
|
476
|
+
*/
|
|
477
|
+
flatMap(mapper, mappedIndexFields) {
|
|
478
|
+
normalizeIndexFields(mappedIndexFields);
|
|
479
|
+
return new FlatMapStream(this, mapper, mappedIndexFields);
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Get the first item from the original stream for each distinct value of the
|
|
483
|
+
* selected index fields.
|
|
484
|
+
*
|
|
485
|
+
* e.g. if the stream has an equality filter on `a`, and index fields `[a, b, c]`,
|
|
486
|
+
* we can do `stream.distinct(["b"])` to get a stream of the first item for
|
|
487
|
+
* each distinct value of `b`.
|
|
488
|
+
* Similarly, you could do `stream.distinct(["a", "b"])` with the same result,
|
|
489
|
+
* or `stream.distinct(["a", "b", "c"])` to get the original stream.
|
|
490
|
+
*
|
|
491
|
+
* This stream efficiently skips past items with the same value for the selected
|
|
492
|
+
* distinct index fields.
|
|
493
|
+
*
|
|
494
|
+
* This can be used to perform a loose index scan.
|
|
495
|
+
*/
|
|
496
|
+
distinct(distinctIndexFields) {
|
|
497
|
+
return new DistinctStream(this, distinctIndexFields);
|
|
498
|
+
}
|
|
499
|
+
/// Implementation of OrderedQuery
|
|
500
|
+
filter(_predicate) {
|
|
501
|
+
throw new Error("Cannot call .filter() directly on a query stream. Use .filterWith() for filtering or .collect() if you want to convert the stream to an array first.");
|
|
502
|
+
}
|
|
503
|
+
async paginate(opts) {
|
|
504
|
+
if (opts.numItems === 0) {
|
|
505
|
+
if (opts.cursor === null) {
|
|
506
|
+
throw new Error(".paginate called with cursor of null and 0 for numItems. This is not supported, as null is not a valid continueCursor. Advice: avoid calling paginate entirely in these cases.");
|
|
507
|
+
}
|
|
508
|
+
return {
|
|
509
|
+
page: [],
|
|
510
|
+
isDone: false,
|
|
511
|
+
continueCursor: opts.cursor
|
|
46
512
|
};
|
|
47
|
-
continue;
|
|
48
513
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
...admin && { admin }
|
|
514
|
+
const order = this.getOrder();
|
|
515
|
+
let newStartKey = {
|
|
516
|
+
key: [],
|
|
517
|
+
inclusive: true
|
|
518
|
+
};
|
|
519
|
+
if (opts.cursor !== null) {
|
|
520
|
+
newStartKey = {
|
|
521
|
+
key: deserializeCursor(opts.cursor),
|
|
522
|
+
inclusive: false
|
|
59
523
|
};
|
|
60
|
-
continue;
|
|
61
524
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
525
|
+
let newEndKey = {
|
|
526
|
+
key: [],
|
|
527
|
+
inclusive: true
|
|
528
|
+
};
|
|
529
|
+
const maxRowsToRead = opts.maximumRowsRead;
|
|
530
|
+
let maxRows = opts.numItems;
|
|
531
|
+
if (opts.endCursor) {
|
|
532
|
+
newEndKey = {
|
|
533
|
+
key: deserializeCursor(opts.endCursor),
|
|
534
|
+
inclusive: true
|
|
535
|
+
};
|
|
536
|
+
maxRows = void 0;
|
|
537
|
+
}
|
|
538
|
+
const newLowerBound = order === "asc" ? newStartKey : newEndKey;
|
|
539
|
+
const newUpperBound = order === "asc" ? newEndKey : newStartKey;
|
|
540
|
+
const narrowStream = this.narrow({
|
|
541
|
+
lowerBound: newLowerBound.key,
|
|
542
|
+
lowerBoundInclusive: newLowerBound.inclusive,
|
|
543
|
+
upperBound: newUpperBound.key,
|
|
544
|
+
upperBoundInclusive: newUpperBound.inclusive
|
|
545
|
+
});
|
|
546
|
+
const page = [];
|
|
547
|
+
const indexKeys = [];
|
|
548
|
+
let hasMore = opts.endCursor && opts.endCursor !== "[]";
|
|
549
|
+
let continueCursor = opts.endCursor ?? "[]";
|
|
550
|
+
const maxBytesToRead = opts.maximumBytesRead;
|
|
551
|
+
const trackBandwidth = maxBytesToRead !== void 0;
|
|
552
|
+
let totalBytesRead = 0;
|
|
553
|
+
let hitLimit = false;
|
|
554
|
+
for await (const [doc, indexKey, bandwidth] of narrowStream.iterWithKeys(trackBandwidth)) {
|
|
555
|
+
if (doc !== null) {
|
|
556
|
+
page.push(doc);
|
|
557
|
+
}
|
|
558
|
+
indexKeys.push(indexKey);
|
|
559
|
+
totalBytesRead += bandwidth;
|
|
560
|
+
if (maxBytesToRead !== void 0 && totalBytesRead >= maxBytesToRead || maxRowsToRead !== void 0 && indexKeys.length >= maxRowsToRead) {
|
|
561
|
+
hitLimit = true;
|
|
562
|
+
}
|
|
563
|
+
if (hitLimit || maxRows !== void 0 && page.length >= maxRows) {
|
|
564
|
+
hasMore = true;
|
|
565
|
+
continueCursor = serializeCursor(indexKey);
|
|
70
566
|
break;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
let pageStatus = void 0;
|
|
570
|
+
let splitCursor = void 0;
|
|
571
|
+
if (hitLimit) {
|
|
572
|
+
pageStatus = "SplitRequired";
|
|
573
|
+
splitCursor = indexKeys[Math.floor((indexKeys.length - 1) / 2)];
|
|
574
|
+
} else if (indexKeys.length >= SOFT_MAX_SCAN_LEN || page.length > opts.numItems + 1) {
|
|
575
|
+
pageStatus = "SplitRecommended";
|
|
576
|
+
splitCursor = indexKeys[Math.floor((indexKeys.length - 1) / 2)];
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
page,
|
|
580
|
+
isDone: !hasMore,
|
|
581
|
+
continueCursor,
|
|
582
|
+
pageStatus,
|
|
583
|
+
splitCursor: splitCursor ? serializeCursor(splitCursor) : void 0
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
async collect() {
|
|
587
|
+
return await this.take(Infinity);
|
|
588
|
+
}
|
|
589
|
+
async take(n) {
|
|
590
|
+
const results = [];
|
|
591
|
+
for await (const [doc] of this.iterWithKeys()) {
|
|
592
|
+
if (doc === null) {
|
|
593
|
+
continue;
|
|
594
|
+
}
|
|
595
|
+
results.push(doc);
|
|
596
|
+
if (results.length === n) {
|
|
78
597
|
break;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return results;
|
|
601
|
+
}
|
|
602
|
+
async unique() {
|
|
603
|
+
const docs = await this.take(2);
|
|
604
|
+
if (docs.length === 2) {
|
|
605
|
+
throw new Error("Query is not unique");
|
|
606
|
+
}
|
|
607
|
+
return docs[0] ?? null;
|
|
608
|
+
}
|
|
609
|
+
async first() {
|
|
610
|
+
const docs = await this.take(1);
|
|
611
|
+
return docs[0] ?? null;
|
|
612
|
+
}
|
|
613
|
+
[Symbol.asyncIterator]() {
|
|
614
|
+
const iterator = this.iterWithKeys()[Symbol.asyncIterator]();
|
|
615
|
+
return {
|
|
616
|
+
async next() {
|
|
617
|
+
const result = await iterator.next();
|
|
618
|
+
if (result.done) {
|
|
619
|
+
return { done: true, value: void 0 };
|
|
620
|
+
}
|
|
621
|
+
return { done: false, value: result.value[0] };
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
};
|
|
626
|
+
var StreamDatabaseReader = class {
|
|
627
|
+
db;
|
|
628
|
+
schema;
|
|
629
|
+
// TODO: support system tables
|
|
630
|
+
system;
|
|
631
|
+
constructor(db, schema) {
|
|
632
|
+
this.db = db;
|
|
633
|
+
this.schema = schema;
|
|
634
|
+
this.system = db.system;
|
|
635
|
+
}
|
|
636
|
+
query(tableName) {
|
|
637
|
+
return new StreamQueryInitializer(this, tableName);
|
|
638
|
+
}
|
|
639
|
+
get(_id) {
|
|
640
|
+
throw new Error("get() not supported for `paginator`");
|
|
641
|
+
}
|
|
642
|
+
normalizeId(_tableName, _id) {
|
|
643
|
+
throw new Error("normalizeId() not supported for `paginator`.");
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
var StreamableQuery = class extends QueryStream {
|
|
647
|
+
};
|
|
648
|
+
var StreamQueryInitializer = class extends StreamableQuery {
|
|
649
|
+
parent;
|
|
650
|
+
table;
|
|
651
|
+
constructor(parent, table) {
|
|
652
|
+
super();
|
|
653
|
+
this.parent = parent;
|
|
654
|
+
this.table = table;
|
|
655
|
+
}
|
|
656
|
+
fullTableScan() {
|
|
657
|
+
return this.withIndex("by_creation_time");
|
|
658
|
+
}
|
|
659
|
+
withIndex(indexName, indexRange) {
|
|
660
|
+
const indexFields = getIndexFields(this.table, indexName, this.parent.schema);
|
|
661
|
+
const q = new ReflectIndexRange(indexFields);
|
|
662
|
+
if (indexRange) {
|
|
663
|
+
indexRange(q);
|
|
664
|
+
}
|
|
665
|
+
return new StreamQuery(this, indexName, q, indexRange);
|
|
666
|
+
}
|
|
667
|
+
withSearchIndex(_indexName, _searchFilter) {
|
|
668
|
+
throw new Error("Cannot paginate withSearchIndex");
|
|
669
|
+
}
|
|
670
|
+
inner() {
|
|
671
|
+
return this.fullTableScan();
|
|
672
|
+
}
|
|
673
|
+
order(order) {
|
|
674
|
+
return this.inner().order(order);
|
|
675
|
+
}
|
|
676
|
+
reflect() {
|
|
677
|
+
return this.inner().reflect();
|
|
678
|
+
}
|
|
679
|
+
iterWithKeys(trackBandwidth = false) {
|
|
680
|
+
return this.inner().iterWithKeys(trackBandwidth);
|
|
681
|
+
}
|
|
682
|
+
getOrder() {
|
|
683
|
+
return this.inner().getOrder();
|
|
684
|
+
}
|
|
685
|
+
getEqualityIndexFilter() {
|
|
686
|
+
return this.inner().getEqualityIndexFilter();
|
|
687
|
+
}
|
|
688
|
+
getIndexFields() {
|
|
689
|
+
return this.inner().getIndexFields();
|
|
690
|
+
}
|
|
691
|
+
narrow(indexBounds) {
|
|
692
|
+
return this.inner().narrow(indexBounds);
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
var StreamQuery = class extends StreamableQuery {
|
|
696
|
+
parent;
|
|
697
|
+
index;
|
|
698
|
+
q;
|
|
699
|
+
indexRange;
|
|
700
|
+
constructor(parent, index, q, indexRange) {
|
|
701
|
+
super();
|
|
702
|
+
this.parent = parent;
|
|
703
|
+
this.index = index;
|
|
704
|
+
this.q = q;
|
|
705
|
+
this.indexRange = indexRange;
|
|
706
|
+
}
|
|
707
|
+
order(order) {
|
|
708
|
+
return new OrderedStreamQuery(this, order);
|
|
709
|
+
}
|
|
710
|
+
inner() {
|
|
711
|
+
return this.order("asc");
|
|
712
|
+
}
|
|
713
|
+
reflect() {
|
|
714
|
+
return this.inner().reflect();
|
|
715
|
+
}
|
|
716
|
+
iterWithKeys(trackBandwidth = false) {
|
|
717
|
+
return this.inner().iterWithKeys(trackBandwidth);
|
|
718
|
+
}
|
|
719
|
+
getOrder() {
|
|
720
|
+
return this.inner().getOrder();
|
|
721
|
+
}
|
|
722
|
+
getEqualityIndexFilter() {
|
|
723
|
+
return this.inner().getEqualityIndexFilter();
|
|
724
|
+
}
|
|
725
|
+
getIndexFields() {
|
|
726
|
+
return this.inner().getIndexFields();
|
|
727
|
+
}
|
|
728
|
+
narrow(indexBounds) {
|
|
729
|
+
return this.inner().narrow(indexBounds);
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
var OrderedStreamQuery = class extends StreamableQuery {
|
|
733
|
+
parent;
|
|
734
|
+
order;
|
|
735
|
+
constructor(parent, order) {
|
|
736
|
+
super();
|
|
737
|
+
this.parent = parent;
|
|
738
|
+
this.order = order;
|
|
739
|
+
}
|
|
740
|
+
reflect() {
|
|
741
|
+
return {
|
|
742
|
+
db: this.parent.parent.parent.db,
|
|
743
|
+
schema: this.parent.parent.parent.schema,
|
|
744
|
+
table: this.parent.parent.table,
|
|
745
|
+
index: this.parent.index,
|
|
746
|
+
indexFields: this.parent.q.indexFields,
|
|
747
|
+
order: this.order,
|
|
748
|
+
bounds: {
|
|
749
|
+
lowerBound: this.parent.q.lowerBoundIndexKey ?? [],
|
|
750
|
+
lowerBoundInclusive: this.parent.q.lowerBoundInclusive,
|
|
751
|
+
upperBound: this.parent.q.upperBoundIndexKey ?? [],
|
|
752
|
+
upperBoundInclusive: this.parent.q.upperBoundInclusive
|
|
753
|
+
},
|
|
754
|
+
indexRange: this.parent.indexRange
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* inner() is as if you had used ctx.db to construct the query.
|
|
759
|
+
*/
|
|
760
|
+
inner() {
|
|
761
|
+
const { db, table, index, order, indexRange } = this.reflect();
|
|
762
|
+
return db.query(table).withIndex(index, indexRange).order(order);
|
|
763
|
+
}
|
|
764
|
+
iterWithKeys(trackBandwidth = false) {
|
|
765
|
+
const { indexFields } = this.reflect();
|
|
766
|
+
const iterable = this.inner();
|
|
767
|
+
return {
|
|
768
|
+
[Symbol.asyncIterator]() {
|
|
769
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
770
|
+
return {
|
|
771
|
+
async next() {
|
|
772
|
+
const result = await iterator.next();
|
|
773
|
+
if (result.done) {
|
|
774
|
+
return { done: true, value: void 0 };
|
|
775
|
+
}
|
|
776
|
+
const doc = result.value;
|
|
777
|
+
const bandwidth = trackBandwidth ? getDocumentSize(doc) : 0;
|
|
778
|
+
return {
|
|
779
|
+
done: false,
|
|
780
|
+
value: [doc, getIndexKey(doc, indexFields), bandwidth]
|
|
781
|
+
};
|
|
782
|
+
}
|
|
85
783
|
};
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
getOrder() {
|
|
788
|
+
return this.order;
|
|
789
|
+
}
|
|
790
|
+
getEqualityIndexFilter() {
|
|
791
|
+
return this.parent.q.equalityIndexFilter;
|
|
792
|
+
}
|
|
793
|
+
getIndexFields() {
|
|
794
|
+
return this.parent.q.indexFields;
|
|
795
|
+
}
|
|
796
|
+
narrow(indexBounds) {
|
|
797
|
+
const { db, table, index, order, bounds, schema } = this.reflect();
|
|
798
|
+
let maxLowerBound = bounds.lowerBound;
|
|
799
|
+
let maxLowerBoundInclusive = bounds.lowerBoundInclusive;
|
|
800
|
+
if (compareKeys({
|
|
801
|
+
value: indexBounds.lowerBound,
|
|
802
|
+
kind: indexBounds.lowerBoundInclusive ? "predecessor" : "successor"
|
|
803
|
+
}, {
|
|
804
|
+
value: bounds.lowerBound,
|
|
805
|
+
kind: bounds.lowerBoundInclusive ? "predecessor" : "successor"
|
|
806
|
+
}) > 0) {
|
|
807
|
+
maxLowerBound = indexBounds.lowerBound;
|
|
808
|
+
maxLowerBoundInclusive = indexBounds.lowerBoundInclusive;
|
|
809
|
+
}
|
|
810
|
+
let minUpperBound = bounds.upperBound;
|
|
811
|
+
let minUpperBoundInclusive = bounds.upperBoundInclusive;
|
|
812
|
+
if (compareKeys({
|
|
813
|
+
value: indexBounds.upperBound,
|
|
814
|
+
kind: indexBounds.upperBoundInclusive ? "successor" : "predecessor"
|
|
815
|
+
}, {
|
|
816
|
+
value: bounds.upperBound,
|
|
817
|
+
kind: bounds.upperBoundInclusive ? "successor" : "predecessor"
|
|
818
|
+
}) < 0) {
|
|
819
|
+
minUpperBound = indexBounds.upperBound;
|
|
820
|
+
minUpperBoundInclusive = indexBounds.upperBoundInclusive;
|
|
821
|
+
}
|
|
822
|
+
return streamIndexRange(db, schema, table, index, {
|
|
823
|
+
lowerBound: maxLowerBound,
|
|
824
|
+
lowerBoundInclusive: maxLowerBoundInclusive,
|
|
825
|
+
upperBound: minUpperBound,
|
|
826
|
+
upperBoundInclusive: minUpperBoundInclusive
|
|
827
|
+
}, order);
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
function streamIndexRange(db, schema, table, index, bounds, order) {
|
|
831
|
+
const indexFields = getIndexFields(table, index, schema);
|
|
832
|
+
const splitBounds = splitRange(indexFields, order, bounds.lowerBound, bounds.upperBound, bounds.lowerBoundInclusive ? "gte" : "gt", bounds.upperBoundInclusive ? "lte" : "lt");
|
|
833
|
+
const subQueries = splitBounds.map((splitBound) => stream(db, schema).query(table).withIndex(index, rangeToQuery(splitBound)).order(order));
|
|
834
|
+
return new ConcatStreams(...subQueries);
|
|
835
|
+
}
|
|
836
|
+
var ReflectIndexRange = class {
|
|
837
|
+
indexFields;
|
|
838
|
+
#hasSuffix = false;
|
|
839
|
+
lowerBoundIndexKey = void 0;
|
|
840
|
+
lowerBoundInclusive = true;
|
|
841
|
+
upperBoundIndexKey = void 0;
|
|
842
|
+
upperBoundInclusive = true;
|
|
843
|
+
equalityIndexFilter = [];
|
|
844
|
+
constructor(indexFields) {
|
|
845
|
+
this.indexFields = indexFields;
|
|
846
|
+
}
|
|
847
|
+
eq(field, value) {
|
|
848
|
+
if (!this.#canLowerBound(field) || !this.#canUpperBound(field)) {
|
|
849
|
+
throw new Error(`Cannot use eq on field '${field}'`);
|
|
850
|
+
}
|
|
851
|
+
this.lowerBoundIndexKey = this.lowerBoundIndexKey ?? [];
|
|
852
|
+
this.lowerBoundIndexKey.push(value);
|
|
853
|
+
this.upperBoundIndexKey = this.upperBoundIndexKey ?? [];
|
|
854
|
+
this.upperBoundIndexKey.push(value);
|
|
855
|
+
this.equalityIndexFilter.push(value);
|
|
856
|
+
return this;
|
|
857
|
+
}
|
|
858
|
+
lt(field, value) {
|
|
859
|
+
if (!this.#canUpperBound(field)) {
|
|
860
|
+
throw new Error(`Cannot use lt on field '${field}'`);
|
|
861
|
+
}
|
|
862
|
+
this.upperBoundIndexKey = this.upperBoundIndexKey ?? [];
|
|
863
|
+
this.upperBoundIndexKey.push(value);
|
|
864
|
+
this.upperBoundInclusive = false;
|
|
865
|
+
this.#hasSuffix = true;
|
|
866
|
+
return this;
|
|
867
|
+
}
|
|
868
|
+
lte(field, value) {
|
|
869
|
+
if (!this.#canUpperBound(field)) {
|
|
870
|
+
throw new Error(`Cannot use lte on field '${field}'`);
|
|
871
|
+
}
|
|
872
|
+
this.upperBoundIndexKey = this.upperBoundIndexKey ?? [];
|
|
873
|
+
this.upperBoundIndexKey.push(value);
|
|
874
|
+
this.#hasSuffix = true;
|
|
875
|
+
return this;
|
|
876
|
+
}
|
|
877
|
+
gt(field, value) {
|
|
878
|
+
if (!this.#canLowerBound(field)) {
|
|
879
|
+
throw new Error(`Cannot use gt on field '${field}'`);
|
|
880
|
+
}
|
|
881
|
+
this.lowerBoundIndexKey = this.lowerBoundIndexKey ?? [];
|
|
882
|
+
this.lowerBoundIndexKey.push(value);
|
|
883
|
+
this.lowerBoundInclusive = false;
|
|
884
|
+
this.#hasSuffix = true;
|
|
885
|
+
return this;
|
|
886
|
+
}
|
|
887
|
+
gte(field, value) {
|
|
888
|
+
if (!this.#canLowerBound(field)) {
|
|
889
|
+
throw new Error(`Cannot use gte on field '${field}'`);
|
|
890
|
+
}
|
|
891
|
+
this.lowerBoundIndexKey = this.lowerBoundIndexKey ?? [];
|
|
892
|
+
this.lowerBoundIndexKey.push(value);
|
|
893
|
+
this.#hasSuffix = true;
|
|
894
|
+
return this;
|
|
895
|
+
}
|
|
896
|
+
#canLowerBound(field) {
|
|
897
|
+
const currentLowerBoundLength = this.lowerBoundIndexKey?.length ?? 0;
|
|
898
|
+
const currentUpperBoundLength = this.upperBoundIndexKey?.length ?? 0;
|
|
899
|
+
if (currentLowerBoundLength > currentUpperBoundLength) {
|
|
900
|
+
return false;
|
|
901
|
+
}
|
|
902
|
+
if (currentLowerBoundLength === currentUpperBoundLength && this.#hasSuffix) {
|
|
903
|
+
return false;
|
|
904
|
+
}
|
|
905
|
+
return currentLowerBoundLength < this.indexFields.length && this.indexFields[currentLowerBoundLength] === field;
|
|
906
|
+
}
|
|
907
|
+
#canUpperBound(field) {
|
|
908
|
+
const currentLowerBoundLength = this.lowerBoundIndexKey?.length ?? 0;
|
|
909
|
+
const currentUpperBoundLength = this.upperBoundIndexKey?.length ?? 0;
|
|
910
|
+
if (currentUpperBoundLength > currentLowerBoundLength) {
|
|
911
|
+
return false;
|
|
912
|
+
}
|
|
913
|
+
if (currentLowerBoundLength === currentUpperBoundLength && this.#hasSuffix) {
|
|
914
|
+
return false;
|
|
915
|
+
}
|
|
916
|
+
return currentUpperBoundLength < this.indexFields.length && this.indexFields[currentUpperBoundLength] === field;
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
function mergedStream(streams, orderByIndexFields) {
|
|
920
|
+
return new MergedStream(streams, orderByIndexFields);
|
|
921
|
+
}
|
|
922
|
+
var MergedStream = class _MergedStream extends QueryStream {
|
|
923
|
+
#order;
|
|
924
|
+
#streams;
|
|
925
|
+
#equalityIndexFilter;
|
|
926
|
+
#indexFields;
|
|
927
|
+
constructor(streams, orderByIndexFields) {
|
|
928
|
+
super();
|
|
929
|
+
if (streams.length === 0) {
|
|
930
|
+
throw new Error("Cannot union empty array of streams");
|
|
931
|
+
}
|
|
932
|
+
this.#order = allSame(streams.map((stream2) => stream2.getOrder()), "Cannot merge streams with different orders");
|
|
933
|
+
this.#streams = streams.map((stream2) => new OrderByStream(stream2, orderByIndexFields));
|
|
934
|
+
this.#indexFields = allSame(this.#streams.map((stream2) => stream2.getIndexFields()), "Cannot merge streams with different index fields. Consider using .orderBy()");
|
|
935
|
+
this.#equalityIndexFilter = commonPrefix(this.#streams.map((stream2) => stream2.getEqualityIndexFilter()));
|
|
936
|
+
}
|
|
937
|
+
iterWithKeys(trackBandwidth = false) {
|
|
938
|
+
const iterables = this.#streams.map((stream2) => stream2.iterWithKeys(trackBandwidth));
|
|
939
|
+
const comparisonInversion = this.#order === "asc" ? 1 : -1;
|
|
940
|
+
return {
|
|
941
|
+
[Symbol.asyncIterator]() {
|
|
942
|
+
const iterators = iterables.map((iterable) => iterable[Symbol.asyncIterator]());
|
|
943
|
+
const results = Array(iterators.length);
|
|
944
|
+
const pendingBandwidth = Array(iterators.length).fill(0);
|
|
945
|
+
return {
|
|
946
|
+
async next() {
|
|
947
|
+
let bandwidthThisIteration = 0;
|
|
948
|
+
await Promise.all(iterators.map(async (iterator, i) => {
|
|
949
|
+
if (!results[i]) {
|
|
950
|
+
const result = await iterator.next();
|
|
951
|
+
results[i] = result;
|
|
952
|
+
if (trackBandwidth && !result.done && result.value) {
|
|
953
|
+
pendingBandwidth[i] = result.value[2];
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}));
|
|
957
|
+
if (trackBandwidth) {
|
|
958
|
+
for (let i = 0; i < pendingBandwidth.length; i++) {
|
|
959
|
+
bandwidthThisIteration += pendingBandwidth[i];
|
|
960
|
+
pendingBandwidth[i] = 0;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
let minIndexKeyAndIndex = void 0;
|
|
964
|
+
for (let i = 0; i < results.length; i++) {
|
|
965
|
+
const result = results[i];
|
|
966
|
+
if (result.done || !result.value) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
const [_2, resultIndexKey] = result.value;
|
|
970
|
+
if (minIndexKeyAndIndex === void 0) {
|
|
971
|
+
minIndexKeyAndIndex = [resultIndexKey, i];
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
const [prevMin] = minIndexKeyAndIndex;
|
|
975
|
+
if (compareKeys({ value: resultIndexKey, kind: "exact" }, { value: prevMin, kind: "exact" }) * comparisonInversion < 0) {
|
|
976
|
+
minIndexKeyAndIndex = [resultIndexKey, i];
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
if (minIndexKeyAndIndex === void 0) {
|
|
980
|
+
return { done: true, value: void 0 };
|
|
981
|
+
}
|
|
982
|
+
const [_, minIndex] = minIndexKeyAndIndex;
|
|
983
|
+
const [doc, indexKey] = results[minIndex].value;
|
|
984
|
+
results[minIndex] = void 0;
|
|
985
|
+
return {
|
|
986
|
+
done: false,
|
|
987
|
+
value: [doc, indexKey, bandwidthThisIteration]
|
|
988
|
+
};
|
|
989
|
+
}
|
|
93
990
|
};
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
getOrder() {
|
|
995
|
+
return this.#order;
|
|
996
|
+
}
|
|
997
|
+
getEqualityIndexFilter() {
|
|
998
|
+
return this.#equalityIndexFilter;
|
|
999
|
+
}
|
|
1000
|
+
getIndexFields() {
|
|
1001
|
+
return this.#indexFields;
|
|
1002
|
+
}
|
|
1003
|
+
narrow(indexBounds) {
|
|
1004
|
+
return new _MergedStream(this.#streams.map((stream2) => stream2.narrow(indexBounds)), this.#indexFields);
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
function allSame(values, errorMessage) {
|
|
1008
|
+
const first = values[0];
|
|
1009
|
+
for (const value of values) {
|
|
1010
|
+
if (compareValues(value, first)) {
|
|
1011
|
+
throw new Error(errorMessage);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return first;
|
|
1015
|
+
}
|
|
1016
|
+
function commonPrefix(values) {
|
|
1017
|
+
let commonPrefix2 = values[0];
|
|
1018
|
+
for (const value of values) {
|
|
1019
|
+
for (let i = 0; i < commonPrefix2.length; i++) {
|
|
1020
|
+
if (i >= value.length || compareValues(commonPrefix2[i], value[i])) {
|
|
1021
|
+
commonPrefix2 = commonPrefix2.slice(0, i);
|
|
94
1022
|
break;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return commonPrefix2;
|
|
1027
|
+
}
|
|
1028
|
+
var ConcatStreams = class _ConcatStreams extends QueryStream {
|
|
1029
|
+
#order;
|
|
1030
|
+
#streams;
|
|
1031
|
+
#equalityIndexFilter;
|
|
1032
|
+
#indexFields;
|
|
1033
|
+
constructor(...streams) {
|
|
1034
|
+
super();
|
|
1035
|
+
this.#streams = streams;
|
|
1036
|
+
if (streams.length === 0) {
|
|
1037
|
+
throw new Error("Cannot concat empty array of streams");
|
|
1038
|
+
}
|
|
1039
|
+
this.#order = allSame(streams.map((stream2) => stream2.getOrder()), "Cannot concat streams with different orders. Consider using .orderBy()");
|
|
1040
|
+
this.#indexFields = allSame(streams.map((stream2) => stream2.getIndexFields()), "Cannot concat streams with different index fields. Consider using .orderBy()");
|
|
1041
|
+
this.#equalityIndexFilter = commonPrefix(streams.map((stream2) => stream2.getEqualityIndexFilter()));
|
|
1042
|
+
}
|
|
1043
|
+
iterWithKeys(trackBandwidth = false) {
|
|
1044
|
+
const iterables = this.#streams.map((stream2) => stream2.iterWithKeys(trackBandwidth));
|
|
1045
|
+
const comparisonInversion = this.#order === "asc" ? 1 : -1;
|
|
1046
|
+
let previousIndexKey = void 0;
|
|
1047
|
+
return {
|
|
1048
|
+
[Symbol.asyncIterator]() {
|
|
1049
|
+
const iterators = iterables.map((iterable) => iterable[Symbol.asyncIterator]());
|
|
1050
|
+
return {
|
|
1051
|
+
async next() {
|
|
1052
|
+
while (iterators.length > 0) {
|
|
1053
|
+
const result = await iterators[0].next();
|
|
1054
|
+
if (result.done) {
|
|
1055
|
+
iterators.shift();
|
|
1056
|
+
} else {
|
|
1057
|
+
const [_, indexKey] = result.value;
|
|
1058
|
+
if (previousIndexKey !== void 0 && compareKeys({
|
|
1059
|
+
value: previousIndexKey,
|
|
1060
|
+
kind: "exact"
|
|
1061
|
+
}, {
|
|
1062
|
+
value: indexKey,
|
|
1063
|
+
kind: "exact"
|
|
1064
|
+
}) * comparisonInversion > 0) {
|
|
1065
|
+
throw new Error(`ConcatStreams in wrong order: ${JSON.stringify(previousIndexKey)}, ${JSON.stringify(indexKey)}`);
|
|
1066
|
+
}
|
|
1067
|
+
previousIndexKey = indexKey;
|
|
1068
|
+
return result;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
return { done: true, value: void 0 };
|
|
1072
|
+
}
|
|
100
1073
|
};
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
getOrder() {
|
|
1078
|
+
return this.#order;
|
|
1079
|
+
}
|
|
1080
|
+
getEqualityIndexFilter() {
|
|
1081
|
+
return this.#equalityIndexFilter;
|
|
1082
|
+
}
|
|
1083
|
+
getIndexFields() {
|
|
1084
|
+
return this.#indexFields;
|
|
1085
|
+
}
|
|
1086
|
+
narrow(indexBounds) {
|
|
1087
|
+
return new _ConcatStreams(...this.#streams.map((stream2) => stream2.narrow(indexBounds)));
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
var FlatMapStreamIterator = class {
|
|
1091
|
+
#outerStream;
|
|
1092
|
+
#outerIterator;
|
|
1093
|
+
#currentOuterItem = null;
|
|
1094
|
+
#mapper;
|
|
1095
|
+
#mappedIndexFields;
|
|
1096
|
+
#trackBandwidth;
|
|
1097
|
+
constructor(outerStream, mapper, mappedIndexFields, trackBandwidth) {
|
|
1098
|
+
this.#outerIterator = outerStream.iterWithKeys(trackBandwidth)[Symbol.asyncIterator]();
|
|
1099
|
+
this.#outerStream = outerStream;
|
|
1100
|
+
this.#mapper = mapper;
|
|
1101
|
+
this.#mappedIndexFields = mappedIndexFields;
|
|
1102
|
+
this.#trackBandwidth = trackBandwidth;
|
|
1103
|
+
}
|
|
1104
|
+
singletonSkipInnerStream() {
|
|
1105
|
+
const indexKey = this.#mappedIndexFields.map(() => null);
|
|
1106
|
+
return new SingletonStream(null, this.#outerStream.getOrder(), this.#mappedIndexFields, indexKey, indexKey);
|
|
1107
|
+
}
|
|
1108
|
+
async setCurrentOuterItem(item) {
|
|
1109
|
+
const [t, indexKey, bandwidth] = item;
|
|
1110
|
+
let innerStream;
|
|
1111
|
+
if (t === null) {
|
|
1112
|
+
innerStream = this.singletonSkipInnerStream();
|
|
1113
|
+
} else {
|
|
1114
|
+
innerStream = await this.#mapper(t);
|
|
1115
|
+
if (!equalIndexFields(innerStream.getIndexFields(), this.#mappedIndexFields)) {
|
|
1116
|
+
throw new Error(`FlatMapStream: inner stream has different index fields than expected: ${JSON.stringify(innerStream.getIndexFields())} vs ${JSON.stringify(this.#mappedIndexFields)}`);
|
|
1117
|
+
}
|
|
1118
|
+
if (innerStream.getOrder() !== this.#outerStream.getOrder()) {
|
|
1119
|
+
throw new Error(`FlatMapStream: inner stream has different order than outer stream: ${innerStream.getOrder()} vs ${this.#outerStream.getOrder()}`);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
this.#currentOuterItem = {
|
|
1123
|
+
t,
|
|
1124
|
+
indexKey,
|
|
1125
|
+
innerIterator: innerStream.iterWithKeys(this.#trackBandwidth)[Symbol.asyncIterator](),
|
|
1126
|
+
count: 0,
|
|
1127
|
+
bandwidth
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
async next() {
|
|
1131
|
+
if (this.#currentOuterItem === null) {
|
|
1132
|
+
const result2 = await this.#outerIterator.next();
|
|
1133
|
+
if (result2.done) {
|
|
1134
|
+
return { done: true, value: void 0 };
|
|
1135
|
+
}
|
|
1136
|
+
await this.setCurrentOuterItem(result2.value);
|
|
1137
|
+
return await this.next();
|
|
1138
|
+
}
|
|
1139
|
+
const result = await this.#currentOuterItem.innerIterator.next();
|
|
1140
|
+
if (result.done) {
|
|
1141
|
+
if (this.#currentOuterItem.count > 0) {
|
|
1142
|
+
this.#currentOuterItem = null;
|
|
1143
|
+
} else {
|
|
1144
|
+
this.#currentOuterItem.innerIterator = this.singletonSkipInnerStream().iterWithKeys(this.#trackBandwidth)[Symbol.asyncIterator]();
|
|
1145
|
+
}
|
|
1146
|
+
return await this.next();
|
|
1147
|
+
}
|
|
1148
|
+
const [u, indexKey, innerBandwidth] = result.value;
|
|
1149
|
+
this.#currentOuterItem.count++;
|
|
1150
|
+
const fullIndexKey = [...this.#currentOuterItem.indexKey, ...indexKey];
|
|
1151
|
+
const bandwidth = (this.#currentOuterItem.count === 1 ? this.#currentOuterItem.bandwidth : 0) + innerBandwidth;
|
|
1152
|
+
return { done: false, value: [u, fullIndexKey, bandwidth] };
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
var FlatMapStream = class _FlatMapStream extends QueryStream {
|
|
1156
|
+
#stream;
|
|
1157
|
+
#mapper;
|
|
1158
|
+
#mappedIndexFields;
|
|
1159
|
+
constructor(stream2, mapper, mappedIndexFields) {
|
|
1160
|
+
super();
|
|
1161
|
+
this.#stream = stream2;
|
|
1162
|
+
this.#mapper = mapper;
|
|
1163
|
+
this.#mappedIndexFields = mappedIndexFields;
|
|
1164
|
+
}
|
|
1165
|
+
iterWithKeys(trackBandwidth = false) {
|
|
1166
|
+
const outerStream = this.#stream;
|
|
1167
|
+
const mapper = this.#mapper;
|
|
1168
|
+
const mappedIndexFields = this.#mappedIndexFields;
|
|
1169
|
+
return {
|
|
1170
|
+
[Symbol.asyncIterator]() {
|
|
1171
|
+
return new FlatMapStreamIterator(outerStream, mapper, mappedIndexFields, trackBandwidth);
|
|
1172
|
+
}
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1175
|
+
getOrder() {
|
|
1176
|
+
return this.#stream.getOrder();
|
|
1177
|
+
}
|
|
1178
|
+
getEqualityIndexFilter() {
|
|
1179
|
+
return this.#stream.getEqualityIndexFilter();
|
|
1180
|
+
}
|
|
1181
|
+
getIndexFields() {
|
|
1182
|
+
return [...this.#stream.getIndexFields(), ...this.#mappedIndexFields];
|
|
1183
|
+
}
|
|
1184
|
+
narrow(indexBounds) {
|
|
1185
|
+
const outerLength = this.#stream.getIndexFields().length;
|
|
1186
|
+
const outerLowerBound = indexBounds.lowerBound.slice(0, outerLength);
|
|
1187
|
+
const outerUpperBound = indexBounds.upperBound.slice(0, outerLength);
|
|
1188
|
+
const innerLowerBound = indexBounds.lowerBound.slice(outerLength);
|
|
1189
|
+
const innerUpperBound = indexBounds.upperBound.slice(outerLength);
|
|
1190
|
+
const outerIndexBounds = {
|
|
1191
|
+
lowerBound: outerLowerBound,
|
|
1192
|
+
lowerBoundInclusive: innerLowerBound.length === 0 ? indexBounds.lowerBoundInclusive : true,
|
|
1193
|
+
upperBound: outerUpperBound,
|
|
1194
|
+
upperBoundInclusive: innerUpperBound.length === 0 ? indexBounds.upperBoundInclusive : true
|
|
1195
|
+
};
|
|
1196
|
+
const innerIndexBounds = {
|
|
1197
|
+
lowerBound: innerLowerBound,
|
|
1198
|
+
lowerBoundInclusive: innerLowerBound.length === 0 ? true : indexBounds.lowerBoundInclusive,
|
|
1199
|
+
upperBound: innerUpperBound,
|
|
1200
|
+
upperBoundInclusive: innerUpperBound.length === 0 ? true : indexBounds.upperBoundInclusive
|
|
1201
|
+
};
|
|
1202
|
+
return new _FlatMapStream(this.#stream.narrow(outerIndexBounds), async (t) => {
|
|
1203
|
+
const innerStream = await this.#mapper(t);
|
|
1204
|
+
return innerStream.narrow(innerIndexBounds);
|
|
1205
|
+
}, this.#mappedIndexFields);
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
var SingletonStream = class _SingletonStream extends QueryStream {
|
|
1209
|
+
#value;
|
|
1210
|
+
#order;
|
|
1211
|
+
#indexFields;
|
|
1212
|
+
#indexKey;
|
|
1213
|
+
#equalityIndexFilter;
|
|
1214
|
+
#bandwidth;
|
|
1215
|
+
constructor(value, order = "asc", indexFields, indexKey, equalityIndexFilter, bandwidth = 0) {
|
|
1216
|
+
super();
|
|
1217
|
+
this.#value = value;
|
|
1218
|
+
this.#order = order;
|
|
1219
|
+
this.#indexFields = indexFields;
|
|
1220
|
+
this.#indexKey = indexKey;
|
|
1221
|
+
this.#equalityIndexFilter = equalityIndexFilter;
|
|
1222
|
+
this.#bandwidth = bandwidth;
|
|
1223
|
+
if (indexKey.length !== indexFields.length) {
|
|
1224
|
+
throw new Error(`indexKey must have the same length as indexFields: ${JSON.stringify(indexKey)} vs ${JSON.stringify(indexFields)}`);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
iterWithKeys(_trackBandwidth = false) {
|
|
1228
|
+
const value = this.#value;
|
|
1229
|
+
const indexKey = this.#indexKey;
|
|
1230
|
+
const bandwidth = this.#bandwidth;
|
|
1231
|
+
return {
|
|
1232
|
+
[Symbol.asyncIterator]() {
|
|
1233
|
+
let sent = false;
|
|
1234
|
+
return {
|
|
1235
|
+
async next() {
|
|
1236
|
+
if (sent) {
|
|
1237
|
+
return { done: true, value: void 0 };
|
|
1238
|
+
}
|
|
1239
|
+
sent = true;
|
|
1240
|
+
return {
|
|
1241
|
+
done: false,
|
|
1242
|
+
value: [value, indexKey, bandwidth]
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
108
1245
|
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
getOrder() {
|
|
1250
|
+
return this.#order;
|
|
1251
|
+
}
|
|
1252
|
+
getIndexFields() {
|
|
1253
|
+
return this.#indexFields;
|
|
1254
|
+
}
|
|
1255
|
+
getEqualityIndexFilter() {
|
|
1256
|
+
return this.#equalityIndexFilter;
|
|
1257
|
+
}
|
|
1258
|
+
narrow(indexBounds) {
|
|
1259
|
+
const compareLowerBound = compareKeys({
|
|
1260
|
+
value: indexBounds.lowerBound,
|
|
1261
|
+
kind: indexBounds.lowerBoundInclusive ? "exact" : "successor"
|
|
1262
|
+
}, {
|
|
1263
|
+
value: this.#indexKey,
|
|
1264
|
+
kind: "exact"
|
|
1265
|
+
});
|
|
1266
|
+
const compareUpperBound = compareKeys({
|
|
1267
|
+
value: this.#indexKey,
|
|
1268
|
+
kind: "exact"
|
|
1269
|
+
}, {
|
|
1270
|
+
value: indexBounds.upperBound,
|
|
1271
|
+
kind: indexBounds.upperBoundInclusive ? "exact" : "predecessor"
|
|
1272
|
+
});
|
|
1273
|
+
if (compareLowerBound <= 0 && compareUpperBound <= 0) {
|
|
1274
|
+
return new _SingletonStream(this.#value, this.#order, this.#indexFields, this.#indexKey, this.#equalityIndexFilter);
|
|
1275
|
+
}
|
|
1276
|
+
return new EmptyStream(this.#order, this.#indexFields);
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
var EmptyStream = class extends QueryStream {
|
|
1280
|
+
#order;
|
|
1281
|
+
#indexFields;
|
|
1282
|
+
constructor(order, indexFields) {
|
|
1283
|
+
super();
|
|
1284
|
+
this.#order = order;
|
|
1285
|
+
this.#indexFields = indexFields;
|
|
1286
|
+
}
|
|
1287
|
+
iterWithKeys(_trackBandwidth = false) {
|
|
1288
|
+
return {
|
|
1289
|
+
[Symbol.asyncIterator]() {
|
|
1290
|
+
return {
|
|
1291
|
+
async next() {
|
|
1292
|
+
return { done: true, value: void 0 };
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
getOrder() {
|
|
1299
|
+
return this.#order;
|
|
1300
|
+
}
|
|
1301
|
+
getIndexFields() {
|
|
1302
|
+
return this.#indexFields;
|
|
1303
|
+
}
|
|
1304
|
+
getEqualityIndexFilter() {
|
|
1305
|
+
return [];
|
|
1306
|
+
}
|
|
1307
|
+
narrow(_indexBounds) {
|
|
1308
|
+
return this;
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1311
|
+
function normalizeIndexFields(indexFields) {
|
|
1312
|
+
if (!indexFields.includes("_creationTime")) {
|
|
1313
|
+
if (indexFields.length !== 1 || indexFields[0] !== "_id") {
|
|
1314
|
+
indexFields.push("_creationTime");
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
if (!indexFields.includes("_id")) {
|
|
1318
|
+
indexFields.push("_id");
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
function* getOrderingIndexFields(stream2) {
|
|
1322
|
+
const streamEqualityIndexLength = stream2.getEqualityIndexFilter().length;
|
|
1323
|
+
const streamIndexFields = stream2.getIndexFields();
|
|
1324
|
+
for (let i = 0; i <= streamEqualityIndexLength; i++) {
|
|
1325
|
+
yield streamIndexFields.slice(i);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
var OrderByStream = class _OrderByStream extends QueryStream {
|
|
1329
|
+
#staticFilter;
|
|
1330
|
+
#stream;
|
|
1331
|
+
#indexFields;
|
|
1332
|
+
constructor(stream2, indexFields) {
|
|
1333
|
+
super();
|
|
1334
|
+
this.#stream = stream2;
|
|
1335
|
+
this.#indexFields = indexFields;
|
|
1336
|
+
normalizeIndexFields(this.#indexFields);
|
|
1337
|
+
const streamIndexFields = stream2.getIndexFields();
|
|
1338
|
+
const orderingIndexFields = Array.from(getOrderingIndexFields(stream2));
|
|
1339
|
+
if (!orderingIndexFields.some((orderingIndexFields2) => equalIndexFields(orderingIndexFields2, indexFields))) {
|
|
1340
|
+
throw new Error(`indexFields must be some sequence of fields the stream is ordered by: ${JSON.stringify(indexFields)}, ${JSON.stringify(streamIndexFields)} (${stream2.getEqualityIndexFilter().length} equality fields)`);
|
|
1341
|
+
}
|
|
1342
|
+
this.#staticFilter = stream2.getEqualityIndexFilter().slice(0, streamIndexFields.length - indexFields.length);
|
|
1343
|
+
}
|
|
1344
|
+
getOrder() {
|
|
1345
|
+
return this.#stream.getOrder();
|
|
1346
|
+
}
|
|
1347
|
+
getEqualityIndexFilter() {
|
|
1348
|
+
return this.#stream.getEqualityIndexFilter().slice(this.#staticFilter.length);
|
|
1349
|
+
}
|
|
1350
|
+
getIndexFields() {
|
|
1351
|
+
return this.#indexFields;
|
|
1352
|
+
}
|
|
1353
|
+
iterWithKeys(trackBandwidth = false) {
|
|
1354
|
+
const iterable = this.#stream.iterWithKeys(trackBandwidth);
|
|
1355
|
+
const staticFilter = this.#staticFilter;
|
|
1356
|
+
return {
|
|
1357
|
+
[Symbol.asyncIterator]() {
|
|
1358
|
+
const iterator = iterable[Symbol.asyncIterator]();
|
|
1359
|
+
return {
|
|
1360
|
+
async next() {
|
|
1361
|
+
const result = await iterator.next();
|
|
1362
|
+
if (result.done) {
|
|
1363
|
+
return result;
|
|
1364
|
+
}
|
|
1365
|
+
const [doc, indexKey, bandwidth] = result.value;
|
|
1366
|
+
return {
|
|
1367
|
+
done: false,
|
|
1368
|
+
value: [doc, indexKey.slice(staticFilter.length), bandwidth]
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
116
1371
|
};
|
|
1372
|
+
}
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
narrow(indexBounds) {
|
|
1376
|
+
return new _OrderByStream(this.#stream.narrow({
|
|
1377
|
+
lowerBound: [...this.#staticFilter, ...indexBounds.lowerBound],
|
|
1378
|
+
lowerBoundInclusive: indexBounds.lowerBoundInclusive,
|
|
1379
|
+
upperBound: [...this.#staticFilter, ...indexBounds.upperBound],
|
|
1380
|
+
upperBoundInclusive: indexBounds.upperBoundInclusive
|
|
1381
|
+
}), this.#indexFields);
|
|
1382
|
+
}
|
|
1383
|
+
};
|
|
1384
|
+
var DistinctStream = class _DistinctStream extends QueryStream {
|
|
1385
|
+
#distinctIndexFieldsLength;
|
|
1386
|
+
#stream;
|
|
1387
|
+
#distinctIndexFields;
|
|
1388
|
+
constructor(stream2, distinctIndexFields) {
|
|
1389
|
+
super();
|
|
1390
|
+
this.#stream = stream2;
|
|
1391
|
+
this.#distinctIndexFields = distinctIndexFields;
|
|
1392
|
+
let distinctIndexFieldsLength = void 0;
|
|
1393
|
+
for (const orderingIndexFields of getOrderingIndexFields(stream2)) {
|
|
1394
|
+
const prefix = orderingIndexFields.slice(0, distinctIndexFields.length);
|
|
1395
|
+
if (equalIndexFields(prefix, distinctIndexFields)) {
|
|
1396
|
+
const equalityLength = stream2.getIndexFields().length - orderingIndexFields.length;
|
|
1397
|
+
distinctIndexFieldsLength = equalityLength + distinctIndexFields.length;
|
|
117
1398
|
break;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
if (distinctIndexFieldsLength === void 0) {
|
|
1402
|
+
throw new Error(`distinctIndexFields must be a prefix of the stream's ordering index fields: ${JSON.stringify(distinctIndexFields)}, ${JSON.stringify(stream2.getIndexFields())} (${stream2.getEqualityIndexFilter().length} equality fields)`);
|
|
1403
|
+
}
|
|
1404
|
+
this.#distinctIndexFieldsLength = distinctIndexFieldsLength;
|
|
1405
|
+
}
|
|
1406
|
+
iterWithKeys(trackBandwidth = false) {
|
|
1407
|
+
const stream2 = this.#stream;
|
|
1408
|
+
const distinctIndexFieldsLength = this.#distinctIndexFieldsLength;
|
|
1409
|
+
return {
|
|
1410
|
+
[Symbol.asyncIterator]() {
|
|
1411
|
+
let currentStream = stream2;
|
|
1412
|
+
let currentIterator = currentStream.iterWithKeys(trackBandwidth)[Symbol.asyncIterator]();
|
|
1413
|
+
return {
|
|
1414
|
+
async next() {
|
|
1415
|
+
const result = await currentIterator.next();
|
|
1416
|
+
if (result.done) {
|
|
1417
|
+
return { done: true, value: void 0 };
|
|
1418
|
+
}
|
|
1419
|
+
const [doc, indexKey, bandwidth] = result.value;
|
|
1420
|
+
if (doc === null) {
|
|
1421
|
+
return {
|
|
1422
|
+
done: false,
|
|
1423
|
+
value: [null, indexKey, bandwidth]
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1426
|
+
const distinctIndexKey = indexKey.slice(0, distinctIndexFieldsLength);
|
|
1427
|
+
if (stream2.getOrder() === "asc") {
|
|
1428
|
+
currentStream = currentStream.narrow({
|
|
1429
|
+
lowerBound: distinctIndexKey,
|
|
1430
|
+
lowerBoundInclusive: false,
|
|
1431
|
+
upperBound: [],
|
|
1432
|
+
upperBoundInclusive: true
|
|
1433
|
+
});
|
|
1434
|
+
} else {
|
|
1435
|
+
currentStream = currentStream.narrow({
|
|
1436
|
+
lowerBound: [],
|
|
1437
|
+
lowerBoundInclusive: true,
|
|
1438
|
+
upperBound: distinctIndexKey,
|
|
1439
|
+
upperBoundInclusive: false
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
currentIterator = currentStream.iterWithKeys(trackBandwidth)[Symbol.asyncIterator]();
|
|
1443
|
+
return result;
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
narrow(indexBounds) {
|
|
1450
|
+
const indexBoundsPrefix = {
|
|
1451
|
+
...indexBounds,
|
|
1452
|
+
lowerBound: indexBounds.lowerBound.slice(0, this.#distinctIndexFieldsLength),
|
|
1453
|
+
upperBound: indexBounds.upperBound.slice(0, this.#distinctIndexFieldsLength)
|
|
1454
|
+
};
|
|
1455
|
+
return new _DistinctStream(this.#stream.narrow(indexBoundsPrefix), this.#distinctIndexFields);
|
|
1456
|
+
}
|
|
1457
|
+
getOrder() {
|
|
1458
|
+
return this.#stream.getOrder();
|
|
1459
|
+
}
|
|
1460
|
+
getIndexFields() {
|
|
1461
|
+
return this.#stream.getIndexFields();
|
|
1462
|
+
}
|
|
1463
|
+
getEqualityIndexFilter() {
|
|
1464
|
+
return this.#stream.getEqualityIndexFilter();
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
function equalIndexFields(indexFields1, indexFields2) {
|
|
1468
|
+
if (indexFields1.length !== indexFields2.length) {
|
|
1469
|
+
return false;
|
|
1470
|
+
}
|
|
1471
|
+
for (let i = 0; i < indexFields1.length; i++) {
|
|
1472
|
+
if (indexFields1[i] !== indexFields2[i]) {
|
|
1473
|
+
return false;
|
|
122
1474
|
}
|
|
123
1475
|
}
|
|
124
|
-
return
|
|
1476
|
+
return true;
|
|
1477
|
+
}
|
|
1478
|
+
function getValueAtIndex(v2, index) {
|
|
1479
|
+
if (index >= v2.length) {
|
|
1480
|
+
return void 0;
|
|
1481
|
+
}
|
|
1482
|
+
return { kind: "found", value: v2[index] };
|
|
1483
|
+
}
|
|
1484
|
+
function compareDanglingSuffix(shorterKeyKind, longerKeyKind, shorterKey, longerKey) {
|
|
1485
|
+
if (shorterKeyKind === "exact" && longerKeyKind === "exact") {
|
|
1486
|
+
throw new Error(`Exact keys are not the same length: ${JSON.stringify(shorterKey.value)}, ${JSON.stringify(longerKey.value)}`);
|
|
1487
|
+
}
|
|
1488
|
+
if (shorterKeyKind === "exact") {
|
|
1489
|
+
throw new Error(`Exact key is shorter than prefix: ${JSON.stringify(shorterKey.value)}, ${JSON.stringify(longerKey.value)}`);
|
|
1490
|
+
}
|
|
1491
|
+
if (shorterKeyKind === "predecessor" && longerKeyKind === "successor") {
|
|
1492
|
+
return -1;
|
|
1493
|
+
}
|
|
1494
|
+
if (shorterKeyKind === "successor" && longerKeyKind === "predecessor") {
|
|
1495
|
+
return 1;
|
|
1496
|
+
}
|
|
1497
|
+
if (shorterKeyKind === "predecessor" && longerKeyKind === "predecessor") {
|
|
1498
|
+
return -1;
|
|
1499
|
+
}
|
|
1500
|
+
if (shorterKeyKind === "successor" && longerKeyKind === "successor") {
|
|
1501
|
+
return 1;
|
|
1502
|
+
}
|
|
1503
|
+
if (shorterKeyKind === "predecessor" && longerKeyKind === "exact") {
|
|
1504
|
+
return -1;
|
|
1505
|
+
}
|
|
1506
|
+
if (shorterKeyKind === "successor" && longerKeyKind === "exact") {
|
|
1507
|
+
return 1;
|
|
1508
|
+
}
|
|
1509
|
+
throw new Error(`Unexpected key kinds: ${shorterKeyKind}, ${longerKeyKind}`);
|
|
125
1510
|
}
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
1511
|
+
function compareKeys(key1, key2) {
|
|
1512
|
+
let i = 0;
|
|
1513
|
+
while (i < Math.max(key1.value.length, key2.value.length)) {
|
|
1514
|
+
const v1 = getValueAtIndex(key1.value, i);
|
|
1515
|
+
const v2 = getValueAtIndex(key2.value, i);
|
|
1516
|
+
if (v1 === void 0) {
|
|
1517
|
+
return compareDanglingSuffix(key1.kind, key2.kind, key1, key2);
|
|
1518
|
+
}
|
|
1519
|
+
if (v2 === void 0) {
|
|
1520
|
+
return -1 * compareDanglingSuffix(key2.kind, key1.kind, key2, key1);
|
|
1521
|
+
}
|
|
1522
|
+
const result = compareValues(v1.value, v2.value);
|
|
1523
|
+
if (result !== 0) {
|
|
1524
|
+
return result;
|
|
1525
|
+
}
|
|
1526
|
+
i++;
|
|
1527
|
+
}
|
|
1528
|
+
if (key1.kind === key2.kind) {
|
|
1529
|
+
return 0;
|
|
1530
|
+
}
|
|
1531
|
+
if (key1.kind === "exact") {
|
|
1532
|
+
if (key2.kind === "successor") {
|
|
1533
|
+
return -1;
|
|
1534
|
+
} else {
|
|
1535
|
+
return 1;
|
|
132
1536
|
}
|
|
133
1537
|
}
|
|
134
|
-
|
|
1538
|
+
if (key1.kind === "predecessor") {
|
|
1539
|
+
return -1;
|
|
1540
|
+
}
|
|
1541
|
+
if (key1.kind === "successor") {
|
|
1542
|
+
return 1;
|
|
1543
|
+
}
|
|
1544
|
+
throw new Error(`Unexpected key kind: ${key1.kind}`);
|
|
135
1545
|
}
|
|
136
|
-
function
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
admin: {
|
|
152
|
-
group: "Auth",
|
|
153
|
-
useAsTitle: "_id",
|
|
154
|
-
...defaultColumns ? { defaultColumns } : {}
|
|
1546
|
+
function serializeCursor(key) {
|
|
1547
|
+
return JSON.stringify(convexToJson(key.map((v2) => v2 === void 0 ? "undefined" : typeof v2 === "string" && v2.endsWith("undefined") ? (
|
|
1548
|
+
// in the unlikely case their string was "undefined"
|
|
1549
|
+
// or "_undefined" etc, we escape it.
|
|
1550
|
+
"_" + v2
|
|
1551
|
+
) : v2)));
|
|
1552
|
+
}
|
|
1553
|
+
function deserializeCursor(cursor) {
|
|
1554
|
+
return jsonToConvex(JSON.parse(cursor)).map((v2) => {
|
|
1555
|
+
if (typeof v2 === "string") {
|
|
1556
|
+
if (v2 === "undefined") {
|
|
1557
|
+
return void 0;
|
|
1558
|
+
}
|
|
1559
|
+
if (v2.endsWith("undefined")) {
|
|
1560
|
+
return v2.slice(1);
|
|
155
1561
|
}
|
|
1562
|
+
}
|
|
1563
|
+
return v2;
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// src/convex/utils.ts
|
|
1568
|
+
var isUniqueField = (betterAuthSchema, model, field) => {
|
|
1569
|
+
const fields = betterAuthSchema[model]?.fields;
|
|
1570
|
+
if (!fields) {
|
|
1571
|
+
return false;
|
|
1572
|
+
}
|
|
1573
|
+
return Object.entries(fields).filter(([, value]) => value.unique).map(([key]) => key).includes(field);
|
|
1574
|
+
};
|
|
1575
|
+
var hasUniqueFields = (betterAuthSchema, model, input) => {
|
|
1576
|
+
for (const field of Object.keys(input)) {
|
|
1577
|
+
if (isUniqueField(betterAuthSchema, model, field)) {
|
|
1578
|
+
return true;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
return false;
|
|
1582
|
+
};
|
|
1583
|
+
var findIndex = (schema, args) => {
|
|
1584
|
+
if ((args.where?.length ?? 0) > 1 && args.where?.some((w) => w.connector === "OR")) {
|
|
1585
|
+
throw new Error(
|
|
1586
|
+
`OR connector not supported with multiple where statements in findIndex, split up the where statements before calling findIndex: ${JSON.stringify(args.where)}`
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
const where = args.where?.filter((w) => {
|
|
1590
|
+
return (!w.operator || ["eq", "gt", "gte", "in", "lt", "lte", "not_in"].includes(
|
|
1591
|
+
w.operator
|
|
1592
|
+
)) && w.field !== "_id";
|
|
1593
|
+
});
|
|
1594
|
+
if (!where?.length && !args.sortBy) {
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
const lowerBounds = where?.filter((w) => w.operator === "lt" || w.operator === "lte") ?? [];
|
|
1598
|
+
if (lowerBounds.length > 1) {
|
|
1599
|
+
throw new Error(
|
|
1600
|
+
`cannot have more than one lower bound where clause: ${JSON.stringify(where)}`
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
const upperBounds = where?.filter((w) => w.operator === "gt" || w.operator === "gte") ?? [];
|
|
1604
|
+
if (upperBounds.length > 1) {
|
|
1605
|
+
throw new Error(
|
|
1606
|
+
`cannot have more than one upper bound where clause: ${JSON.stringify(where)}`
|
|
1607
|
+
);
|
|
1608
|
+
}
|
|
1609
|
+
const lowerBound = lowerBounds[0];
|
|
1610
|
+
const upperBound = upperBounds[0];
|
|
1611
|
+
if (lowerBound && upperBound && lowerBound.field !== upperBound.field) {
|
|
1612
|
+
throw new Error(
|
|
1613
|
+
`lower bound and upper bound must have the same field: ${JSON.stringify(where)}`
|
|
1614
|
+
);
|
|
1615
|
+
}
|
|
1616
|
+
const boundField = lowerBound?.field ?? upperBound?.field;
|
|
1617
|
+
if (boundField && where?.some(
|
|
1618
|
+
(w) => w.field === boundField && w !== lowerBound && w !== upperBound
|
|
1619
|
+
)) {
|
|
1620
|
+
throw new Error(
|
|
1621
|
+
`too many where clauses on the bound field: ${JSON.stringify(where)}`
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
const indexEqFields = where?.filter((w) => !w.operator || w.operator === "eq").sort((a, b) => {
|
|
1625
|
+
return a.field.localeCompare(b.field);
|
|
1626
|
+
}).map((w) => [w.field, w.value]) ?? [];
|
|
1627
|
+
if (!indexEqFields?.length && !boundField && !args.sortBy) {
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
const table = schema.tables[args.model];
|
|
1631
|
+
if (!table) {
|
|
1632
|
+
throw new Error(`Table ${args.model} not found`);
|
|
1633
|
+
}
|
|
1634
|
+
const indexes = table[" indexes"]();
|
|
1635
|
+
const sortField = args.sortBy?.field;
|
|
1636
|
+
const indexFields = indexEqFields.map(([field]) => field).concat(
|
|
1637
|
+
boundField && boundField !== "createdAt" ? `${indexEqFields.length ? "_" : ""}${boundField}` : ""
|
|
1638
|
+
).concat(
|
|
1639
|
+
sortField && sortField !== "createdAt" && boundField !== sortField ? `${indexEqFields.length || boundField ? "_" : ""}${sortField}` : ""
|
|
1640
|
+
).filter(Boolean);
|
|
1641
|
+
if (!indexFields.length && !boundField && !sortField) {
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
const index = !indexFields.length ? {
|
|
1645
|
+
fields: [],
|
|
1646
|
+
indexDescriptor: "by_creation_time"
|
|
1647
|
+
} : indexes.find(({ fields }) => {
|
|
1648
|
+
const fieldsMatch = indexFields.every(
|
|
1649
|
+
(field, idx) => field === fields[idx]
|
|
1650
|
+
);
|
|
1651
|
+
const boundFieldMatch = boundField === "createdAt" || sortField === "createdAt" ? indexFields.length === fields.length : true;
|
|
1652
|
+
return fieldsMatch && boundFieldMatch;
|
|
1653
|
+
});
|
|
1654
|
+
if (!index) {
|
|
1655
|
+
return { indexFields };
|
|
1656
|
+
}
|
|
1657
|
+
return {
|
|
1658
|
+
boundField,
|
|
1659
|
+
index: {
|
|
1660
|
+
fields: [...index.fields, "_creationTime"],
|
|
1661
|
+
indexDescriptor: index.indexDescriptor
|
|
1662
|
+
},
|
|
1663
|
+
sortField,
|
|
1664
|
+
values: {
|
|
1665
|
+
eq: indexEqFields.map(([, value]) => value),
|
|
1666
|
+
gt: upperBound?.operator === "gt" ? upperBound.value : void 0,
|
|
1667
|
+
gte: upperBound?.operator === "gte" ? upperBound.value : void 0,
|
|
1668
|
+
lt: lowerBound?.operator === "lt" ? lowerBound.value : void 0,
|
|
1669
|
+
lte: lowerBound?.operator === "lte" ? lowerBound.value : void 0
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
};
|
|
1673
|
+
var checkUniqueFields = async (ctx, schema, betterAuthSchema, table, input, doc) => {
|
|
1674
|
+
if (!hasUniqueFields(betterAuthSchema, table, input)) {
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
for (const field of Object.keys(input)) {
|
|
1678
|
+
if (!isUniqueField(betterAuthSchema, table, field)) {
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1681
|
+
const { index } = findIndex(schema, {
|
|
1682
|
+
model: table,
|
|
1683
|
+
where: [{ field, operator: "eq", value: input[field] }]
|
|
1684
|
+
}) ?? {};
|
|
1685
|
+
if (!index) {
|
|
1686
|
+
throw new Error(`No index found for ${table}.${field}`);
|
|
1687
|
+
}
|
|
1688
|
+
const existingDoc = await ctx.db.query(table).withIndex(index.indexDescriptor, (q) => q.eq(field, input[field])).unique();
|
|
1689
|
+
if (existingDoc && existingDoc._id !== doc?._id) {
|
|
1690
|
+
throw new Error(`${table} ${field} already exists`);
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
};
|
|
1694
|
+
var selectFields = (doc, select2) => {
|
|
1695
|
+
if (!doc) {
|
|
1696
|
+
return null;
|
|
1697
|
+
}
|
|
1698
|
+
if (!select2?.length) {
|
|
1699
|
+
return doc;
|
|
1700
|
+
}
|
|
1701
|
+
return select2.reduce((acc, field) => {
|
|
1702
|
+
acc[field] = doc[field];
|
|
1703
|
+
return acc;
|
|
1704
|
+
}, {});
|
|
1705
|
+
};
|
|
1706
|
+
var filterByWhere = (doc, where, filterWhere) => {
|
|
1707
|
+
if (!doc) {
|
|
1708
|
+
return false;
|
|
1709
|
+
}
|
|
1710
|
+
for (const w of where ?? []) {
|
|
1711
|
+
if (filterWhere && !filterWhere(w)) {
|
|
1712
|
+
continue;
|
|
1713
|
+
}
|
|
1714
|
+
const value = doc[w.field];
|
|
1715
|
+
const isLessThan = (val, wVal) => {
|
|
1716
|
+
if (!wVal) {
|
|
1717
|
+
return false;
|
|
1718
|
+
}
|
|
1719
|
+
if (!val) {
|
|
1720
|
+
return true;
|
|
1721
|
+
}
|
|
1722
|
+
return val < wVal;
|
|
1723
|
+
};
|
|
1724
|
+
const isGreaterThan = (val, wVal) => {
|
|
1725
|
+
if (!val) {
|
|
1726
|
+
return false;
|
|
1727
|
+
}
|
|
1728
|
+
if (!wVal) {
|
|
1729
|
+
return true;
|
|
1730
|
+
}
|
|
1731
|
+
return val > wVal;
|
|
1732
|
+
};
|
|
1733
|
+
const filter = (w2) => {
|
|
1734
|
+
switch (w2.operator) {
|
|
1735
|
+
case "contains": {
|
|
1736
|
+
return typeof value === "string" && value.includes(w2.value);
|
|
1737
|
+
}
|
|
1738
|
+
case "ends_with": {
|
|
1739
|
+
return typeof value === "string" && value.endsWith(w2.value);
|
|
1740
|
+
}
|
|
1741
|
+
case "eq":
|
|
1742
|
+
case void 0: {
|
|
1743
|
+
return value === w2.value;
|
|
1744
|
+
}
|
|
1745
|
+
case "gt": {
|
|
1746
|
+
return isGreaterThan(value, w2.value);
|
|
1747
|
+
}
|
|
1748
|
+
case "gte": {
|
|
1749
|
+
return value === w2.value || isGreaterThan(value, w2.value);
|
|
1750
|
+
}
|
|
1751
|
+
case "in": {
|
|
1752
|
+
return Array.isArray(w2.value) && w2.value.includes(value);
|
|
1753
|
+
}
|
|
1754
|
+
case "lt": {
|
|
1755
|
+
return isLessThan(value, w2.value);
|
|
1756
|
+
}
|
|
1757
|
+
case "lte": {
|
|
1758
|
+
return value === w2.value || isLessThan(value, w2.value);
|
|
1759
|
+
}
|
|
1760
|
+
case "ne": {
|
|
1761
|
+
return value !== w2.value;
|
|
1762
|
+
}
|
|
1763
|
+
case "not_in": {
|
|
1764
|
+
return Array.isArray(w2.value) && !w2.value.includes(value);
|
|
1765
|
+
}
|
|
1766
|
+
case "starts_with": {
|
|
1767
|
+
return typeof value === "string" && value.startsWith(w2.value);
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
if (!filter(w)) {
|
|
1772
|
+
return false;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
return true;
|
|
1776
|
+
};
|
|
1777
|
+
var generateQuery = (ctx, schema, args) => {
|
|
1778
|
+
const { boundField, index, indexFields, values } = findIndex(schema, args) ?? {};
|
|
1779
|
+
const query = stream(ctx.db, schema).query(args.model);
|
|
1780
|
+
const hasValues = values?.eq?.length ?? values?.lt ?? values?.lte ?? values?.gt ?? values?.gte;
|
|
1781
|
+
const indexedQuery = index && index.indexDescriptor !== "by_creation_time" ? query.withIndex(
|
|
1782
|
+
index.indexDescriptor,
|
|
1783
|
+
hasValues ? (q) => {
|
|
1784
|
+
for (const [idx, value] of (values?.eq ?? []).entries()) {
|
|
1785
|
+
q = q.eq(index.fields[idx], value);
|
|
1786
|
+
}
|
|
1787
|
+
if (values?.lt) {
|
|
1788
|
+
q = q.lt(boundField, values.lt);
|
|
1789
|
+
}
|
|
1790
|
+
if (values?.lte) {
|
|
1791
|
+
q = q.lte(boundField, values.lte);
|
|
1792
|
+
}
|
|
1793
|
+
if (values?.gt) {
|
|
1794
|
+
q = q.gt(boundField, values.gt);
|
|
1795
|
+
}
|
|
1796
|
+
if (values?.gte) {
|
|
1797
|
+
q = q.gte(boundField, values.gte);
|
|
1798
|
+
}
|
|
1799
|
+
return q;
|
|
1800
|
+
} : void 0
|
|
1801
|
+
) : query;
|
|
1802
|
+
const orderedQuery = args.sortBy ? indexedQuery.order(args.sortBy.direction === "desc" ? "desc" : "asc") : indexedQuery;
|
|
1803
|
+
const filteredQuery = orderedQuery.filterWith(async (doc) => {
|
|
1804
|
+
if (!index && indexFields?.length) {
|
|
1805
|
+
console.warn(
|
|
1806
|
+
`Querying without an index on table "${args.model}".
|
|
1807
|
+
This can cause performance issues, and may hit the document read limit.
|
|
1808
|
+
To fix, add an index that begins with the following fields in order:
|
|
1809
|
+
[${indexFields.join(", ")}]`
|
|
1810
|
+
);
|
|
1811
|
+
return filterByWhere(doc, args.where);
|
|
1812
|
+
}
|
|
1813
|
+
return filterByWhere(
|
|
1814
|
+
doc,
|
|
1815
|
+
args.where,
|
|
1816
|
+
// Index used for all eq and range clauses, apply remaining clauses
|
|
1817
|
+
// incompatible with Convex statically.
|
|
1818
|
+
(w) => w.operator && ["contains", "ends_with", "ne", "not_in", "starts_with"].includes(
|
|
1819
|
+
w.operator
|
|
1820
|
+
)
|
|
1821
|
+
);
|
|
1822
|
+
});
|
|
1823
|
+
return filteredQuery;
|
|
1824
|
+
};
|
|
1825
|
+
var paginate = async (ctx, schema, betterAuthSchema, args) => {
|
|
1826
|
+
if (args.offset) {
|
|
1827
|
+
throw new Error(`offset not supported: ${JSON.stringify(args.offset)}`);
|
|
1828
|
+
}
|
|
1829
|
+
if (args.where?.some((w) => w.connector === "OR") && args.where?.length > 1) {
|
|
1830
|
+
throw new Error(
|
|
1831
|
+
`OR connector not supported with multiple where statements in paginate, split up the where statements before calling paginate: ${JSON.stringify(args.where)}`
|
|
1832
|
+
);
|
|
1833
|
+
}
|
|
1834
|
+
if (args.where?.some(
|
|
1835
|
+
(w) => w.field === "_id" && w.operator && !["eq", "in", "not_in"].includes(w.operator)
|
|
1836
|
+
)) {
|
|
1837
|
+
throw new Error(
|
|
1838
|
+
`_id can only be used with eq, in, or not_in operator: ${JSON.stringify(args.where)}`
|
|
1839
|
+
);
|
|
1840
|
+
}
|
|
1841
|
+
const uniqueWhere = args.where?.find(
|
|
1842
|
+
(w) => (!w.operator || w.operator === "eq") && (isUniqueField(betterAuthSchema, args.model, w.field) || w.field === "_id")
|
|
1843
|
+
);
|
|
1844
|
+
if (uniqueWhere) {
|
|
1845
|
+
const { index } = findIndex(schema, {
|
|
1846
|
+
model: args.model,
|
|
1847
|
+
where: [uniqueWhere]
|
|
1848
|
+
}) ?? {};
|
|
1849
|
+
let doc;
|
|
1850
|
+
if (uniqueWhere.field === "_id") {
|
|
1851
|
+
doc = await ctx.db.get(uniqueWhere.value);
|
|
1852
|
+
} else if (index?.indexDescriptor) {
|
|
1853
|
+
doc = await ctx.db.query(args.model).withIndex(
|
|
1854
|
+
index.indexDescriptor,
|
|
1855
|
+
(q) => q.eq(index.fields[0], uniqueWhere.value)
|
|
1856
|
+
).unique();
|
|
1857
|
+
} else {
|
|
1858
|
+
const results = await ctx.db.query(args.model).collect();
|
|
1859
|
+
doc = results.find((d) => d[uniqueWhere.field] === uniqueWhere.value) ?? null;
|
|
1860
|
+
}
|
|
1861
|
+
if (filterByWhere(doc, args.where, (w) => w !== uniqueWhere)) {
|
|
1862
|
+
return {
|
|
1863
|
+
continueCursor: "",
|
|
1864
|
+
isDone: true,
|
|
1865
|
+
page: [selectFields(doc, args.select)].filter(Boolean)
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
return {
|
|
1869
|
+
continueCursor: "",
|
|
1870
|
+
isDone: true,
|
|
1871
|
+
page: []
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
const paginationOpts = {
|
|
1875
|
+
...args.paginationOpts,
|
|
1876
|
+
// If maximumRowsRead is not at least 1 higher than numItems, bad cursors
|
|
1877
|
+
// and incorrect paging will result (at least with convex-test).
|
|
1878
|
+
maximumRowsRead: Math.max((args.paginationOpts.numItems ?? 0) + 1, 200)
|
|
1879
|
+
};
|
|
1880
|
+
const inWhere = args.where?.find((w) => w.operator === "in");
|
|
1881
|
+
if (inWhere) {
|
|
1882
|
+
if (!Array.isArray(inWhere.value)) {
|
|
1883
|
+
throw new Error("in clause value must be an array");
|
|
1884
|
+
}
|
|
1885
|
+
if (inWhere.field === "_id") {
|
|
1886
|
+
const docs = await asyncMap(inWhere.value, async (value) => {
|
|
1887
|
+
return ctx.db.get(value);
|
|
1888
|
+
});
|
|
1889
|
+
const filteredDocs = docs.flatMap((doc) => doc ? [doc] : []).filter((doc) => filterByWhere(doc, args.where, (w) => w !== inWhere));
|
|
1890
|
+
return {
|
|
1891
|
+
continueCursor: "",
|
|
1892
|
+
isDone: true,
|
|
1893
|
+
page: filteredDocs.sort((a, b) => {
|
|
1894
|
+
if (args.sortBy?.field === "createdAt") {
|
|
1895
|
+
return args.sortBy.direction === "asc" ? a._creationTime - b._creationTime : b._creationTime - a._creationTime;
|
|
1896
|
+
}
|
|
1897
|
+
if (args.sortBy) {
|
|
1898
|
+
const aValue = a[args.sortBy.field];
|
|
1899
|
+
const bValue = b[args.sortBy.field];
|
|
1900
|
+
if (aValue === bValue) {
|
|
1901
|
+
return 0;
|
|
1902
|
+
}
|
|
1903
|
+
return args.sortBy.direction === "asc" ? aValue > bValue ? 1 : -1 : aValue > bValue ? -1 : 1;
|
|
1904
|
+
}
|
|
1905
|
+
return 0;
|
|
1906
|
+
})
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
const streams = inWhere.value.map((value) => {
|
|
1910
|
+
return generateQuery(ctx, schema, {
|
|
1911
|
+
...args,
|
|
1912
|
+
where: args.where?.map((w) => {
|
|
1913
|
+
if (w === inWhere) {
|
|
1914
|
+
return { ...w, operator: "eq", value };
|
|
1915
|
+
}
|
|
1916
|
+
return w;
|
|
1917
|
+
})
|
|
1918
|
+
});
|
|
156
1919
|
});
|
|
1920
|
+
const result2 = await mergedStream(
|
|
1921
|
+
streams,
|
|
1922
|
+
[
|
|
1923
|
+
args.sortBy?.field !== "createdAt" && args.sortBy?.field,
|
|
1924
|
+
"_creationTime"
|
|
1925
|
+
].flatMap((f) => f ? [f] : [])
|
|
1926
|
+
).paginate(paginationOpts);
|
|
1927
|
+
return {
|
|
1928
|
+
...result2,
|
|
1929
|
+
page: await asyncMap(
|
|
1930
|
+
result2.page,
|
|
1931
|
+
(doc) => selectFields(doc, args.select)
|
|
1932
|
+
)
|
|
1933
|
+
};
|
|
157
1934
|
}
|
|
158
|
-
|
|
1935
|
+
const query = generateQuery(ctx, schema, args);
|
|
1936
|
+
const result = await query.paginate(paginationOpts);
|
|
1937
|
+
return {
|
|
1938
|
+
...result,
|
|
1939
|
+
page: await asyncMap(result.page, (doc) => selectFields(doc, args.select))
|
|
1940
|
+
};
|
|
1941
|
+
};
|
|
1942
|
+
var listOne = async (ctx, schema, betterAuthSchema, args) => {
|
|
1943
|
+
return (await paginate(ctx, schema, betterAuthSchema, {
|
|
1944
|
+
...args,
|
|
1945
|
+
paginationOpts: {
|
|
1946
|
+
cursor: null,
|
|
1947
|
+
numItems: 1
|
|
1948
|
+
}
|
|
1949
|
+
})).page[0];
|
|
1950
|
+
};
|
|
1951
|
+
|
|
1952
|
+
// src/convex/db.ts
|
|
1953
|
+
var getBetterAuthSchema = (schemaJson) => {
|
|
1954
|
+
return JSON.parse(schemaJson);
|
|
1955
|
+
};
|
|
1956
|
+
var create = {
|
|
1957
|
+
args: {
|
|
1958
|
+
betterAuthSchema: v.string(),
|
|
1959
|
+
data: v.any(),
|
|
1960
|
+
model: v.string(),
|
|
1961
|
+
select: v.optional(v.array(v.string()))
|
|
1962
|
+
},
|
|
1963
|
+
handler: async (ctx, { betterAuthSchema, data, model, select: select2 }, schema) => {
|
|
1964
|
+
const authSchema = getBetterAuthSchema(betterAuthSchema);
|
|
1965
|
+
await checkUniqueFields(ctx, schema, authSchema, model, data);
|
|
1966
|
+
const id = await ctx.db.insert(model, data);
|
|
1967
|
+
const doc = await ctx.db.get(id);
|
|
1968
|
+
if (!doc) {
|
|
1969
|
+
throw new Error(`Failed to create ${model}`);
|
|
1970
|
+
}
|
|
1971
|
+
return selectFields(doc, select2);
|
|
1972
|
+
}
|
|
1973
|
+
};
|
|
1974
|
+
var findOne = {
|
|
1975
|
+
args: {
|
|
1976
|
+
betterAuthSchema: v.string(),
|
|
1977
|
+
model: v.string(),
|
|
1978
|
+
select: v.optional(v.array(v.string())),
|
|
1979
|
+
where: v.array(v.any())
|
|
1980
|
+
},
|
|
1981
|
+
handler: async (ctx, { betterAuthSchema, model, select: select2, where }, schema) => {
|
|
1982
|
+
const authSchema = getBetterAuthSchema(betterAuthSchema);
|
|
1983
|
+
const result = await listOne(ctx, schema, authSchema, {
|
|
1984
|
+
model,
|
|
1985
|
+
select: select2,
|
|
1986
|
+
where
|
|
1987
|
+
});
|
|
1988
|
+
return result;
|
|
1989
|
+
}
|
|
1990
|
+
};
|
|
1991
|
+
var findMany = {
|
|
1992
|
+
args: {
|
|
1993
|
+
betterAuthSchema: v.string(),
|
|
1994
|
+
limit: v.optional(v.number()),
|
|
1995
|
+
model: v.string(),
|
|
1996
|
+
sortBy: v.optional(
|
|
1997
|
+
v.object({
|
|
1998
|
+
direction: v.union(v.literal("asc"), v.literal("desc")),
|
|
1999
|
+
field: v.string()
|
|
2000
|
+
})
|
|
2001
|
+
),
|
|
2002
|
+
where: v.optional(v.array(v.any()))
|
|
2003
|
+
},
|
|
2004
|
+
handler: async (ctx, { betterAuthSchema, limit, model, sortBy, where }, schema) => {
|
|
2005
|
+
const authSchema = getBetterAuthSchema(betterAuthSchema);
|
|
2006
|
+
const parsedWhere = where ?? [];
|
|
2007
|
+
if (parsedWhere.some((w) => w.connector === "OR")) {
|
|
2008
|
+
const results = await Promise.all(
|
|
2009
|
+
parsedWhere.map(async (w) => {
|
|
2010
|
+
const result2 = await paginate(ctx, schema, authSchema, {
|
|
2011
|
+
model,
|
|
2012
|
+
paginationOpts: { cursor: null, numItems: limit ?? 200 },
|
|
2013
|
+
sortBy,
|
|
2014
|
+
where: [w]
|
|
2015
|
+
});
|
|
2016
|
+
return result2.page;
|
|
2017
|
+
})
|
|
2018
|
+
);
|
|
2019
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2020
|
+
const uniqueDocs = [];
|
|
2021
|
+
for (const docs of results) {
|
|
2022
|
+
for (const doc of docs) {
|
|
2023
|
+
const docId = doc._id;
|
|
2024
|
+
if (!seen.has(docId)) {
|
|
2025
|
+
seen.add(docId);
|
|
2026
|
+
uniqueDocs.push(doc);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
if (sortBy) {
|
|
2031
|
+
uniqueDocs.sort((a, b) => {
|
|
2032
|
+
const aVal = a[sortBy.field];
|
|
2033
|
+
const bVal = b[sortBy.field];
|
|
2034
|
+
if (aVal === bVal) {
|
|
2035
|
+
return 0;
|
|
2036
|
+
}
|
|
2037
|
+
const comparison = aVal > bVal ? 1 : -1;
|
|
2038
|
+
return sortBy.direction === "desc" ? -comparison : comparison;
|
|
2039
|
+
});
|
|
2040
|
+
}
|
|
2041
|
+
return uniqueDocs.slice(0, limit);
|
|
2042
|
+
}
|
|
2043
|
+
const result = await paginate(ctx, schema, authSchema, {
|
|
2044
|
+
model,
|
|
2045
|
+
paginationOpts: { cursor: null, numItems: limit ?? 200 },
|
|
2046
|
+
sortBy,
|
|
2047
|
+
where: parsedWhere
|
|
2048
|
+
});
|
|
2049
|
+
return result.page;
|
|
2050
|
+
}
|
|
2051
|
+
};
|
|
2052
|
+
var count = {
|
|
2053
|
+
args: {
|
|
2054
|
+
betterAuthSchema: v.string(),
|
|
2055
|
+
model: v.string(),
|
|
2056
|
+
where: v.optional(v.array(v.any()))
|
|
2057
|
+
},
|
|
2058
|
+
handler: async (ctx, { betterAuthSchema, model, where }, schema) => {
|
|
2059
|
+
const authSchema = getBetterAuthSchema(betterAuthSchema);
|
|
2060
|
+
const parsedWhere = where ?? [];
|
|
2061
|
+
if (parsedWhere.some((w) => w.connector === "OR")) {
|
|
2062
|
+
const results = await Promise.all(
|
|
2063
|
+
parsedWhere.map(async (w) => {
|
|
2064
|
+
const result2 = await paginate(ctx, schema, authSchema, {
|
|
2065
|
+
model,
|
|
2066
|
+
paginationOpts: { cursor: null, numItems: 200 },
|
|
2067
|
+
where: [w]
|
|
2068
|
+
});
|
|
2069
|
+
return result2.page;
|
|
2070
|
+
})
|
|
2071
|
+
);
|
|
2072
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2073
|
+
for (const docs of results) {
|
|
2074
|
+
for (const doc of docs) {
|
|
2075
|
+
const docId = doc._id;
|
|
2076
|
+
seen.add(docId);
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
return seen.size;
|
|
2080
|
+
}
|
|
2081
|
+
const result = await paginate(ctx, schema, authSchema, {
|
|
2082
|
+
model,
|
|
2083
|
+
paginationOpts: { cursor: null, numItems: 200 },
|
|
2084
|
+
where: parsedWhere
|
|
2085
|
+
});
|
|
2086
|
+
return result.page.length;
|
|
2087
|
+
}
|
|
2088
|
+
};
|
|
2089
|
+
var update = {
|
|
2090
|
+
args: {
|
|
2091
|
+
betterAuthSchema: v.string(),
|
|
2092
|
+
model: v.string(),
|
|
2093
|
+
update: v.any(),
|
|
2094
|
+
where: v.array(v.any())
|
|
2095
|
+
},
|
|
2096
|
+
handler: async (ctx, { betterAuthSchema, model, update: update2, where }, schema) => {
|
|
2097
|
+
const authSchema = getBetterAuthSchema(betterAuthSchema);
|
|
2098
|
+
const parsedWhere = where;
|
|
2099
|
+
const doc = await listOne(ctx, schema, authSchema, {
|
|
2100
|
+
model,
|
|
2101
|
+
where: parsedWhere
|
|
2102
|
+
});
|
|
2103
|
+
if (!doc) {
|
|
2104
|
+
return null;
|
|
2105
|
+
}
|
|
2106
|
+
await checkUniqueFields(ctx, schema, authSchema, model, update2, doc);
|
|
2107
|
+
await ctx.db.patch(doc._id, update2);
|
|
2108
|
+
return await ctx.db.get(doc._id);
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
var updateMany = {
|
|
2112
|
+
args: {
|
|
2113
|
+
betterAuthSchema: v.string(),
|
|
2114
|
+
model: v.string(),
|
|
2115
|
+
update: v.any(),
|
|
2116
|
+
where: v.array(v.any())
|
|
2117
|
+
},
|
|
2118
|
+
handler: async (ctx, { betterAuthSchema, model, update: update2, where }, schema) => {
|
|
2119
|
+
const authSchema = getBetterAuthSchema(betterAuthSchema);
|
|
2120
|
+
const parsedWhere = where;
|
|
2121
|
+
const result = await paginate(ctx, schema, authSchema, {
|
|
2122
|
+
model,
|
|
2123
|
+
paginationOpts: { cursor: null, numItems: 200 },
|
|
2124
|
+
where: parsedWhere
|
|
2125
|
+
});
|
|
2126
|
+
if (result.page.length > 1) {
|
|
2127
|
+
const uniqueFieldKeys = Object.keys(update2).filter(
|
|
2128
|
+
(key) => authSchema[model]?.fields?.[key]?.unique
|
|
2129
|
+
);
|
|
2130
|
+
if (uniqueFieldKeys.length > 0) {
|
|
2131
|
+
throw new Error(
|
|
2132
|
+
`Attempted to set unique fields in multiple documents in ${model} with the same value. Fields: ${uniqueFieldKeys.join(", ")}`
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
for (const doc of result.page) {
|
|
2137
|
+
await checkUniqueFields(
|
|
2138
|
+
ctx,
|
|
2139
|
+
schema,
|
|
2140
|
+
authSchema,
|
|
2141
|
+
model,
|
|
2142
|
+
update2,
|
|
2143
|
+
doc
|
|
2144
|
+
);
|
|
2145
|
+
await ctx.db.patch(doc._id, update2);
|
|
2146
|
+
}
|
|
2147
|
+
return result.page.length;
|
|
2148
|
+
}
|
|
2149
|
+
};
|
|
2150
|
+
var deleteOne = {
|
|
2151
|
+
args: {
|
|
2152
|
+
betterAuthSchema: v.string(),
|
|
2153
|
+
model: v.string(),
|
|
2154
|
+
where: v.array(v.any())
|
|
2155
|
+
},
|
|
2156
|
+
handler: async (ctx, { betterAuthSchema, model, where }, schema) => {
|
|
2157
|
+
const authSchema = getBetterAuthSchema(betterAuthSchema);
|
|
2158
|
+
const parsedWhere = where;
|
|
2159
|
+
const doc = await listOne(ctx, schema, authSchema, {
|
|
2160
|
+
model,
|
|
2161
|
+
where: parsedWhere
|
|
2162
|
+
});
|
|
2163
|
+
if (!doc) {
|
|
2164
|
+
return;
|
|
2165
|
+
}
|
|
2166
|
+
await ctx.db.delete(doc._id);
|
|
2167
|
+
}
|
|
2168
|
+
};
|
|
2169
|
+
var deleteMany = {
|
|
2170
|
+
args: {
|
|
2171
|
+
betterAuthSchema: v.string(),
|
|
2172
|
+
model: v.string(),
|
|
2173
|
+
where: v.array(v.any())
|
|
2174
|
+
},
|
|
2175
|
+
handler: async (ctx, { betterAuthSchema, model, where }, schema) => {
|
|
2176
|
+
const authSchema = getBetterAuthSchema(betterAuthSchema);
|
|
2177
|
+
const parsedWhere = where;
|
|
2178
|
+
const result = await paginate(ctx, schema, authSchema, {
|
|
2179
|
+
model,
|
|
2180
|
+
paginationOpts: { cursor: null, numItems: 200 },
|
|
2181
|
+
where: parsedWhere
|
|
2182
|
+
});
|
|
2183
|
+
for (const doc of result.page) {
|
|
2184
|
+
await ctx.db.delete(doc._id);
|
|
2185
|
+
}
|
|
2186
|
+
return result.page.length;
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
|
|
2190
|
+
// src/convex/getAuth.ts
|
|
2191
|
+
function createGetAuth(props) {
|
|
2192
|
+
return async (ctx) => {
|
|
2193
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
2194
|
+
if (identity === null) {
|
|
2195
|
+
return { user: null };
|
|
2196
|
+
}
|
|
2197
|
+
const user = await ctx.db.get(
|
|
2198
|
+
props.userCollectionSlug,
|
|
2199
|
+
identity.subject
|
|
2200
|
+
);
|
|
2201
|
+
if (user === null) {
|
|
2202
|
+
return { user };
|
|
2203
|
+
}
|
|
2204
|
+
if (!props.resolveOrgs) {
|
|
2205
|
+
return { user };
|
|
2206
|
+
}
|
|
2207
|
+
const sessionId = identity.sessionId;
|
|
2208
|
+
const session = sessionId ? await ctx.db.get(props.sessionCollectionSlug, sessionId) : null;
|
|
2209
|
+
const orgId = session?.activeOrganizationId;
|
|
2210
|
+
const organization = orgId ? await ctx.db.get(props.orgCollectionSlug, orgId) ?? void 0 : void 0;
|
|
2211
|
+
return { user, organization };
|
|
2212
|
+
};
|
|
159
2213
|
}
|
|
160
2214
|
|
|
161
|
-
// src/index.ts
|
|
162
|
-
function
|
|
163
|
-
const collections = extractAuthCollections(props?.config ?? {});
|
|
2215
|
+
// src/convex/index.ts
|
|
2216
|
+
function authDbApi(options) {
|
|
164
2217
|
return {
|
|
165
|
-
|
|
166
|
-
|
|
2218
|
+
dbCreate: options.internalMutation({
|
|
2219
|
+
...create,
|
|
2220
|
+
handler: async (ctx, args) => await create.handler(
|
|
2221
|
+
ctx,
|
|
2222
|
+
args,
|
|
2223
|
+
options.schema
|
|
2224
|
+
)
|
|
2225
|
+
}),
|
|
2226
|
+
dbFindOne: options.internalQuery({
|
|
2227
|
+
...findOne,
|
|
2228
|
+
handler: async (ctx, args) => await findOne.handler(
|
|
2229
|
+
ctx,
|
|
2230
|
+
args,
|
|
2231
|
+
options.schema
|
|
2232
|
+
)
|
|
2233
|
+
}),
|
|
2234
|
+
dbFindMany: options.internalQuery({
|
|
2235
|
+
...findMany,
|
|
2236
|
+
handler: async (ctx, args) => await findMany.handler(
|
|
2237
|
+
ctx,
|
|
2238
|
+
args,
|
|
2239
|
+
options.schema
|
|
2240
|
+
)
|
|
2241
|
+
}),
|
|
2242
|
+
dbCount: options.internalQuery({
|
|
2243
|
+
...count,
|
|
2244
|
+
handler: async (ctx, args) => await count.handler(
|
|
2245
|
+
ctx,
|
|
2246
|
+
args,
|
|
2247
|
+
options.schema
|
|
2248
|
+
)
|
|
2249
|
+
}),
|
|
2250
|
+
dbUpdate: options.internalMutation({
|
|
2251
|
+
...update,
|
|
2252
|
+
handler: async (ctx, args) => await update.handler(
|
|
2253
|
+
ctx,
|
|
2254
|
+
args,
|
|
2255
|
+
options.schema
|
|
2256
|
+
)
|
|
2257
|
+
}),
|
|
2258
|
+
dbUpdateMany: options.internalMutation({
|
|
2259
|
+
...updateMany,
|
|
2260
|
+
handler: async (ctx, args) => await updateMany.handler(
|
|
2261
|
+
ctx,
|
|
2262
|
+
args,
|
|
2263
|
+
options.schema
|
|
2264
|
+
)
|
|
2265
|
+
}),
|
|
2266
|
+
dbDelete: options.internalMutation({
|
|
2267
|
+
...deleteOne,
|
|
2268
|
+
handler: async (ctx, args) => await deleteOne.handler(
|
|
2269
|
+
ctx,
|
|
2270
|
+
args,
|
|
2271
|
+
options.schema
|
|
2272
|
+
)
|
|
2273
|
+
}),
|
|
2274
|
+
dbDeleteMany: options.internalMutation({
|
|
2275
|
+
...deleteMany,
|
|
2276
|
+
handler: async (ctx, args) => await deleteMany.handler(
|
|
2277
|
+
ctx,
|
|
2278
|
+
args,
|
|
2279
|
+
options.schema
|
|
2280
|
+
)
|
|
2281
|
+
})
|
|
167
2282
|
};
|
|
168
2283
|
}
|
|
2284
|
+
function createBetterAuthAdapter(ctx) {
|
|
2285
|
+
return convexAdapter(ctx);
|
|
2286
|
+
}
|
|
169
2287
|
export {
|
|
170
|
-
|
|
2288
|
+
authDbApi,
|
|
2289
|
+
betterAuthAdapter,
|
|
2290
|
+
convexAdapter,
|
|
2291
|
+
createBetterAuthAdapter,
|
|
2292
|
+
createGetAuth
|
|
171
2293
|
};
|
|
172
2294
|
//# sourceMappingURL=index.js.map
|