@querykitjs/mongoose 2.0.0 → 3.0.1

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/dist/index.cjs CHANGED
@@ -1,611 +1,2 @@
1
- 'use strict';
2
-
3
- var core = require('@querykitjs/core');
4
- var async_hooks = require('async_hooks');
5
-
6
- // src/registry.ts
7
-
8
- // src/internal/fields.ts
9
- function resolveField(model, key) {
10
- const name = key === "id" ? "_id" : key;
11
- if (name === "_id") return "_id";
12
- return model.schema.path(name) ? name : void 0;
13
- }
14
- function hasPath(model, path) {
15
- return Boolean(model.schema.path(path));
16
- }
17
- function castValue(model, field, value) {
18
- const path = model.schema.path(field);
19
- if (!path) return value;
20
- try {
21
- return path.cast(value);
22
- } catch {
23
- return value;
24
- }
25
- }
26
- var TEXT_OPERATORS = new Set(core.TEXT_FILTER_OPERATORS);
27
- var INVALID_VALUE = /* @__PURE__ */ Symbol("querykit.invalid-value");
28
- var isTextOperator = (operator) => TEXT_OPERATORS.has(operator);
29
- function isDatePath(model, field) {
30
- return model.schema.path(field)?.instance === "Date";
31
- }
32
- function castForPath(model, field, value) {
33
- const path = model.schema.path(field);
34
- if (!path || path.instance !== "Date" || value === null || value === void 0) return value;
35
- if (value instanceof Date) return Number.isNaN(value.getTime()) ? INVALID_VALUE : value;
36
- try {
37
- const cast = path.cast(value);
38
- if (cast instanceof Date) return Number.isNaN(cast.getTime()) ? INVALID_VALUE : cast;
39
- return cast ?? INVALID_VALUE;
40
- } catch {
41
- return INVALID_VALUE;
42
- }
43
- }
44
- function coerceCondition(model, field, operator, value) {
45
- if (value === void 0 || TEXT_OPERATORS.has(operator)) return value;
46
- if (Array.isArray(value)) {
47
- const items = [];
48
- for (const item of value) {
49
- const cast2 = castForPath(model, field, item);
50
- if (cast2 === INVALID_VALUE) return INVALID_VALUE;
51
- items.push(cast2);
52
- }
53
- return items;
54
- }
55
- const cast = castForPath(model, field, value);
56
- return cast === INVALID_VALUE ? INVALID_VALUE : cast;
57
- }
58
- function reportSkip(diag, info) {
59
- if (!diag) return;
60
- const full = { ...info, source: diag.source };
61
- if (diag.strict) throw new core.QueryKitError(full);
62
- diag.onSkipped?.(full);
63
- }
64
-
65
- // src/internal/operators.ts
66
- var esc = (s) => String(s ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
67
- var sqlLikeToRegex = (pattern) => {
68
- let out = "";
69
- for (const ch of String(pattern ?? "")) {
70
- if (ch === "%") out += ".*";
71
- else if (ch === "_") out += ".";
72
- else out += ch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
73
- }
74
- return `^${out}$`;
75
- };
76
- var asTuple = (v) => Array.isArray(v) && v.length === 2 ? [v[0], v[1]] : void 0;
77
- var operators = {
78
- "=": (v) => ({ $eq: v }),
79
- eq: (v) => ({ $eq: v }),
80
- // SQL three-valued logic: inequality/negation excludes NULL (and missing) rows,
81
- // matching drizzle-pg. `$nin: [v, null]` = "not v AND not null".
82
- "!=": (v) => ({ $nin: [v, null] }),
83
- ne: (v) => ({ $nin: [v, null] }),
84
- ">": (v) => ({ $gt: v }),
85
- gt: (v) => ({ $gt: v }),
86
- ">=": (v) => ({ $gte: v }),
87
- gte: (v) => ({ $gte: v }),
88
- "<": (v) => ({ $lt: v }),
89
- lt: (v) => ({ $lt: v }),
90
- "<=": (v) => ({ $lte: v }),
91
- lte: (v) => ({ $lte: v }),
92
- contains: (v) => ({ $regex: esc(v), $options: "i" }),
93
- "%_%": (v) => ({ $regex: esc(v), $options: "i" }),
94
- startsWith: (v) => ({ $regex: `^${esc(v)}`, $options: "i" }),
95
- "%_": (v) => ({ $regex: `^${esc(v)}`, $options: "i" }),
96
- endsWith: (v) => ({ $regex: `${esc(v)}$`, $options: "i" }),
97
- "_%": (v) => ({ $regex: `${esc(v)}$`, $options: "i" }),
98
- like: (v) => ({ $regex: sqlLikeToRegex(v) }),
99
- ilike: (v) => ({ $regex: sqlLikeToRegex(v), $options: "i" }),
100
- // negation also excludes NULL/missing (SQL `NOT LIKE` semantics).
101
- notLike: (v) => ({ $not: { $regex: sqlLikeToRegex(v) }, $ne: null }),
102
- in: (v) => Array.isArray(v) ? { $in: v } : void 0,
103
- notIn: (v) => Array.isArray(v) ? { $nin: [...v, null] } : void 0,
104
- between: (v) => {
105
- const t = asTuple(v);
106
- return t ? { $gte: t[0], $lte: t[1] } : void 0;
107
- },
108
- notBetween: (v) => {
109
- const t = asTuple(v);
110
- return t ? { $not: { $gte: t[0], $lte: t[1] }, $ne: null } : void 0;
111
- },
112
- isNull: () => ({ $eq: null }),
113
- isNotNull: () => ({ $ne: null })
114
- };
115
-
116
- // src/internal/where.ts
117
- var isObject = (n) => typeof n === "object" && n !== null;
118
- var isFieldCondition = (n) => isObject(n) && "key" in n && !("and" in n) && !("or" in n) && !("not" in n);
119
- function combine(kind, parts) {
120
- const valid = parts.filter((p) => p !== void 0);
121
- if (valid.length === 0) return void 0;
122
- if (valid.length === 1) return valid[0];
123
- return { [kind]: valid };
124
- }
125
- function buildCondition(model, cond, diag) {
126
- const operator = cond.operation ?? "=";
127
- const build = operators[operator];
128
- if (!build) throw new Error(`Unsupported filter operator: "${operator}"`);
129
- const field = resolveField(model, cond.key);
130
- if (!field) {
131
- reportSkip(diag, { site: "filter", key: cond.key, operation: operator, reason: "unknown-key" });
132
- return void 0;
133
- }
134
- if (isTextOperator(operator) && isDatePath(model, field)) {
135
- reportSkip(diag, { site: "filter", key: cond.key, operation: operator, reason: "invalid-value" });
136
- return void 0;
137
- }
138
- const value = coerceCondition(model, field, operator, cond.value);
139
- if (value === INVALID_VALUE) {
140
- reportSkip(diag, { site: "filter", key: cond.key, operation: operator, reason: "invalid-value" });
141
- return void 0;
142
- }
143
- const fragment = build(value);
144
- if (fragment === void 0) {
145
- reportSkip(diag, { site: "filter", key: cond.key, operation: operator, reason: "invalid-value" });
146
- return void 0;
147
- }
148
- return { [field]: fragment };
149
- }
150
- function buildNode(model, node, diag) {
151
- if (isFieldCondition(node)) return buildCondition(model, node, diag);
152
- if (isObject(node)) {
153
- if ("and" in node)
154
- return combine(
155
- "$and",
156
- node.and.map((n) => buildNode(model, n, diag))
157
- );
158
- if ("or" in node)
159
- return combine(
160
- "$or",
161
- node.or.map((n) => buildNode(model, n, diag))
162
- );
163
- if ("not" in node) {
164
- const notNode = node.not;
165
- const inner = buildNode(model, notNode, diag);
166
- if (!inner) return void 0;
167
- if (isFieldCondition(notNode)) {
168
- const op = notNode.operation ?? "=";
169
- if (op !== "isNull" && op !== "isNotNull") {
170
- const field = resolveField(model, notNode.key);
171
- if (field) return { $and: [{ [field]: { $ne: null } }, { $nor: [inner] }] };
172
- }
173
- }
174
- return { $nor: [inner] };
175
- }
176
- return node;
177
- }
178
- return void 0;
179
- }
180
- function buildWhere(model, filter, diag) {
181
- if (filter === void 0) return void 0;
182
- if (Array.isArray(filter))
183
- return combine(
184
- "$and",
185
- filter.map((c) => buildNode(model, c, diag))
186
- );
187
- return buildNode(model, filter, diag);
188
- }
189
-
190
- // src/internal/order-by.ts
191
- function normalize(sort) {
192
- if (!sort) return [];
193
- return sort.filter((item) => item && item.key).map((item) => ({ key: item.key, direction: item.direction ?? "asc" }));
194
- }
195
- function buildSort(model, sort, diag) {
196
- const spec = {};
197
- for (const item of normalize(sort)) {
198
- const field = resolveField(model, item.key);
199
- if (field) spec[field] = item.direction === "desc" ? -1 : 1;
200
- else reportSkip(diag, { site: "sort", key: item.key, reason: "unknown-key" });
201
- }
202
- if (Object.keys(spec).length === 0) {
203
- if (hasPath(model, "createdAt")) spec.createdAt = -1;
204
- else spec._id = -1;
205
- }
206
- return spec;
207
- }
208
-
209
- // src/internal/cursor.ts
210
- function encodeCursor(value) {
211
- const normalized = value instanceof Date ? value.toISOString() : value;
212
- const json = JSON.stringify({ v: normalized });
213
- return Buffer.from(json, "utf8").toString("base64url");
214
- }
215
- function decodeCursor(cursor) {
216
- if (!cursor) return void 0;
217
- try {
218
- const json = Buffer.from(cursor, "base64url").toString("utf8");
219
- const payload = JSON.parse(json);
220
- return payload.v;
221
- } catch {
222
- return void 0;
223
- }
224
- }
225
-
226
- // src/repository.ts
227
- var clampPageSize = (requested, fallback, max) => Math.min(max, Math.max(1, Math.trunc(requested ?? fallback)));
228
- function buildRepository(runtime, model, config = {}) {
229
- const m = model;
230
- const hasDeletedAt = hasPath(m, "deletedAt");
231
- const timestampsOn = Boolean(m.schema.options?.timestamps);
232
- const stampUpdatedAt = hasPath(m, "updatedAt") && !timestampsOn;
233
- const forUpdate = (patch) => stampUpdatedAt ? { ...patch, updatedAt: /* @__PURE__ */ new Date() } : patch;
234
- const scopeConditions = config.scope ? Object.entries(config.scope).map(([key, value]) => ({ key, operation: "=", value })) : [];
235
- for (const condition of scopeConditions) {
236
- if (!resolveField(m, condition.key)) {
237
- throw new core.QueryKitError({ source: m.modelName, site: "filter", key: condition.key, reason: "unknown-key" });
238
- }
239
- }
240
- const scopeWhere = scopeConditions.length ? buildWhere(m, scopeConditions) : void 0;
241
- const diag = {
242
- source: m.modelName,
243
- strict: runtime.strict,
244
- onSkipped: runtime.onSkippedCondition
245
- };
246
- const sortSpec = (sort) => buildSort(m, sort, diag);
247
- const composeWhere = (userFilter, withDeleted) => {
248
- const parts = [
249
- buildWhere(m, userFilter, diag),
250
- scopeWhere,
251
- hasDeletedAt && !withDeleted ? { deletedAt: { $eq: null } } : void 0
252
- ].filter((p) => p !== void 0);
253
- if (parts.length === 0) return {};
254
- if (parts.length === 1) return parts[0];
255
- return { $and: parts };
256
- };
257
- const withId = (filter, idKey, id) => {
258
- const idCondition = { key: idKey, operation: "=", value: id };
259
- if (!filter) return [idCondition];
260
- if (Array.isArray(filter)) return [...filter, idCondition];
261
- return { and: [filter, idCondition] };
262
- };
263
- const truthyKeys = (selection) => Object.entries(selection).filter(([, on]) => on).map(([key]) => key);
264
- const forcedKeys = config.forcedColumns ? truthyKeys(config.forcedColumns) : void 0;
265
- if (config.forcedColumns && forcedKeys.length === 0) {
266
- throw new Error("buildRepository: forcedColumns must select at least one field (an empty selection would return the full document).");
267
- }
268
- const allowed = config.allowedColumns ? new Set(config.allowedColumns) : void 0;
269
- if (config.allowedColumns && allowed.size === 0) {
270
- throw new Error("buildRepository: allowedColumns must list at least one field (an empty allowlist would return the full document).");
271
- }
272
- const guarded = Boolean(forcedKeys ?? allowed);
273
- const permits = (key) => forcedKeys ? forcedKeys.includes(key) : allowed ? allowed.has(key) : true;
274
- const intersectAllowed = (requested) => {
275
- const hit = requested.filter((key) => allowed.has(key));
276
- return hit.length > 0 ? hit : [...allowed];
277
- };
278
- const projection = (columns) => {
279
- const requested = columns ? truthyKeys(columns) : [];
280
- const keys = forcedKeys ?? (allowed ? intersectAllowed(requested) : requested);
281
- if (keys.length === 0) return void 0;
282
- const proj = {};
283
- let includesId = false;
284
- for (const k of keys) {
285
- const field = resolveField(m, k);
286
- if (!field) continue;
287
- proj[field] = 1;
288
- if (field === "_id") includesId = true;
289
- }
290
- if (Object.keys(proj).length === 0) return void 0;
291
- if (!includesId) proj._id = 0;
292
- return proj;
293
- };
294
- const applyPopulate = (q, withRel) => {
295
- if (withRel) {
296
- for (const key of Object.keys(withRel)) {
297
- const value = withRel[key];
298
- if (!value) continue;
299
- q.populate(typeof value === "object" ? { path: key, ...value } : key);
300
- }
301
- }
302
- return q;
303
- };
304
- const attachSession = (q) => {
305
- const s = runtime.getSession();
306
- if (s) q.session(s);
307
- return q;
308
- };
309
- const toArr = (v) => v === void 0 ? [] : Array.isArray(v) ? v : [v];
310
- const forInsert = (values) => ({
311
- ...config.scope,
312
- ...values,
313
- ...config.scope
314
- });
315
- const buildUpsert = (values, options) => {
316
- const targetKeys = toArr(options.target);
317
- const inserted = forInsert(values);
318
- const filter = {};
319
- for (const key of targetKeys) {
320
- const field = resolveField(m, key);
321
- if (!field) throw new Error(`upsert: unknown target field "${key}".`);
322
- filter[field] = inserted[key];
323
- }
324
- const set = options.set ? { ...options.set } : Object.fromEntries(Object.entries(inserted).filter(([k]) => !targetKeys.includes(k)));
325
- const setOnInsert = Object.fromEntries(Object.entries(inserted).filter(([k]) => !targetKeys.includes(k) && !(k in set)));
326
- const update = {};
327
- if (Object.keys(set).length) update.$set = set;
328
- if (Object.keys(setOnInsert).length) update.$setOnInsert = setOnInsert;
329
- return { filter, update };
330
- };
331
- const runFind = (where, opts) => {
332
- let q = m.find(where, projection(opts.columns));
333
- if (opts.sort) q = q.sort(opts.sort);
334
- if (typeof opts.skip === "number") q = q.skip(opts.skip);
335
- if (typeof opts.limit === "number") q = q.limit(opts.limit);
336
- q = applyPopulate(q, opts.with);
337
- return attachSession(q).lean().exec();
338
- };
339
- const countWhere = (where) => attachSession(m.countDocuments(where)).exec();
340
- const repository = {
341
- model,
342
- async findAll(params = {}) {
343
- const rows = await runFind(composeWhere(params.filter, params.withDeleted), {
344
- sort: sortSpec(params.sort),
345
- columns: params.columns,
346
- with: params.with
347
- });
348
- return rows;
349
- },
350
- async findOne(params = {}) {
351
- let q = m.findOne(composeWhere(params.filter, params.withDeleted), projection(params.columns));
352
- q = q.sort(sortSpec(params.sort));
353
- q = applyPopulate(q, params.with);
354
- const row = await attachSession(q).lean().exec();
355
- return row ?? void 0;
356
- },
357
- async findById(id, params = {}) {
358
- const { idKey = "id", filter, columns, with: withRel } = params;
359
- let q = m.findOne(composeWhere(withId(filter, idKey, id), params.withDeleted), projection(columns));
360
- q = applyPopulate(q, withRel);
361
- const row = await attachSession(q).lean().exec();
362
- return row ?? void 0;
363
- },
364
- async findList(params = {}) {
365
- const page = Math.max(1, Math.trunc(params.page ?? 1));
366
- const perPage = clampPageSize(params.perPage, runtime.defaultPerPage, runtime.maxPerPage);
367
- const where = composeWhere(params.filter, params.withDeleted);
368
- const [data, total_items] = await Promise.all([
369
- runFind(where, { sort: sortSpec(params.sort), columns: params.columns, with: params.with, limit: perPage, skip: (page - 1) * perPage }),
370
- countWhere(where)
371
- ]);
372
- const total_pages = Math.ceil(total_items / perPage);
373
- return {
374
- data,
375
- meta: {
376
- total_items,
377
- total_pages,
378
- current_page: page,
379
- per_page: perPage,
380
- has_next: page < total_pages,
381
- has_prev: page > 1
382
- }
383
- };
384
- },
385
- async findInfinite(params = {}) {
386
- const limit = clampPageSize(params.limit, runtime.defaultLimit, runtime.maxLimit);
387
- const offset = Math.max(0, Math.trunc(params.offset ?? 0));
388
- const rows = await runFind(composeWhere(params.filter, params.withDeleted), {
389
- sort: sortSpec(params.sort),
390
- columns: params.columns,
391
- with: params.with,
392
- limit: limit + 1,
393
- skip: offset
394
- });
395
- const has_more = rows.length > limit;
396
- const data = has_more ? rows.slice(0, limit) : rows;
397
- return {
398
- data,
399
- meta: { limit, offset, count: data.length, has_more, next_offset: has_more ? offset + limit : null }
400
- };
401
- },
402
- async findCursor(params = {}) {
403
- const limit = clampPageSize(params.limit, runtime.defaultLimit, runtime.maxLimit);
404
- const cursorKey = params.cursorKey ?? "id";
405
- const order = params.order ?? "asc";
406
- const direction = params.direction ?? "forward";
407
- const field = resolveField(m, cursorKey);
408
- if (!field) throw new core.QueryKitError({ source: diag.source, site: "cursorKey", key: cursorKey, reason: "unknown-key" });
409
- const decoded = decodeCursor(params.cursor);
410
- const cursorValue = decoded !== void 0 ? castValue(m, field, decoded) : void 0;
411
- const baseWhere = composeWhere(params.filter, params.withDeleted);
412
- const ascInQuery = direction === "forward" ? order === "asc" : order === "desc";
413
- const seek = cursorValue !== void 0 ? { [field]: ascInQuery ? { $gt: cursorValue } : { $lt: cursorValue } } : void 0;
414
- const where = seek ? Object.keys(baseWhere).length ? { $and: [baseWhere, seek] } : seek : baseWhere;
415
- const selected = projection(params.columns);
416
- const proj = selected ? { ...selected, [field]: 1 } : void 0;
417
- const leaksCursorField = guarded && selected !== void 0 && !permits(cursorKey) && !permits(field);
418
- let q = m.find(where, proj).sort({ [field]: ascInQuery ? 1 : -1 }).limit(limit + 1);
419
- q = applyPopulate(q, params.with);
420
- const rows = await attachSession(q).lean().exec();
421
- const hasExtra = rows.length > limit;
422
- let pageRows = hasExtra ? rows.slice(0, limit) : rows;
423
- if (direction === "backward") pageRows = [...pageRows].reverse();
424
- const first = pageRows[0];
425
- const last = pageRows[pageRows.length - 1];
426
- const has_next = direction === "forward" ? hasExtra : cursorValue !== void 0;
427
- const has_prev = direction === "forward" ? cursorValue !== void 0 : hasExtra;
428
- const meta = {
429
- limit,
430
- has_next,
431
- has_prev,
432
- next_cursor: has_next && last ? encodeCursor(last[field]) : null,
433
- prev_cursor: has_prev && first ? encodeCursor(first[field]) : null
434
- };
435
- if (leaksCursorField) {
436
- pageRows = pageRows.map((row) => {
437
- const { [field]: _cursor, ...rest } = row;
438
- return rest;
439
- });
440
- }
441
- return { data: pageRows, meta };
442
- },
443
- count(filter) {
444
- return countWhere(composeWhere(filter));
445
- },
446
- async exists(filter) {
447
- return await countWhere(composeWhere(filter)) > 0;
448
- },
449
- /* ------------------------------- writes ------------------------------- */
450
- async create(values) {
451
- const [doc] = await m.create([forInsert(values)], { session: runtime.getSession() });
452
- return doc.toObject();
453
- },
454
- async createMany(values) {
455
- if (values.length === 0) return [];
456
- const docs = await m.insertMany(values.map(forInsert), { session: runtime.getSession() });
457
- return docs.map((d) => d.toObject());
458
- },
459
- async upsert(values, options) {
460
- const { filter, update } = buildUpsert(values, options);
461
- const q = m.findOneAndUpdate(filter, update, { returnDocument: "after", upsert: true });
462
- return await attachSession(q).lean().exec();
463
- },
464
- async upsertMany(values, options) {
465
- if (values.length === 0) return [];
466
- const CHUNK = 1e3;
467
- const entries = values.map((v) => buildUpsert(v, options));
468
- for (let i = 0; i < entries.length; i += CHUNK) {
469
- const ops = entries.slice(i, i + CHUNK).map((e) => ({ updateOne: { filter: e.filter, update: e.update, upsert: true } }));
470
- await m.bulkWrite(ops, { session: runtime.getSession() });
471
- }
472
- const rows = await attachSession(m.find({ $or: entries.map((e) => e.filter) })).lean().exec();
473
- const targetFields = toArr(options.target).map((k) => resolveField(m, k)).filter((f2) => Boolean(f2));
474
- const keyOf = (o) => targetFields.map((f2) => String(o[f2])).join("\0");
475
- const byKey = new Map(rows.map((r) => [keyOf(r), r]));
476
- return entries.map((e) => byKey.get(keyOf(e.filter))).filter((r) => Boolean(r));
477
- },
478
- async updateById(id, patch, idKey = "id") {
479
- const where = composeWhere(withId(void 0, idKey, id));
480
- const q = m.findOneAndUpdate(where, forUpdate(patch), { returnDocument: "after" });
481
- const row = await attachSession(q).lean().exec();
482
- return row ?? void 0;
483
- },
484
- async updateWhere(filter, patch) {
485
- const where = composeWhere(filter);
486
- const found = await attachSession(m.find(where).select({ _id: 1 })).lean().exec();
487
- if (found.length === 0) return [];
488
- const ids = found.map((d) => d._id);
489
- await attachSession(m.updateMany({ _id: { $in: ids } }, forUpdate(patch))).exec();
490
- return await attachSession(m.find({ _id: { $in: ids } })).lean().exec();
491
- },
492
- async deleteById(id, idKey = "id") {
493
- const where = composeWhere(withId(void 0, idKey, id));
494
- const row = await attachSession(m.findOneAndDelete(where)).lean().exec();
495
- return row ?? void 0;
496
- },
497
- async deleteWhere(filter) {
498
- const where = composeWhere(filter);
499
- const rows = await attachSession(m.find(where)).lean().exec();
500
- if (rows.length === 0) return [];
501
- await attachSession(m.deleteMany({ _id: { $in: rows.map((d) => d._id) } })).exec();
502
- return rows;
503
- },
504
- async softDelete(id, idKey = "id") {
505
- if (!hasDeletedAt) throw new Error("softDelete: schema has no deletedAt path.");
506
- const where = composeWhere(withId(void 0, idKey, id), false);
507
- const q = m.findOneAndUpdate(where, forUpdate({ deletedAt: /* @__PURE__ */ new Date() }), { returnDocument: "after" });
508
- const row = await attachSession(q).lean().exec();
509
- return row ?? void 0;
510
- },
511
- async restore(id, idKey = "id") {
512
- if (!hasDeletedAt) throw new Error("restore: schema has no deletedAt path.");
513
- const where = composeWhere(withId(void 0, idKey, id), true);
514
- const q = m.findOneAndUpdate(where, forUpdate({ deletedAt: null }), { returnDocument: "after" });
515
- const row = await attachSession(q).lean().exec();
516
- return row ?? void 0;
517
- },
518
- async aggregate(spec) {
519
- const aggregable = (key) => {
520
- const field = permits(key) ? resolveField(m, key) : void 0;
521
- if (!field) reportSkip(diag, { site: "aggregate", key, reason: "unknown-key" });
522
- return field;
523
- };
524
- const groupKeys = toArr(spec.groupBy).filter((key) => Boolean(aggregable(key)));
525
- const rawMatch = composeWhere(spec.filter, spec.withDeleted);
526
- let match = rawMatch;
527
- try {
528
- const q = m.find(rawMatch);
529
- q.cast(m);
530
- match = q.getFilter();
531
- } catch {
532
- }
533
- const groupId = groupKeys.length ? Object.fromEntries(groupKeys.map((k) => [k, `$${aggregable(k) ?? k}`])) : null;
534
- const group = { _id: groupId };
535
- if (spec.count) group.count = { $sum: 1 };
536
- const addAgg = (op, keys, prefix) => {
537
- for (const k of keys) {
538
- const field = aggregable(k);
539
- if (field) group[`${prefix}_${k}`] = { [op]: `$${field}` };
540
- }
541
- };
542
- addAgg("$sum", toArr(spec.sum), "sum");
543
- addAgg("$avg", toArr(spec.avg), "avg");
544
- addAgg("$min", toArr(spec.min), "min");
545
- addAgg("$max", toArr(spec.max), "max");
546
- let agg = m.aggregate([{ $match: match }, { $group: group }]);
547
- const session = runtime.getSession();
548
- if (session) agg = agg.session(session);
549
- const rows = await agg.exec();
550
- return rows.map(({ _id, ...rest }) => ({ ...typeof _id === "object" && _id ? _id : {}, ...rest }));
551
- },
552
- scoped(scope) {
553
- return buildRepository(runtime, model, { ...config, scope: { ...config.scope, ...scope } });
554
- }
555
- };
556
- return repository;
557
- }
558
- function createContextStore() {
559
- const als = new async_hooks.AsyncLocalStorage();
560
- return {
561
- get: () => als.getStore(),
562
- run: (patch, fn) => als.run({ ...als.getStore(), ...patch }, fn)
563
- };
564
- }
565
-
566
- // src/registry.ts
567
- function createRegistry(connection, options = {}) {
568
- const ctx = createContextStore();
569
- const runtime = {
570
- getSession: () => ctx.get()?.session,
571
- defaultPerPage: options.defaultPerPage ?? core.DEFAULT_PER_PAGE,
572
- defaultLimit: options.defaultLimit ?? core.DEFAULT_LIMIT,
573
- maxPerPage: options.maxPerPage ?? core.DEFAULT_MAX_PER_PAGE,
574
- maxLimit: options.maxLimit ?? core.DEFAULT_MAX_LIMIT,
575
- strict: options.strict ?? false,
576
- onSkippedCondition: options.onSkippedCondition
577
- };
578
- function repository(model, arg2, arg3) {
579
- const repoOptions = typeof arg2 === "function" ? void 0 : arg2;
580
- const extend = typeof arg2 === "function" ? arg2 : arg3;
581
- const base = buildRepository(runtime, model, repoOptions);
582
- return extend ? { ...base, ...extend(base) } : base;
583
- }
584
- const transaction = async (fn) => {
585
- if (ctx.get()?.session) return fn();
586
- const session = await connection.startSession();
587
- try {
588
- let result;
589
- await session.withTransaction(async () => {
590
- result = await ctx.run({ session }, fn);
591
- });
592
- return result;
593
- } finally {
594
- await session.endSession();
595
- }
596
- };
597
- return { repository, transaction };
598
- }
599
- function createFilters() {
600
- return core.createFilters();
601
- }
602
-
603
- Object.defineProperty(exports, "f", {
604
- enumerable: true,
605
- get: function () { return core.f; }
606
- });
607
- exports.buildRepository = buildRepository;
608
- exports.createFilters = createFilters;
609
- exports.createRegistry = createRegistry;
610
- //# sourceMappingURL=index.cjs.map
1
+ 'use strict';var core=require('@querykitjs/core'),internal=require('@querykitjs/core/internal');function S(e,t){let o=t==="id"?"_id":t;return o==="_id"?"_id":e.schema.path(o)?o:void 0}function E(e,t){return !!e.schema.path(t)}function U(e,t,o,n=E(e,"createdAt")?"createdAt":void 0){let c={};for(let l of internal.normalizeSort(t,o)){let g=S(e,l.key);g?c[g]=l.direction==="desc"?-1:1:internal.reportSkip(o,{site:"sort",key:l.key,reason:"unknown-key"});}return Object.keys(c).length===0&&(n?c[n]=-1:c._id=-1),c}function H(e,t,o,n){let c=U(e,t,o,n);return "_id"in c?c:{...c,_id:Object.values(c)[0]}}var Y=new Set(core.TEXT_FILTER_OPERATORS),P=Symbol("querykit.invalid-value"),J=e=>Y.has(e);function Z(e,t){return e.schema.path(t)?.instance==="Date"}function j(e,t,o){let n=e.schema.path(t);if(!n||o===null||o===void 0||!xe.has(n.instance))return o;if(o instanceof Date)return Number.isNaN(o.getTime())?P:o;try{let c=n.cast(o);if(c instanceof Date)return Number.isNaN(c.getTime())?P:c;if(c==null)return P;let l=n.enumValues;return n.instance==="String"&&l&&l.length>0&&!l.includes(c)?P:c}catch{return P}}var xe=new Set(["Date","Number","Boolean","ObjectId","String","Decimal128"]);function ee(e,t,o,n){if(n===void 0||Y.has(o))return n;if(Array.isArray(n)){let l=[];for(let g of n){let d=j(e,t,g);if(d===P)return P;l.push(d);}return l}let c=j(e,t,n);return c===P?P:c}var Q=e=>String(e??"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),B=e=>{let t="";for(let o of String(e??""))o==="%"?t.endsWith(".*")||(t+=".*"):o==="_"?t+=".":t+=o.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return `^${t}$`},te=e=>Array.isArray(e)&&e.length===2?[e[0],e[1]]:void 0,ne={"=":e=>({$eq:e}),eq:e=>({$eq:e}),"!=":e=>({$nin:[e,null]}),ne:e=>({$nin:[e,null]}),">":e=>({$gt:e}),gt:e=>({$gt:e}),">=":e=>({$gte:e}),gte:e=>({$gte:e}),"<":e=>({$lt:e}),lt:e=>({$lt:e}),"<=":e=>({$lte:e}),lte:e=>({$lte:e}),contains:e=>({$regex:Q(e),$options:"i"}),"%_%":e=>({$regex:Q(e),$options:"i"}),startsWith:e=>({$regex:`^${Q(e)}`,$options:"i"}),"%_":e=>({$regex:`^${Q(e)}`,$options:"i"}),endsWith:e=>({$regex:`${Q(e)}$`,$options:"i"}),"_%":e=>({$regex:`${Q(e)}$`,$options:"i"}),like:e=>({$regex:B(e)}),ilike:e=>({$regex:B(e),$options:"i"}),notLike:e=>({$not:{$regex:B(e)},$ne:null}),in:e=>Array.isArray(e)?{$in:e}:void 0,notIn:e=>Array.isArray(e)?{$nin:[...e,null]}:void 0,between:e=>{let t=te(e);return t?{$gte:t[0],$lte:t[1]}:void 0},notBetween:e=>{let t=te(e);return t?{$not:{$gte:t[0],$lte:t[1]},$ne:null}:void 0},isNull:()=>({$eq:null}),isNotNull:()=>({$ne:null})};var z=e=>typeof e=="object"&&e!==null,oe=e=>z(e)&&"key"in e&&!("and"in e)&&!("or"in e)&&!("not"in e);function _(e,t){let o=t.filter(n=>n!==void 0);if(o.length!==0)return o.length===1?o[0]:{[e]:o}}function re(e,t,o){let n=t.operation??"=",c=ne[n];if(!c)throw new Error(`Unsupported filter operator: "${n}"`);let l=S(e,t.key);if(!l){internal.reportSkip(o,{site:"filter",key:t.key,operation:n,reason:"unknown-key"});return}if(J(n)&&Z(e,l)){internal.reportSkip(o,{site:"filter",key:t.key,operation:n,reason:"invalid-value"});return}let g=ee(e,l,n,t.value);if(g===P){internal.reportSkip(o,{site:"filter",key:t.key,operation:n,reason:"invalid-value"});return}let d=c(g);if(d===void 0){internal.reportSkip(o,{site:"filter",key:t.key,operation:n,reason:"invalid-value"});return}return {[l]:d}}function I(e,t,o){if(oe(t))return re(e,t,o);if(z(t)){if("and"in t)return _("$and",t.and.map(n=>I(e,n,o)));if("or"in t)return _("$or",t.or.map(n=>I(e,n,o)));if("not"in t)return q(e,t.not,o);if(internal.isRaw(t))return t;throw new core.QueryKitError({source:e.modelName,site:"filter",key:Object.keys(t)[0]??"",reason:"forbidden-key"})}}function q(e,t,o){if(oe(t)){let c=re(e,t,o);if(!c)return;let l=t.operation??"=",g=l==="isNull"||l==="isNotNull"?void 0:S(e,t.key);return g?{$and:[{[g]:{$ne:null}},{$nor:[c]}]}:{$nor:[c]}}if(z(t)){if("and"in t)return _("$or",t.and.map(c=>q(e,c,o)));if("or"in t)return _("$and",t.or.map(c=>q(e,c,o)));if("not"in t)return I(e,t.not,o)}let n=I(e,t,o);return n?{$nor:[n]}:void 0}function W(e,t,o){if(t!==void 0)return Array.isArray(t)?_("$and",t.map(n=>I(e,n,o))):I(e,t,o)}var ie=e=>Object.entries(e).filter(([,t])=>t).map(([t])=>t);function se(e,t){let o=t.forcedColumns?ie(t.forcedColumns):void 0;if(t.forcedColumns&&o.length===0)throw new Error("buildRepository: forcedColumns must select at least one field (an empty selection would return the full document).");let n=t.allowedColumns?new Set(t.allowedColumns):void 0;if(t.allowedColumns&&n.size===0)throw new Error("buildRepository: allowedColumns must list at least one field (an empty allowlist would return the full document).");let c=d=>o?o.includes(d):n?n.has(d):true,l=d=>{let w=d.filter(F=>n.has(F));return w.length>0?w:[...n]};return {guarded:!!(o??n),permits:c,projection:d=>{let w=d?ie(d):[],F=o??(n?l(w):w);if(F.length===0)return;let u={},D=false;for(let y of F){let b=S(e,y);b&&(u[b]=1,b==="_id"&&(D=true));}if(Object.keys(u).length!==0)return D||(u._id=0),u}}}function ae(e,t,o){let n=t,c=S(n,e.timestamps?.deletedAt??"deletedAt")??"deletedAt",l=E(n,c),g=e.timestamps?.createdAt!==void 0,d=S(n,e.timestamps?.createdAt??"createdAt"),w=!!n.schema.options?.timestamps,F=E(n,"updatedAt")&&!w,u=m=>F?{...m,updatedAt:new Date}:m,D=Ie(n,o),y={source:n.modelName,strict:e.strict,onSkipped:e.onSkippedCondition},b=m=>U(n,m,y,d),x=m=>H(n,m,y,d),r=m=>internal.checkWith(m,o.relations,Qe,y.source),i=(m,T)=>{let k=[m,D,l&&!T?{[c]:{$eq:null}}:void 0].filter(C=>C!==void 0);return k.length===0?{}:k.length===1?k[0]:{$and:k}},s=(m,T)=>i(W(n,m,y),T),f=(m,T,k)=>{let C={key:T,operation:"=",value:k};return m?Array.isArray(m)?[...m,C]:{and:[m,C]}:[C]},a=se(n,o),h=o.maxTimeMS??e.maxTimeMS,A=m=>{let T=e.getSession();return T&&m.session(T),Ee.has(m.op??"")&&je(m,n.modelName,h),m},p=(m,T)=>{let k=n.find(m,a.projection(T.columns));return T.sort&&(k=k.sort(T.sort)),typeof T.skip=="number"&&(k=k.skip(T.skip)),typeof T.limit=="number"&&(k=k.limit(T.limit)),k=ue(k,T.with),A(k).lean().exec()},R=m=>A(n.countDocuments(m)).exec(),$=internal.resolvePolicy({source:n.modelName,fieldOf:m=>S(n,m),primaryKey:"_id",createdAt:g?d:d??S(n,"created_at"),filterable:o.filterable,sortable:o.sortable,allowedColumns:o.allowedColumns,forcedColumns:o.forcedColumns,limits:{...e.limits,...o.limits}});return {...a,policy:$,maxTimeMS:h,runtime:e,m:n,diag:y,hasDeletedAt:l,deletedAtPath:c,forUpdate:u,sortSpec:b,listSortSpec:x,withOf:r,composeWhere:s,mutationWhere:(m,T)=>{if(internal.isAllRows(m))return i(void 0);internal.isEmptyFilter(m)&&internal.refuseEmptyFilter(y.source,T),$.checkFilter(m);let k=W(n,m,y);return k===void 0?internal.refuseEmptyFilter(y.source,T):i(k)},withId:f,applyPopulate:ue,attachSession:A,runFind:p,countWhere:R}}var Qe={columns:"select",nested:"populate",filters:["match"],logical:["$and","$or","$nor"],paging:["limit","skip"],options:"options"};function ue(e,t){if(t)for(let o of Object.keys(t)){let n=t[o];n&&e.populate(typeof n=="object"?{path:o,...n}:o);}return e}var M=e=>e===void 0?[]:Array.isArray(e)?e:[e];function Ie(e,t){let o=internal.scopeConditions(t.scope);for(let n of o)if(!S(e,n.key))throw new core.QueryKitError({source:e.modelName,site:"filter",key:n.key,reason:"unknown-key"});return o.length?W(e,o):void 0}var Ee=new Set(["find","findOne","countDocuments","distinct"]),X=e=>typeof e=="object"&&e!==null&&(e.codeName==="MaxTimeMSExpired"||e.code===50);function je(e,t,o){e.maxTimeMS(o);let n=e.exec.bind(e);e.exec=(async(...c)=>{try{return await n(...c)}catch(l){throw X(l)?new core.QueryKitTimeoutError(t,o,{cause:l}):l}});}function de(e){let{runtime:t,m:o,diag:n,permits:c,policy:l,maxTimeMS:g,composeWhere:d}=e,w=u=>{let D=c(u)?S(o,u):void 0;return D||internal.reportSkip(n,{site:"aggregate",key:u,reason:"unknown-key"}),D},F=u=>{try{let D=o.find(u);return D.cast(o),D.getFilter()}catch{return u}};return {async aggregate(u){l.checkFilter(u.filter);for(let a of [u.groupBy,u.sum,u.avg,u.min,u.max].flatMap(h=>h===void 0?[]:Array.isArray(h)?h:[h]))l.checkAggregateKey(a);let D=M(u.groupBy).filter(a=>!!w(a)),y=F(d(u.filter,u.withDeleted)),x={_id:D.length?Object.fromEntries(D.map(a=>[a,`$${w(a)??a}`])):null};u.count&&(x.count={$sum:1});let r=(a,h,A)=>{for(let p of h){let R=w(p);R&&(x[`${A}_${p}`]={[a]:`$${R}`});}};r("$sum",M(u.sum),"sum"),r("$avg",M(u.avg),"avg"),r("$min",M(u.min),"min"),r("$max",M(u.max),"max");let i=o.aggregate([{$match:y},{$group:x}]),s=t.getSession();s&&(i=i.session(s)),i=i.option({maxTimeMS:g});let f;try{f=await i.exec();}catch(a){throw X(a)?new core.QueryKitTimeoutError(o.modelName,g,{cause:a}):a}return f.map(({_id:a,...h})=>({...typeof a=="object"&&a?a:{},...h}))}}}function pe(e){let{runtime:t,m:o,diag:n,guarded:c,permits:l,projection:g,policy:d,withOf:w,composeWhere:F,applyPopulate:u,attachSession:D}=e;return {async findCursor(y={}){let b=internal.clampPageSize(y.limit,t.defaultLimit,t.maxLimit),x=y.cursorKey??"id";d.checkFilter(y.filter),y.cursorKey!==void 0&&d.checkCursorKey(x);let r=y.order??"asc",i=y.direction??"forward",s=S(o,x);if(!s)throw new core.QueryKitError({source:n.source,site:"cursorKey",key:x,reason:"unknown-key"});let f=await internal.decodeCursorToken(y.cursor,{source:n.source,secret:t.cursorSecret,key:x}),a=f===void 0?void 0:j(o,s,f.k),h=f===void 0?void 0:j(o,"_id",f.id);if(a===P||h===P)throw new core.QueryKitError({source:n.source,site:"cursorKey",key:x,reason:"invalid-value"});let A=F(y.filter,y.withDeleted),p=i==="forward"?r==="asc":r==="desc",R=p?"$gt":"$lt",$=s!=="_id",L=f===void 0?void 0:$?{$or:[{[s]:{[R]:a}},{[s]:a,_id:{[R]:h}}]}:{[s]:{[R]:a}},m=L?Object.keys(A).length?{$and:[A,L]}:L:A,T=g(y.columns),k=T?{...T,[s]:1,_id:1}:void 0,C=O=>T!==void 0&&!!T[O],G=T===void 0?[]:[s,"_id"].filter(O=>c&&(!l(O)||!C(O))||O==="_id"&&O!==s&&!C(O)),v=o.find(m,k).sort($?{[s]:p?1:-1,_id:p?1:-1}:{[s]:p?1:-1}).limit(b+1);v=u(v,w(y.with));let he=await D(v).lean().exec(),V=await internal.cursorPage(he,{limit:b,direction:i,hasCursor:f!==void 0,cursorField:s,idField:"_id",secret:t.cursorSecret});return G.length>0&&(V.data=V.data.map(O=>Object.fromEntries(Object.entries(O).filter(([Te])=>!G.includes(Te))))),V}}}var me=1e3,Ve="\0";function ye(e,t){let{runtime:o,m:n,hasDeletedAt:c,deletedAtPath:l,forUpdate:g,composeWhere:d,mutationWhere:w,withId:F,attachSession:u}=e,D=r=>internal.clampPageSize(r.limit,o.defaultLimit,o.maxLimit),y=r=>({...internal.scopeDefaults(t.scope),...r,...internal.scopeDefaults(t.scope)}),b=(r,i)=>{let s=M(i.target),f=y(r),a={};for(let R of s){let $=S(n,R);if(!$)throw new Error(`upsert: unknown target field "${R}".`);a[$]=f[R];}let h=i.set?{...i.set}:Object.fromEntries(Object.entries(f).filter(([R])=>!s.includes(R))),A=Object.fromEntries(Object.entries(f).filter(([R])=>!s.includes(R)&&!(R in h))),p={};return Object.keys(h).length&&(p.$set=h),Object.keys(A).length&&(p.$setOnInsert=A),{filter:a,update:p}},x=async(r,i)=>{let s=n.findOneAndUpdate(r,i,{returnDocument:"after"});return await u(s).lean().exec()??void 0};return {async create(r){let[i]=await n.create([y(r)],{session:o.getSession()});return i.toObject()},async createMany(r){return r.length===0?[]:(await n.insertMany(r.map(y),{session:o.getSession()})).map(s=>s.toObject())},async upsert(r,i){let{filter:s,update:f}=b(r,i),a=n.findOneAndUpdate(s,f,{returnDocument:"after",upsert:true});return await u(a).lean().exec()},async upsertMany(r,i){if(r.length===0)return [];let s=r.map(p=>b(p,i));for(let p=0;p<s.length;p+=me){let R=s.slice(p,p+me).map($=>({updateOne:{filter:$.filter,update:$.update,upsert:true}}));await n.bulkWrite(R,{session:o.getSession()});}let f=await u(n.find({$or:s.map(p=>p.filter)})).lean().exec(),a=M(i.target).map(p=>S(n,p)).filter(p=>!!p),h=p=>a.map(R=>String(p[R])).join(Ve),A=new Map(f.map(p=>[h(p),p]));return s.map(p=>A.get(h(p.filter))).filter(p=>!!p)},updateById(r,i,s="id"){return x(d(F(void 0,s,r)),g(i))},async updateWhere(r,i,s={}){let f=w(r,"updateWhere"),{matchedCount:a}=await u(n.updateMany(f,g(i))).exec();if(!s.returning)return {count:a};let h=await u(n.find(f).limit(D(s.returning))).lean().exec();return {count:a,rows:h}},async deleteById(r,i="id"){let s=d(F(void 0,i,r));return await u(n.findOneAndDelete(s)).lean().exec()??void 0},async deleteWhere(r,i={}){let s=w(r,"deleteWhere"),f=i.returning?await u(n.find(s).limit(D(i.returning))).lean().exec():void 0,{deletedCount:a}=await u(n.deleteMany(s)).exec();return f?{count:a,rows:f}:{count:a}},async softDelete(r,i="id"){if(!c)throw new Error("softDelete: schema has no deletedAt path.");return x(d(F(void 0,i,r),false),g({[l]:new Date}))},async restore(r,i="id"){if(!c)throw new Error("restore: schema has no deletedAt path.");return x(d(F(void 0,i,r),true),g({[l]:null}))}}}function we(e){let{runtime:t,m:o,policy:n,projection:c,sortSpec:l,listSortSpec:g,withOf:d,composeWhere:w,withId:F,applyPopulate:u,attachSession:D,runFind:y,countWhere:b}=e,x=r=>{n.checkFilter(r.filter),n.checkSort(r.sort);};return {async findAll(r={}){return x(r),await y(w(r.filter,r.withDeleted),{sort:l(r.sort),columns:r.columns,with:d(r.with)})},async findOne(r={}){x(r);let i=o.findOne(w(r.filter,r.withDeleted),c(r.columns));return i=i.sort(l(r.sort)),i=u(i,d(r.with)),await D(i).lean().exec()??void 0},async findById(r,i={}){let{idKey:s="id",filter:f,columns:a,with:h}=i;n.checkFilter(f);let A=o.findOne(w(F(f,s,r),i.withDeleted),c(a));return A=u(A,h),await D(A).lean().exec()??void 0},async findList(r={}){x(r);let i=internal.clampPage(r.page),s=internal.clampPageSize(r.perPage,t.defaultPerPage,t.maxPerPage);n.checkPage({page:i,perPage:s});let f=w(r.filter,r.withDeleted),a=p=>y(f,{sort:g(r.sort),columns:r.columns,with:d(r.with),limit:p,skip:(i-1)*s});if(r.withTotal===false)return internal.offsetPageWithoutTotal(await a(s+1),i,s);let[h,A]=await Promise.all([a(s),b(f)]);return {data:h,meta:internal.offsetMeta(i,s,A)}},async findInfinite(r={}){let i=internal.clampPageSize(r.limit,t.defaultLimit,t.maxLimit),s=internal.clampOffset(r.offset);x(r),n.checkPage({offset:s});let f=await y(w(r.filter,r.withDeleted),{sort:g(r.sort),columns:r.columns,with:d(r.with),limit:i+1,skip:s});return internal.infinitePage(f,i,s)},async count(r){return n.checkFilter(r),b(w(r))},async exists(r){return n.checkFilter(r),await b(w(r))>0}}}function K(e,t,o={}){let n=ae(e,t,o);return {model:t,...we(n),...pe(n),...ye(n,o),...de(n),scoped(l){return K(e,t,{...o,scope:{...o.scope,...l}})}}}var et=2e3;function tt(e,t={}){let o=internal.createContextStore(),n={getSession:()=>o.get()?.session,defaultPerPage:t.defaultPerPage??core.DEFAULT_PER_PAGE,defaultLimit:t.defaultLimit??core.DEFAULT_LIMIT,maxPerPage:t.maxPerPage??core.DEFAULT_MAX_PER_PAGE,maxLimit:t.maxLimit??core.DEFAULT_MAX_LIMIT,strict:t.strict??false,onSkippedCondition:t.onSkippedCondition,limits:t.limits,cursorSecret:t.cursorSecret,timestamps:t.timestamps,maxTimeMS:t.maxTimeMS??et};function c(g,d,w){let F=typeof d=="function"?void 0:d,u=typeof d=="function"?d:w,D=K(n,g,F);return u?{...D,...u(D)}:D}return {repository:c,transaction:async g=>{if(o.get()?.session)return g();let d=await e.startSession();try{let w;return await d.withTransaction(async()=>{w=await o.run({session:d},g);}),w}finally{await d.endSession();}}}}function rt(){return core.createFilters()}Object.defineProperty(exports,"f",{enumerable:true,get:function(){return core.f}});exports.buildRepository=K;exports.createFilters=rt;exports.createRegistry=tt;//# sourceMappingURL=index.cjs.map
611
2
  //# sourceMappingURL=index.cjs.map