@classytic/mongokit 3.11.0 → 3.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -2
- package/dist/{PaginationEngine-B93fLCcO.mjs → PaginationEngine-BDaAsyPn.mjs} +2 -2
- package/dist/actions/index.d.mts +1 -1
- package/dist/actions/index.mjs +1 -1
- package/dist/ai/index.d.mts +1 -1
- package/dist/ai/index.mjs +3 -3
- package/dist/{error-CzYwrfUP.mjs → error-B00pI7dV.mjs} +11 -4
- package/dist/{index-C1MzVFsT.d.mts → index-D9cORR-o.d.mts} +1 -1
- package/dist/index.d.mts +16 -11
- package/dist/index.mjs +21 -6
- package/dist/{mongooseToJsonSchema-D1sdCOmj.mjs → mongooseToJsonSchema-B187ufOP.mjs} +67 -19
- package/dist/{mongooseToJsonSchema-BH6OFec5.d.mts → mongooseToJsonSchema-rtkdbBhf.d.mts} +44 -8
- package/dist/pagination/PaginationEngine.d.mts +2 -1
- package/dist/pagination/PaginationEngine.mjs +1 -1
- package/dist/plugins/index.d.mts +1 -1
- package/dist/plugins/index.mjs +1 -1
- package/dist/{types-xOyTec4n.d.mts → types-DXscZnLu.d.mts} +49 -81
- package/dist/{update-CWXfFC6O.mjs → update-CDxPtpsk.mjs} +145 -20
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +3 -3
- package/dist/{validation-chain.plugin-CLjEFr0c.mjs → validation-chain.plugin-BicIgzzt.mjs} +43 -180
- package/dist/{validation-chain.plugin-BGdr5KRw.d.mts → validation-chain.plugin-CH-xPoKM.d.mts} +17 -19
- package/package.json +146 -143
- /package/dist/{logger-DJ7egMnd.mjs → logger-8-VS1za6.mjs} +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import * as _$_classytic_repo_core_filter0 from "@classytic/repo-core/filter";
|
|
2
|
+
import { Filter } from "@classytic/repo-core/filter";
|
|
1
3
|
import * as _$mongoose from "mongoose";
|
|
2
4
|
import { Document, Model, PipelineStage, PopulateOptions, Types } from "mongoose";
|
|
3
|
-
import * as _$_classytic_repo_core_filter0 from "@classytic/repo-core/filter";
|
|
4
5
|
import * as _$_classytic_repo_core_repository0 from "@classytic/repo-core/repository";
|
|
5
6
|
import { DeleteManyResult, DeleteResult, LookupPopulateResult, UpdateManyResult } from "@classytic/repo-core/repository";
|
|
6
7
|
import { ValidationResult } from "@classytic/repo-core/schema";
|
|
8
|
+
import { AnyPaginationResult, OffsetPaginationResult } from "@classytic/repo-core/pagination";
|
|
9
|
+
import { HttpError } from "@classytic/repo-core/errors";
|
|
7
10
|
|
|
8
11
|
//#region src/query/LookupBuilder.d.ts
|
|
9
12
|
interface LookupOptions {
|
|
@@ -17,8 +20,15 @@ interface LookupOptions {
|
|
|
17
20
|
as?: string;
|
|
18
21
|
/** Whether to unwrap array to single object */
|
|
19
22
|
single?: boolean;
|
|
20
|
-
/**
|
|
21
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Field selection on the joined collection (shorthand for `$project`).
|
|
25
|
+
* Three accepted shapes — all map to a single `$project` stage:
|
|
26
|
+
* - `string` — CSV form (`'name,price'`); leading `-` marks an exclusion.
|
|
27
|
+
* - `readonly string[]` — array form (`['name', 'price']` / `['-status']`);
|
|
28
|
+
* mirrors `repo-core/lookup` `LookupSpec.select` for cross-kit parity.
|
|
29
|
+
* - `Record<string, 0 | 1>` — explicit Mongo projection map.
|
|
30
|
+
*/
|
|
31
|
+
select?: string | readonly string[] | Record<string, 0 | 1>;
|
|
22
32
|
/** Additional pipeline to run on the joined collection */
|
|
23
33
|
pipeline?: PipelineStage[];
|
|
24
34
|
/** Optional let variables for pipeline */
|
|
@@ -31,6 +41,18 @@ interface LookupOptions {
|
|
|
31
41
|
};
|
|
32
42
|
/** Sanitize pipeline stages (default: true). Set false only for trusted server-side pipelines */
|
|
33
43
|
sanitize?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Joined-side filter, compiled into a `$match` stage inside the lookup
|
|
46
|
+
* pipeline AFTER the auto-generated join correlation. Mirrors
|
|
47
|
+
* `repo-core/lookup/types.ts#LookupSpec.where` and sqlitekit's
|
|
48
|
+
* predicate-on-the-JOIN behavior, so cross-kit code that narrows the
|
|
49
|
+
* foreign side (`where: eq('status', 'active')`) gets identical rows
|
|
50
|
+
* from both backends.
|
|
51
|
+
*
|
|
52
|
+
* Accepts Filter IR or a plain mongo query — `compileFilterToMongo`
|
|
53
|
+
* routes each correctly.
|
|
54
|
+
*/
|
|
55
|
+
where?: Filter | Record<string, unknown>;
|
|
34
56
|
}
|
|
35
57
|
/**
|
|
36
58
|
* Fluent builder for MongoDB $lookup aggregation stage
|
|
@@ -307,7 +329,7 @@ interface IController<TDoc> {
|
|
|
307
329
|
* }
|
|
308
330
|
* ```
|
|
309
331
|
*/
|
|
310
|
-
list(context: IRequestContext): Promise<IControllerResponse<
|
|
332
|
+
list(context: IRequestContext): Promise<IControllerResponse<AnyPaginationResult<TDoc>>>;
|
|
311
333
|
/**
|
|
312
334
|
* Get single resource by ID
|
|
313
335
|
*
|
|
@@ -457,7 +479,7 @@ interface IResponseFormatter {
|
|
|
457
479
|
* Format paginated response
|
|
458
480
|
* @param result - Pagination result from Repository.getAll()
|
|
459
481
|
*/
|
|
460
|
-
paginated<T>(result:
|
|
482
|
+
paginated<T>(result: AnyPaginationResult<T>): IControllerResponse<AnyPaginationResult<T>>;
|
|
461
483
|
}
|
|
462
484
|
//#endregion
|
|
463
485
|
//#region src/types.d.ts
|
|
@@ -732,63 +754,6 @@ interface AggregatePaginationOptions {
|
|
|
732
754
|
/** Read preference for replica sets (e.g. 'secondaryPreferred') */
|
|
733
755
|
readPreference?: ReadPreferenceType;
|
|
734
756
|
}
|
|
735
|
-
/** Offset pagination result */
|
|
736
|
-
interface OffsetPaginationResult<T = unknown> {
|
|
737
|
-
/** Pagination method used */
|
|
738
|
-
method: 'offset';
|
|
739
|
-
/** Array of documents */
|
|
740
|
-
docs: T[];
|
|
741
|
-
/** Current page number */
|
|
742
|
-
page: number;
|
|
743
|
-
/** Documents per page */
|
|
744
|
-
limit: number;
|
|
745
|
-
/** Total document count */
|
|
746
|
-
total: number;
|
|
747
|
-
/** Total page count */
|
|
748
|
-
pages: number;
|
|
749
|
-
/** Whether next page exists */
|
|
750
|
-
hasNext: boolean;
|
|
751
|
-
/** Whether previous page exists */
|
|
752
|
-
hasPrev: boolean;
|
|
753
|
-
/** Performance warning for deep pagination */
|
|
754
|
-
warning?: string;
|
|
755
|
-
}
|
|
756
|
-
/** Keyset pagination result */
|
|
757
|
-
interface KeysetPaginationResult<T = unknown> {
|
|
758
|
-
/** Pagination method used */
|
|
759
|
-
method: 'keyset';
|
|
760
|
-
/** Array of documents */
|
|
761
|
-
docs: T[];
|
|
762
|
-
/** Documents per page */
|
|
763
|
-
limit: number;
|
|
764
|
-
/** Whether more documents exist */
|
|
765
|
-
hasMore: boolean;
|
|
766
|
-
/** Cursor token for next page */
|
|
767
|
-
next: string | null;
|
|
768
|
-
}
|
|
769
|
-
/** Aggregate pagination result */
|
|
770
|
-
interface AggregatePaginationResult<T = unknown> {
|
|
771
|
-
/** Pagination method used */
|
|
772
|
-
method: 'aggregate';
|
|
773
|
-
/** Array of documents */
|
|
774
|
-
docs: T[];
|
|
775
|
-
/** Current page number */
|
|
776
|
-
page: number;
|
|
777
|
-
/** Documents per page */
|
|
778
|
-
limit: number;
|
|
779
|
-
/** Total document count */
|
|
780
|
-
total: number;
|
|
781
|
-
/** Total page count */
|
|
782
|
-
pages: number;
|
|
783
|
-
/** Whether next page exists */
|
|
784
|
-
hasNext: boolean;
|
|
785
|
-
/** Whether previous page exists */
|
|
786
|
-
hasPrev: boolean;
|
|
787
|
-
/** Performance warning for deep pagination */
|
|
788
|
-
warning?: string;
|
|
789
|
-
}
|
|
790
|
-
/** Union type for all pagination results */
|
|
791
|
-
type PaginationResult<T = unknown> = OffsetPaginationResult<T> | KeysetPaginationResult<T> | AggregatePaginationResult<T>;
|
|
792
757
|
/** Session-only options — shared base for lightweight operations */
|
|
793
758
|
interface SessionOptions {
|
|
794
759
|
/** MongoDB session for transactions */
|
|
@@ -1136,6 +1101,27 @@ interface FieldPreset {
|
|
|
1136
1101
|
/** Additional fields for admins */
|
|
1137
1102
|
admin?: string[];
|
|
1138
1103
|
}
|
|
1104
|
+
/** Value type identifier for cursor serialization */
|
|
1105
|
+
type ValueType = 'date' | 'objectid' | 'boolean' | 'number' | 'string' | 'null' | 'unknown';
|
|
1106
|
+
/** Cursor payload */
|
|
1107
|
+
interface CursorPayload {
|
|
1108
|
+
/** Primary sort field value (legacy single-field) */
|
|
1109
|
+
v: string | number | boolean | null;
|
|
1110
|
+
/** Value type identifier (legacy single-field) */
|
|
1111
|
+
t: ValueType;
|
|
1112
|
+
/** Document ID */
|
|
1113
|
+
id: string;
|
|
1114
|
+
/** ID type identifier */
|
|
1115
|
+
idType: ValueType;
|
|
1116
|
+
/** Sort specification */
|
|
1117
|
+
sort: SortSpec;
|
|
1118
|
+
/** Cursor version */
|
|
1119
|
+
ver: number;
|
|
1120
|
+
/** Compound sort field values (multi-field keyset) */
|
|
1121
|
+
vals?: Record<string, string | number | boolean | null>;
|
|
1122
|
+
/** Compound sort value types */
|
|
1123
|
+
types?: Record<string, ValueType>;
|
|
1124
|
+
}
|
|
1139
1125
|
/** Decoded cursor */
|
|
1140
1126
|
interface DecodedCursor {
|
|
1141
1127
|
/** Primary sort field value (rehydrated) — legacy compat */
|
|
@@ -1349,24 +1335,6 @@ interface CascadeOptions {
|
|
|
1349
1335
|
/** Logger for cascade operations */
|
|
1350
1336
|
logger?: Logger;
|
|
1351
1337
|
}
|
|
1352
|
-
/** HTTP Error with status code */
|
|
1353
|
-
interface HttpError extends Error {
|
|
1354
|
-
status: number;
|
|
1355
|
-
validationErrors?: Array<{
|
|
1356
|
-
validator: string;
|
|
1357
|
-
error: string;
|
|
1358
|
-
}>;
|
|
1359
|
-
/**
|
|
1360
|
-
* Structured metadata for duplicate-key (E11000) errors. Safe to surface in
|
|
1361
|
-
* logs/audit. Includes the offending field names and a boolean-only mirror
|
|
1362
|
-
* of whether each field had a duplicate value — never the value itself
|
|
1363
|
-
* unless `parseDuplicateKeyError` was called with `{ exposeValues: true }`.
|
|
1364
|
-
*/
|
|
1365
|
-
duplicate?: {
|
|
1366
|
-
fields: string[]; /** Only populated when parseDuplicateKeyError was called with exposeValues:true. */
|
|
1367
|
-
values?: Record<string, unknown>;
|
|
1368
|
-
};
|
|
1369
|
-
}
|
|
1370
1338
|
/**
|
|
1371
1339
|
* Combines all plugin method types into a single type
|
|
1372
1340
|
* Useful when you're using all plugins and want full type safety
|
|
@@ -1497,4 +1465,4 @@ type AllPluginMethods<TDoc> = {
|
|
|
1497
1465
|
*/
|
|
1498
1466
|
type WithPlugins<TDoc, TRepo extends RepositoryInstance = RepositoryInstance> = TRepo & AllPluginMethods<TDoc>;
|
|
1499
1467
|
//#endregion
|
|
1500
|
-
export {
|
|
1468
|
+
export { RepositoryInstance as $, InferDocument as A, OffsetPaginationOptions as B, EventHandlers as C, IControllerResponse as Ct, FindOneAndUpdateOptions as D, LookupOptions as Dt, FieldPreset as E, LookupBuilder as Et, LookupPopulateOptions as F, PluginFunction as G, PaginationConfig as H, LookupPopulateResult as I, PrioritizedHook as J, PluginType as K, MinMaxResult as L, KeysOfType as M, KeysetPaginationOptions as N, GroupResult as O, Logger as P, RepositoryEvent as Q, NonNullableFields as R, DocField as S, IController as St, EventPhase as T, IResponseFormatter as Tt, PartialBy as U, OperationOptions as V, Plugin as W, ReadPreferenceType as X, ReadOptions as Y, RepositoryContext as Z, CursorPayload as _, ValidationChainOptions as _t, AnyModel as a, SoftDeleteFilterMode as at, DeleteManyResult as b, WithPlugins as bt, CacheOperationOptions as c, SortDirection as ct, CacheableOptions as d, UpdateInput as dt, RepositoryOperation as et, CascadeOptions as f, UpdateManyResult as ft, CreateOptions as g, UserContext as gt, CreateInput as h, UpdateWithValidationResult as ht, AnyDocument as i, SessionOptions as it, InferRawDoc as j, HookMode as k, CacheOptions as l, SortSpec as lt, CollationOptions as m, UpdatePatch as mt, AggregatePaginationOptions as n, RequiredBy as nt, BasePaginationOptions as o, SoftDeleteOptions as ot, CascadeRelation as p, UpdateOptions as pt, PopulateSpec as q, AllPluginMethods as r, SelectSpec as rt, CacheAdapter as s, SoftDeleteRepository as st, AggregateOptions as t, RepositoryOptions as tt, CacheStats as u, Strict as ut, DecodedCursor as v, ValidatorDefinition as vt, EventPayload as w, IRequestContext as wt, DeleteResult as x, WithTransactionOptions as xt, DeepPartial as y, ValueType as yt, ObjectId as z };
|
|
@@ -1,6 +1,103 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
-
import { r as warn } from "./logger-
|
|
3
|
-
import { t as createError } from "./error-
|
|
2
|
+
import { r as warn } from "./logger-8-VS1za6.mjs";
|
|
3
|
+
import { t as createError } from "./error-B00pI7dV.mjs";
|
|
4
|
+
import { isFilter } from "@classytic/repo-core/filter";
|
|
5
|
+
//#region src/filter/compile.ts
|
|
6
|
+
/**
|
|
7
|
+
* Compile a Filter IR node to a MongoDB query object. Returns `{}`
|
|
8
|
+
* when the node is `TRUE` (or an empty `and`) so callers can merge
|
|
9
|
+
* with other scopes without special-casing.
|
|
10
|
+
*
|
|
11
|
+
* The `input` is typed as `unknown` so callers can pass either a
|
|
12
|
+
* Filter IR node or an already-built Mongo query and we dispatch
|
|
13
|
+
* appropriately. This keeps the portable API ergonomic for apps that
|
|
14
|
+
* write Mongo queries directly today.
|
|
15
|
+
*/
|
|
16
|
+
function compileFilterToMongo(input) {
|
|
17
|
+
if (!input) return {};
|
|
18
|
+
if (!isFilter(input)) return input;
|
|
19
|
+
return compile(input);
|
|
20
|
+
}
|
|
21
|
+
function compile(filter) {
|
|
22
|
+
switch (filter.op) {
|
|
23
|
+
case "true": return {};
|
|
24
|
+
case "false": return { $expr: false };
|
|
25
|
+
case "eq": return { [filter.field]: filter.value };
|
|
26
|
+
case "ne": return filter.value === null ? { [filter.field]: { $ne: null } } : { $and: [{ [filter.field]: { $ne: filter.value } }, { [filter.field]: { $ne: null } }] };
|
|
27
|
+
case "gt": return { [filter.field]: { $gt: filter.value } };
|
|
28
|
+
case "gte": return { [filter.field]: { $gte: filter.value } };
|
|
29
|
+
case "lt": return { [filter.field]: { $lt: filter.value } };
|
|
30
|
+
case "lte": return { [filter.field]: { $lte: filter.value } };
|
|
31
|
+
case "in":
|
|
32
|
+
if (filter.values.length === 0) return { $expr: false };
|
|
33
|
+
return { [filter.field]: { $in: [...filter.values] } };
|
|
34
|
+
case "nin":
|
|
35
|
+
if (filter.values.length === 0) return {};
|
|
36
|
+
return { [filter.field]: { $nin: [...filter.values] } };
|
|
37
|
+
case "exists": return filter.exists ? { [filter.field]: { $ne: null } } : { [filter.field]: null };
|
|
38
|
+
case "like": return { [filter.field]: likeToRegex(filter.pattern, filter.caseSensitivity) };
|
|
39
|
+
case "regex": return { [filter.field]: { $regex: filter.pattern } };
|
|
40
|
+
case "and": {
|
|
41
|
+
if (filter.children.length === 0) return {};
|
|
42
|
+
const parts = filter.children.map(compile);
|
|
43
|
+
if (parts.length === 1) return parts[0];
|
|
44
|
+
return { $and: parts };
|
|
45
|
+
}
|
|
46
|
+
case "or": {
|
|
47
|
+
if (filter.children.length === 0) return { $expr: false };
|
|
48
|
+
const parts = filter.children.map(compile);
|
|
49
|
+
if (parts.length === 1) return parts[0];
|
|
50
|
+
return { $or: parts };
|
|
51
|
+
}
|
|
52
|
+
case "not": return { $nor: [compile(filter.child)] };
|
|
53
|
+
case "raw": throw new Error("mongokit/filter: `raw` is a SQL escape hatch and has no safe MongoDB translation. Pass a Mongo query object directly to the filter slot instead.");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Translate a SQL-style LIKE pattern to a MongoDB regex.
|
|
58
|
+
*
|
|
59
|
+
* SQL semantics:
|
|
60
|
+
* - `%` → match zero or more characters (`.*`)
|
|
61
|
+
* - `_` → match exactly one character (`.`)
|
|
62
|
+
* - `\%` → literal percent (escape via backslash)
|
|
63
|
+
* - `\_` → literal underscore
|
|
64
|
+
* - `\\` → literal backslash
|
|
65
|
+
* - everything else is literal — regex metacharacters get escaped.
|
|
66
|
+
*
|
|
67
|
+
* The naive implementation `pattern.replace(/%/g, '.*')` is wrong:
|
|
68
|
+
* it also expands escaped `\%` into a wildcard. We walk the pattern
|
|
69
|
+
* char-by-char, honoring backslash escapes, so `like('notes', '50\\% off')`
|
|
70
|
+
* matches the literal string `50% off` instead of `50<anything> off`.
|
|
71
|
+
*
|
|
72
|
+
* Anchored with `^` / `$` so LIKE matches the whole field value, the
|
|
73
|
+
* same way sqlitekit's `LIKE` semantically does.
|
|
74
|
+
*/
|
|
75
|
+
function likeToRegex(pattern, caseSensitivity) {
|
|
76
|
+
let out = "^";
|
|
77
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
78
|
+
const ch = pattern[i];
|
|
79
|
+
if (ch === "\\" && i + 1 < pattern.length) {
|
|
80
|
+
const next = pattern[i + 1];
|
|
81
|
+
if (next === "%" || next === "_" || next === "\\") {
|
|
82
|
+
out += escapeRegexChar(next);
|
|
83
|
+
i++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (ch === "%") out += ".*";
|
|
88
|
+
else if (ch === "_") out += ".";
|
|
89
|
+
else out += escapeRegexChar(ch);
|
|
90
|
+
}
|
|
91
|
+
out += "$";
|
|
92
|
+
return caseSensitivity === "sensitive" ? { $regex: out } : {
|
|
93
|
+
$regex: out,
|
|
94
|
+
$options: "i"
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function escapeRegexChar(ch) {
|
|
98
|
+
return /[.*+?^${}()|[\]\\]/.test(ch) ? `\\${ch}` : ch;
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
4
101
|
//#region src/query/LookupBuilder.ts
|
|
5
102
|
/** Stages that are never valid inside a $lookup pipeline */
|
|
6
103
|
const BLOCKED_PIPELINE_STAGES = [
|
|
@@ -191,28 +288,24 @@ var LookupBuilder = class LookupBuilder {
|
|
|
191
288
|
const builder = new LookupBuilder(lookup.from).localField(lookup.localField).foreignField(lookup.foreignField);
|
|
192
289
|
if (lookup.as) builder.as(lookup.as);
|
|
193
290
|
if (lookup.single) builder.single(lookup.single);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
]);
|
|
291
|
+
const stages = [];
|
|
292
|
+
const appendCallerStages = (raw) => {
|
|
293
|
+
if (raw.length === 0) return;
|
|
294
|
+
stages.push(...LookupBuilder.sanitizePipeline(raw));
|
|
295
|
+
};
|
|
296
|
+
const compiledWhere = lookup.where ? compileFilterToMongo(lookup.where) : void 0;
|
|
297
|
+
const hasWhere = !!compiledWhere && Object.keys(compiledWhere).length > 0;
|
|
298
|
+
if (!!(lookup.select || lookup.pipeline || hasWhere)) {
|
|
299
|
+
stages.push({ $match: { $expr: { $eq: [`$${lookup.foreignField}`, "$$lookupJoinVal"] } } });
|
|
300
|
+
if (hasWhere && compiledWhere) appendCallerStages([{ $match: compiledWhere }]);
|
|
301
|
+
if (lookup.pipeline) appendCallerStages(lookup.pipeline);
|
|
302
|
+
if (lookup.select) stages.push({ $project: compileSelectToProjection(lookup.select) });
|
|
303
|
+
builder.pipeline(stages);
|
|
208
304
|
builder.let({
|
|
209
305
|
lookupJoinVal: `$${lookup.localField}`,
|
|
210
306
|
...lookup.let || {}
|
|
211
307
|
});
|
|
212
308
|
builder.sanitize(false);
|
|
213
|
-
} else if (lookup.pipeline) {
|
|
214
|
-
builder.pipeline(lookup.pipeline);
|
|
215
|
-
if (lookup.let) builder.let(lookup.let);
|
|
216
309
|
} else if (lookup.let) builder.let(lookup.let);
|
|
217
310
|
return builder.build();
|
|
218
311
|
});
|
|
@@ -281,6 +374,38 @@ var LookupBuilder = class LookupBuilder {
|
|
|
281
374
|
}
|
|
282
375
|
};
|
|
283
376
|
/**
|
|
377
|
+
* Normalize the three accepted `LookupOptions.select` shapes into a single
|
|
378
|
+
* Mongo `$project` map.
|
|
379
|
+
*
|
|
380
|
+
* - `string` → CSV form, leading `-` excludes (`'name,-status'`)
|
|
381
|
+
* - `readonly string[]` → array form, leading `-` excludes (`['-status']`)
|
|
382
|
+
* - `Record<string, 0|1>` → already a projection map, copied as-is
|
|
383
|
+
*
|
|
384
|
+
* The array form mirrors `repo-core`'s `LookupSpec.select` so cross-kit
|
|
385
|
+
* callers (`select: ['name']`) compile identically on mongokit and sqlitekit.
|
|
386
|
+
*/
|
|
387
|
+
function compileSelectToProjection(select) {
|
|
388
|
+
if (typeof select === "string") {
|
|
389
|
+
const projection = {};
|
|
390
|
+
for (const field of select.split(",").map((f) => f.trim())) {
|
|
391
|
+
if (!field) continue;
|
|
392
|
+
if (field.startsWith("-")) projection[field.substring(1)] = 0;
|
|
393
|
+
else projection[field] = 1;
|
|
394
|
+
}
|
|
395
|
+
return projection;
|
|
396
|
+
}
|
|
397
|
+
if (Array.isArray(select)) {
|
|
398
|
+
const projection = {};
|
|
399
|
+
for (const field of select) {
|
|
400
|
+
if (!field) continue;
|
|
401
|
+
if (field.startsWith("-")) projection[field.substring(1)] = 0;
|
|
402
|
+
else projection[field] = 1;
|
|
403
|
+
}
|
|
404
|
+
return projection;
|
|
405
|
+
}
|
|
406
|
+
return { ...select };
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
284
409
|
* Performance Guidelines for $lookup at Scale:
|
|
285
410
|
*
|
|
286
411
|
* 1. **Index Requirements** (Critical for millions of records):
|
|
@@ -918,4 +1043,4 @@ async function pullFromArray(Model, id, field, value, options = {}) {
|
|
|
918
1043
|
return update(Model, id, { $pull: { [field]: value } }, options);
|
|
919
1044
|
}
|
|
920
1045
|
//#endregion
|
|
921
|
-
export { upsert as _, count as a, LookupBuilder as b, getByQuery as c, deleteById as d, deleteByQuery as f, create_exports as g, createMany as h, update_exports as i, getOrCreate as l, create as m, update as n, exists as o, delete_exports as p, updateByQuery as r, getById as s, findOneAndUpdate as t, read_exports as u, aggregate_exports as v, distinct as y };
|
|
1046
|
+
export { upsert as _, count as a, LookupBuilder as b, getByQuery as c, deleteById as d, deleteByQuery as f, create_exports as g, createMany as h, update_exports as i, getOrCreate as l, create as m, update as n, exists as o, delete_exports as p, updateByQuery as r, getById as s, findOneAndUpdate as t, read_exports as u, aggregate_exports as v, compileFilterToMongo as x, distinct as y };
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as createFieldPreset, c as getMongooseProjection, i as configureLogger, l as createError, n as buildCrudSchemasFromMongooseSchema, o as filterResponseData, r as createMemoryCache, s as getFieldsForUser, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-
|
|
1
|
+
import { lt as SortSpec, q as PopulateSpec, rt as SelectSpec } from "../types-DXscZnLu.mjs";
|
|
2
|
+
import { a as createFieldPreset, c as getMongooseProjection, i as configureLogger, l as createError, n as buildCrudSchemasFromMongooseSchema, o as filterResponseData, r as createMemoryCache, s as getFieldsForUser, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-rtkdbBhf.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/cache-keys.d.ts
|
|
5
5
|
/**
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as configureLogger } from "../logger-
|
|
2
|
-
import { t as createError } from "../error-
|
|
1
|
+
import { t as configureLogger } from "../logger-8-VS1za6.mjs";
|
|
2
|
+
import { t as createError } from "../error-B00pI7dV.mjs";
|
|
3
3
|
import { n as isObjectIdInstance, r as isValidIdForType, t as getSchemaIdType } from "../id-resolution-e4BT9zAV.mjs";
|
|
4
4
|
import { a as byIdKey, c as listQueryKey, i as getMongooseProjection, l as modelPattern, n as filterResponseData, o as byQueryKey, r as getFieldsForUser, s as listPattern, t as createFieldPreset, u as versionKey } from "../field-selection-dI0m4RBs.mjs";
|
|
5
|
-
import { n as buildCrudSchemasFromMongooseSchema, r as createMemoryCache, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-
|
|
5
|
+
import { n as buildCrudSchemasFromMongooseSchema, r as createMemoryCache, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-B187ufOP.mjs";
|
|
6
6
|
export { buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, byIdKey, byQueryKey, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getMongooseProjection, getSchemaIdType, isObjectIdInstance, isValidIdForType, listPattern, listQueryKey, modelPattern, versionKey };
|