@classytic/mongokit 3.6.4 → 3.8.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.
Files changed (33) hide show
  1. package/dist/{PaginationEngine-CunXkR9p.mjs → PaginationEngine-C2_tAvAD.mjs} +8 -5
  2. package/dist/actions/index.d.mts +1 -1
  3. package/dist/actions/index.mjs +1 -1
  4. package/dist/ai/index.d.mts +23 -1
  5. package/dist/ai/index.mjs +194 -12
  6. package/dist/{cursor-CHToazHy.mjs → cursor-J84rR6wG.mjs} +11 -7
  7. package/dist/error-Lw9GEAro.mjs +46 -0
  8. package/dist/{index-jzO5hMtF.d.mts → index-DlSAFIc9.d.mts} +10 -2
  9. package/dist/index.d.mts +97 -9
  10. package/dist/index.mjs +53 -12
  11. package/dist/logger-DJ7egMnd.mjs +30 -0
  12. package/dist/{mongooseToJsonSchema-BvD1NDl9.d.mts → mongooseToJsonSchema-BZaKOXXI.d.mts} +18 -2
  13. package/dist/{mongooseToJsonSchema-BlfkI9LC.mjs → mongooseToJsonSchema-DUwXPStK.mjs} +1 -1
  14. package/dist/pagination/PaginationEngine.d.mts +3 -1
  15. package/dist/pagination/PaginationEngine.mjs +1 -1
  16. package/dist/plugins/index.d.mts +2 -2
  17. package/dist/plugins/index.mjs +2 -2
  18. package/dist/query/primitives/coercion.d.mts +1 -1
  19. package/dist/query/primitives/coercion.mjs +1 -1
  20. package/dist/query/primitives/indexes.d.mts +1 -1
  21. package/dist/{sort-C-BJEWUZ.mjs → sort-CrZjTK25.mjs} +19 -4
  22. package/dist/{types-BoH1qdHo.d.mts → types-D57SZJeE.d.mts} +83 -3
  23. package/dist/{update-AVfKWNGt.mjs → update-yjSoNnWy.mjs} +26 -2
  24. package/dist/utils/index.d.mts +2 -2
  25. package/dist/utils/index.mjs +5 -4
  26. package/dist/{validation-chain.plugin-7A5qGEQq.mjs → validation-chain.plugin-DwoOOWse.mjs} +460 -144
  27. package/dist/{validation-chain.plugin-BXx6Ogpp.d.mts → validation-chain.plugin-wGF34Ym-.d.mts} +27 -4
  28. package/package.json +8 -3
  29. package/dist/error-Bpbi_NKo.mjs +0 -63
  30. /package/dist/{coercion-B19MD38s.d.mts → coercion-oM8G4b6m.d.mts} +0 -0
  31. /package/dist/{field-selection-C5o7jyyA.mjs → field-selection-dI0m4RBs.mjs} +0 -0
  32. /package/dist/{id-resolution-LZ-cCwR5.mjs → id-resolution-e4BT9zAV.mjs} +0 -0
  33. /package/dist/{indexes-qIBCZ3mY.d.mts → indexes-C26GNZRU.d.mts} +0 -0
@@ -1,6 +1,7 @@
1
- import { a as warn, t as createError } from "./error-Bpbi_NKo.mjs";
2
- import { n as encodeCursor, r as resolveCursorFilter } from "./cursor-CHToazHy.mjs";
3
- import { r as validateKeysetSort, t as getPrimaryField } from "./sort-C-BJEWUZ.mjs";
1
+ import { r as warn } from "./logger-DJ7egMnd.mjs";
2
+ import { t as createError } from "./error-Lw9GEAro.mjs";
3
+ import { n as encodeCursor, r as resolveCursorFilter } from "./cursor-J84rR6wG.mjs";
4
+ import { r as validateKeysetSort, t as getPrimaryField } from "./sort-CrZjTK25.mjs";
4
5
  //#region src/pagination/utils/index-hint.ts
5
6
  /**
6
7
  * Read a Mongoose schema's declared indexes, defensively.
@@ -197,6 +198,8 @@ var PaginationEngine = class {
197
198
  maxPage: config.maxPage ?? 1e4,
198
199
  deepPageThreshold: config.deepPageThreshold ?? 100,
199
200
  cursorVersion: config.cursorVersion ?? 1,
201
+ minCursorVersion: config.minCursorVersion ?? 1,
202
+ strictKeysetSortFields: config.strictKeysetSortFields,
200
203
  useEstimatedCount: config.useEstimatedCount ?? false
201
204
  };
202
205
  }
@@ -298,7 +301,7 @@ var PaginationEngine = class {
298
301
  const { filters = {}, sort, after, limit = this.config.defaultLimit, select, populate = [], lean = true, session, hint, maxTimeMS, readPreference, collation } = options;
299
302
  if (!sort) throw createError(400, "sort is required for keyset pagination");
300
303
  const sanitizedLimit = validateLimit(limit, this.config);
301
- const normalizedSort = validateKeysetSort(sort);
304
+ const normalizedSort = validateKeysetSort(sort, this.config.strictKeysetSortFields);
302
305
  const sortWithoutIdTail = stripTrailingIdTiebreaker(normalizedSort);
303
306
  const filterKeys = Object.keys(filters).filter((k) => !k.startsWith("$"));
304
307
  const effectiveSortFields = Object.keys(sortWithoutIdTail);
@@ -307,7 +310,7 @@ var PaginationEngine = class {
307
310
  warn(`[mongokit] Keyset pagination with filters [${filterKeys.join(", ")}] and sort [${effectiveSortFields.join(", ")}] has no matching schema-declared compound index. For O(1) performance, declare: { ${indexFields.join(", ")} }. (Collection-level indexes created outside the schema are not visible here.)`);
308
311
  }
309
312
  let query = { ...filters };
310
- if (after) query = resolveCursorFilter(after, normalizedSort, this.config.cursorVersion, query);
313
+ if (after) query = resolveCursorFilter(after, normalizedSort, this.config.cursorVersion, query, this.config.minCursorVersion);
311
314
  const effectiveSelect = ensureKeysetSelectIncludesCursorFields(select, normalizedSort);
312
315
  let mongoQuery = this.Model.find(query);
313
316
  if (effectiveSelect) mongoQuery = mongoQuery.select(effectiveSelect);
@@ -1,2 +1,2 @@
1
- import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-jzO5hMtF.mjs";
1
+ import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-DlSAFIc9.mjs";
2
2
  export { aggregate_d_exports as aggregate, create_d_exports as create, delete_d_exports as deleteActions, read_d_exports as read, update_d_exports as update };
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "../chunk-CfYAbeIz.mjs";
2
- import { _ as aggregate_exports, f as delete_exports, h as create_exports, l as read_exports, r as update_exports } from "../update-AVfKWNGt.mjs";
2
+ import { g as create_exports, i as update_exports, p as delete_exports, u as read_exports, v as aggregate_exports } from "../update-yjSoNnWy.mjs";
3
3
  //#region src/actions/index.ts
4
4
  var actions_exports = /* @__PURE__ */ __exportAll({
5
5
  aggregate: () => aggregate_exports,
@@ -1,4 +1,4 @@
1
- import { J as Plugin } from "../types-BoH1qdHo.mjs";
1
+ import { Y as Plugin } from "../types-D57SZJeE.mjs";
2
2
  import { ClientSession, PipelineStage } from "mongoose";
3
3
 
4
4
  //#region src/ai/types.d.ts
@@ -106,6 +106,28 @@ interface VectorPluginOptions {
106
106
  * If not provided, the error propagates and blocks the write.
107
107
  */
108
108
  onEmbedError?: (error: Error, doc: unknown) => void;
109
+ /**
110
+ * SSRF defense for media URLs (`image`, `audio`, entries in `media`).
111
+ *
112
+ * If set, only URLs whose origin (`scheme://host[:port]`) matches one of
113
+ * these entries are forwarded to `embedFn`. Entries can be exact origins
114
+ * (`https://cdn.example.com`) or wildcard hosts (`https://*.example.com`).
115
+ * When the allowlist is active, URLs failing validation are dropped with a
116
+ * warning and the doc is embedded without them (never passed to embedFn).
117
+ *
118
+ * Undefined (default) = no allowlist check — same as prior behavior.
119
+ */
120
+ allowedMediaOrigins?: string[];
121
+ /**
122
+ * When true, reject URLs that resolve to a private, loopback, or
123
+ * link-local IP literal (e.g., `http://169.254.169.254/…` cloud metadata,
124
+ * `http://127.0.0.1`, RFC1918 addresses). Hostnames that later resolve to
125
+ * private IPs via DNS are NOT caught here — that rebinding class of SSRF
126
+ * must be handled in your network/egress layer.
127
+ *
128
+ * Default: `false` (legacy behavior). Flip to `true` for production setups.
129
+ */
130
+ blockPrivateIpUrls?: boolean;
109
131
  }
110
132
  //#endregion
111
133
  //#region src/ai/vector.plugin.d.ts
package/dist/ai/index.mjs CHANGED
@@ -1,3 +1,163 @@
1
+ import { r as warn } from "../logger-DJ7egMnd.mjs";
2
+ //#region src/ai/url-policy.ts
3
+ /**
4
+ * URL policy helpers for the vector plugin media pipeline.
5
+ *
6
+ * Protects callers from passing hostile document URLs straight into an
7
+ * embedding service that may fetch them. The policy is pure / side-effect-
8
+ * free so it can be reused by anyone composing their own embed pipeline.
9
+ */
10
+ /**
11
+ * Returns true when the string is a safe media URL under the given policy.
12
+ *
13
+ * - Non-URL strings (data URLs, base64 blobs, relative paths) are accepted —
14
+ * they never trigger an outbound HTTP fetch, so SSRF isn't a concern.
15
+ * - `data:` URLs are accepted.
16
+ * - `http` / `https` URLs must pass:
17
+ * a) origin allowlist (if `allowedOrigins` is set)
18
+ * b) private-IP rejection (if `blockPrivateIpUrls` is true)
19
+ *
20
+ * Any parse failure returns `true` only when strict mode is off — a string
21
+ * that doesn't parse as a URL cannot be used to SSRF either. In strict mode
22
+ * (either option set), unparseable http(s)-looking strings are rejected.
23
+ */
24
+ function isMediaUrlAllowed(value, policy = {}) {
25
+ if (typeof value !== "string" || value.length === 0) return false;
26
+ if (value.startsWith("data:")) return true;
27
+ if (!/^https?:\/\//i.test(value)) return true;
28
+ let url;
29
+ try {
30
+ url = new URL(value);
31
+ } catch {
32
+ return false;
33
+ }
34
+ if (policy.blockPrivateIpUrls && isPrivateOrLocalHost(url.hostname)) return false;
35
+ if (policy.allowedOrigins && policy.allowedOrigins.length > 0) {
36
+ const origin = `${url.protocol}//${url.host}`;
37
+ if (!policy.allowedOrigins.some((entry) => matchesOriginEntry(origin, entry, url))) return false;
38
+ }
39
+ return true;
40
+ }
41
+ function matchesOriginEntry(origin, entry, url) {
42
+ if (!entry) return false;
43
+ if (entry === origin) return true;
44
+ const wildcardMatch = entry.match(/^(https?:)\/\/\*\.(.+)$/i);
45
+ if (wildcardMatch) {
46
+ const [, entryProtocol, suffix] = wildcardMatch;
47
+ if (url.protocol.toLowerCase() !== entryProtocol.toLowerCase()) return false;
48
+ return url.hostname.toLowerCase().endsWith("." + suffix.toLowerCase());
49
+ }
50
+ return false;
51
+ }
52
+ /**
53
+ * Literal-IP private/loopback/link-local checks. Does NOT perform DNS
54
+ * resolution — callers who need rebinding-proof behavior must handle that
55
+ * at the HTTP layer.
56
+ */
57
+ function isPrivateOrLocalHost(hostname) {
58
+ const h = hostname.toLowerCase().replace(/^\[|\]$/g, "");
59
+ if (h === "::1" || h.startsWith("fe80:") || h.startsWith("fc") || h.startsWith("fd")) return true;
60
+ if (h === "localhost" || h === "localhost.localdomain") return true;
61
+ const ipv4 = h.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
62
+ if (!ipv4) return false;
63
+ const octets = ipv4.slice(1).map(Number);
64
+ if (octets.some((o) => o < 0 || o > 255)) return false;
65
+ const [a, b] = octets;
66
+ if (a === 10) return true;
67
+ if (a === 127) return true;
68
+ if (a === 169 && b === 254) return true;
69
+ if (a === 172 && b >= 16 && b <= 31) return true;
70
+ if (a === 192 && b === 168) return true;
71
+ if (a === 0) return true;
72
+ if (a === 100 && b >= 64 && b <= 127) return true;
73
+ return false;
74
+ }
75
+ //#endregion
76
+ //#region src/ai/vector-error-hints.ts
77
+ /**
78
+ * Pure classifier. Takes the raw error and the context the plugin knows
79
+ * (index name, dimensions, filter paths it tried to use) and returns a hint.
80
+ *
81
+ * Returns `{ code: 'UNKNOWN', ... }` when no pattern matches — callers
82
+ * should then fall back to rethrowing the original.
83
+ */
84
+ function classifyVectorSearchError(err, ctx) {
85
+ const lower = extractErrorMessage(err).toLowerCase();
86
+ if (lower.includes("unrecognized pipeline stage name: '$vectorsearch'") || lower.includes("unrecognized pipeline stage name: \"$vectorsearch\"") || lower.includes("$vectorsearch is only supported") || lower.includes("$vectorsearch stage is only allowed") || lower.includes("$vectorsearch is only allowed") || lower.includes("unknown top level operator: $vectorsearch") || lower.includes("$vectorsearch") && lower.includes("atlas") && (lower.includes("only") || lower.includes("require"))) return {
87
+ code: "NOT_ATLAS",
88
+ summary: "MongoDB did not recognize the `$vectorSearch` stage. This stage is Atlas-only — it doesn't exist on standalone MongoDB, self-hosted Community/Enterprise, or mongodb-memory-server.",
89
+ hints: [
90
+ "Run against MongoDB Atlas — create a cluster and point your app at its connection string.",
91
+ "If you're testing locally, use the integration suite that mocks `$vectorSearch` (see tests/integration/vector-*.test.ts) rather than calling `searchSimilar` directly.",
92
+ "Atlas Free tier (M0) supports Vector Search as of late 2024; older free tiers do not."
93
+ ]
94
+ };
95
+ if (lower.includes("$vectorsearch") && lower.includes("index") || lower.includes("couldn't find index") || lower.includes("search index") && lower.includes("not found") || lower.includes("no such index") || lower.includes("index") && lower.includes(ctx.indexName.toLowerCase())) return {
96
+ code: "INDEX_NOT_FOUND",
97
+ summary: `Atlas reported the vector search index "${ctx.indexName}" is not available on the target collection.`,
98
+ hints: [
99
+ `Verify an index named "${ctx.indexName}" exists on the collection in the Atlas UI (Search → Indexes) or via \`db.collection.listSearchIndexes()\`.`,
100
+ `If you just created the index, wait until it reports \`queryable: true\`. Builds typically take 1–3 minutes on new clusters.`,
101
+ `The index's \`numDimensions\` must match your vector config (${ctx.dimensions}), and \`path\` must match the field name.`
102
+ ]
103
+ };
104
+ if (lower.includes("not a supported filter field") || lower.includes("is not a filter field") || lower.includes("filter") && lower.includes("path") && lower.includes("not") || lower.includes("unknown filter path")) return {
105
+ code: "FILTER_FIELD_NOT_INDEXED",
106
+ summary: "Atlas rejected the `filter` clause because at least one of its paths is not declared as a `filter`-typed field in the vector search index.",
107
+ hints: [
108
+ `Add every path you filter on as \`{ type: "filter", path: "<field>" }\` in the index definition.`,
109
+ `Paths passed in this call: ${ctx.filterPaths?.length ? ctx.filterPaths.join(", ") : "(none declared)"}.`,
110
+ `Example for multi-tenant: index must include \`{ type: "filter", path: "tenantId" }\` when you call \`searchSimilar({ filter: { tenantId: "..." } })\`.`
111
+ ]
112
+ };
113
+ if (lower.includes("dimension") && (lower.includes("mismatch") || lower.includes("expected"))) return {
114
+ code: "DIMENSION_MISMATCH",
115
+ summary: "Atlas rejected the query vector — its length does not match the index definition.",
116
+ hints: [`Your vectorPlugin config says \`dimensions: ${ctx.dimensions}\`. The Atlas index for "${ctx.indexName}" must declare the same \`numDimensions\`.`, `If the index was created with a different dimension count, re-create it with the correct value (indexes are immutable for dimensions).`]
117
+ };
118
+ if (lower.includes("vectorsearch") && (lower.includes("requires") || lower.includes("expected") || lower.includes("missing"))) return {
119
+ code: "MALFORMED_PIPELINE",
120
+ summary: "The `$vectorSearch` stage is missing a required argument. This is almost always a mongokit bug — please file an issue with the full error text.",
121
+ hints: ["Capture the original error (`err.cause`) and attach it to the bug report."]
122
+ };
123
+ return {
124
+ code: "UNKNOWN",
125
+ summary: "",
126
+ hints: []
127
+ };
128
+ }
129
+ /**
130
+ * Wrap an error-producing promise so known-recognizable failures become
131
+ * mongokit errors with hint context. On unrecognized errors, rethrows the
132
+ * original untouched.
133
+ */
134
+ async function withVectorErrorHints(op, ctx) {
135
+ try {
136
+ return await op();
137
+ } catch (err) {
138
+ const hint = classifyVectorSearchError(err, ctx);
139
+ if (hint.code === "UNKNOWN") throw err;
140
+ const message = `[mongokit:vector] ${hint.summary}\n` + hint.hints.map((h) => ` • ${h}`).join("\n");
141
+ const wrapped = new Error(message, { cause: err });
142
+ wrapped.code = hint.code;
143
+ throw wrapped;
144
+ }
145
+ }
146
+ function extractErrorMessage(err) {
147
+ if (typeof err === "string") return err;
148
+ if (err && typeof err === "object") {
149
+ const e = err;
150
+ if (typeof e.message === "string") return e.message;
151
+ if (typeof e.errmsg === "string") return e.errmsg;
152
+ try {
153
+ return JSON.stringify(err);
154
+ } catch {
155
+ return String(err);
156
+ }
157
+ }
158
+ return String(err);
159
+ }
160
+ //#endregion
1
161
  //#region src/ai/vector.plugin.ts
2
162
  /** Maximum numCandidates allowed by Atlas Vector Search */
3
163
  const MAX_NUM_CANDIDATES = 1e4;
@@ -28,23 +188,35 @@ function getNestedValue(obj, path) {
28
188
  }, obj);
29
189
  }
30
190
  /**
31
- * Builds EmbeddingInput from document source fields
191
+ * Builds EmbeddingInput from document source fields.
192
+ *
193
+ * `urlPolicy` is applied to every media value that looks like an http(s)
194
+ * URL. Values that fail the policy are silently dropped (with a warn log)
195
+ * — never forwarded to the embedding service. Non-URL strings (base64,
196
+ * data URLs, file paths) are untouched.
32
197
  */
33
- function buildInputFromDoc(data, field) {
198
+ function buildInputFromDoc(data, field, urlPolicy = {}) {
34
199
  const input = {};
35
200
  if (field.sourceFields?.length) {
36
201
  const text = field.sourceFields.map((f) => getNestedValue(data, f)).filter(Boolean).join(" ");
37
202
  if (text.trim()) input.text = text;
38
203
  }
204
+ const policyActive = urlPolicy.allowedOrigins && urlPolicy.allowedOrigins.length > 0 || urlPolicy.blockPrivateIpUrls === true;
205
+ const screenMediaValue = (mf, value) => {
206
+ if (!policyActive) return value;
207
+ if (typeof value !== "string") return value;
208
+ if (isMediaUrlAllowed(value, urlPolicy)) return value;
209
+ warn(`[mongokit] vectorPlugin: blocked media URL in field "${mf}" by URL policy`);
210
+ };
39
211
  if (field.mediaFields?.length) {
40
212
  const firstImageField = field.mediaFields[0];
41
- const imageValue = getNestedValue(data, firstImageField);
42
- if (typeof imageValue === "string") input.image = imageValue;
213
+ const screenedImage = screenMediaValue(firstImageField, getNestedValue(data, firstImageField));
214
+ if (typeof screenedImage === "string") input.image = screenedImage;
43
215
  if (field.mediaFields.length > 1) {
44
216
  input.media = {};
45
217
  for (const mf of field.mediaFields) {
46
- const val = getNestedValue(data, mf);
47
- if (val != null) input.media[mf] = val;
218
+ const screened = screenMediaValue(mf, getNestedValue(data, mf));
219
+ if (screened != null) input.media[mf] = screened;
48
220
  }
49
221
  }
50
222
  }
@@ -88,7 +260,11 @@ function buildVectorSearchPipeline(field, queryVector, params) {
88
260
  function vectorPlugin(options) {
89
261
  const { fields, autoEmbed = false } = options;
90
262
  if (!fields?.length) throw new Error("[mongokit] vectorPlugin requires at least one field config");
91
- const { embedFn, batchEmbedFn } = options;
263
+ const { embedFn, batchEmbedFn, allowedMediaOrigins, blockPrivateIpUrls } = options;
264
+ const urlPolicy = {
265
+ allowedOrigins: allowedMediaOrigins,
266
+ blockPrivateIpUrls
267
+ };
92
268
  return {
93
269
  name: "vector",
94
270
  apply(repo) {
@@ -105,7 +281,11 @@ function vectorPlugin(options) {
105
281
  const pipeline = buildVectorSearchPipeline(field, queryVector, params);
106
282
  const agg = repo.Model.aggregate(pipeline);
107
283
  if (params.session) agg.session(params.session);
108
- return (await agg.exec()).map((doc) => {
284
+ return (await withVectorErrorHints(() => agg.exec(), {
285
+ indexName: field.index,
286
+ dimensions: field.dimensions,
287
+ filterPaths: params.filter ? Object.keys(params.filter) : void 0
288
+ })).map((doc) => {
109
289
  const score = doc._score ?? 0;
110
290
  const { _score, ...rest } = doc;
111
291
  return {
@@ -133,7 +313,7 @@ function vectorPlugin(options) {
133
313
  };
134
314
  const embedFromSource = async (data, field) => {
135
315
  if (data[field.path] && Array.isArray(data[field.path])) return;
136
- const input = buildInputFromDoc(data, field);
316
+ const input = buildInputFromDoc(data, field, urlPolicy);
137
317
  if (!hasContent(input)) return;
138
318
  const vector = await safeEmbed(input, data);
139
319
  if (vector) data[field.path] = vector;
@@ -143,7 +323,7 @@ function vectorPlugin(options) {
143
323
  for (let i = 0; i < dataArray.length; i++) {
144
324
  const data = dataArray[i];
145
325
  if (data[field.path] && Array.isArray(data[field.path])) continue;
146
- const input = buildInputFromDoc(data, field);
326
+ const input = buildInputFromDoc(data, field, urlPolicy);
147
327
  if (hasContent(input)) toEmbed.push({
148
328
  idx: i,
149
329
  input
@@ -181,7 +361,9 @@ function vectorPlugin(options) {
181
361
  return allFields.length > 0 && contextData && allFields.some((f) => f in contextData);
182
362
  });
183
363
  if (!fieldsToEmbed.length) return;
184
- const existing = await repo.Model.findById(context.id).lean().session(context.session ?? null);
364
+ const idField = repo.idField || "_id";
365
+ const lookupQuery = idField === "_id" ? { _id: context.id } : { [idField]: context.id };
366
+ const existing = await repo.Model.findOne(lookupQuery).lean().session(context.session ?? null);
185
367
  if (!existing) return;
186
368
  for (const field of fieldsToEmbed) {
187
369
  const merged = {
@@ -189,7 +371,7 @@ function vectorPlugin(options) {
189
371
  ...context.data
190
372
  };
191
373
  delete merged[field.path];
192
- const input = buildInputFromDoc(merged, field);
374
+ const input = buildInputFromDoc(merged, field, urlPolicy);
193
375
  if (!hasContent(input)) continue;
194
376
  const vector = await safeEmbed(input, merged);
195
377
  if (vector) context.data[field.path] = vector;
@@ -180,14 +180,18 @@ function validateCursorSort(cursorSort, currentSort) {
180
180
  if (JSON.stringify(cursorSort) !== JSON.stringify(currentSort)) throw new Error("Cursor sort does not match current query sort");
181
181
  }
182
182
  /**
183
- * Validates cursor version matches expected version
183
+ * Validates cursor version against the server's expected range.
184
184
  *
185
- * @param cursorVersion - Version from cursor
186
- * @param expectedVersion - Expected version from config
187
- * @throws Error if versions don't match
185
+ * - Cursors newer than `expectedVersion` are rejected (client ahead of server).
186
+ * - Cursors older than `minVersion` are rejected (client cached a cursor from
187
+ * a pre-breaking-change deploy). Default `minVersion = 1` keeps the legacy
188
+ * "accept anything <= expected" behavior; bump it when you ship a breaking
189
+ * cursor format change so old clients restart pagination cleanly instead of
190
+ * silently paginating from the wrong position.
188
191
  */
189
- function validateCursorVersion(cursorVersion, expectedVersion) {
192
+ function validateCursorVersion(cursorVersion, expectedVersion, minVersion = 1) {
190
193
  if (cursorVersion > expectedVersion) throw new Error(`Cursor version ${cursorVersion} is newer than expected version ${expectedVersion}. Please upgrade.`);
194
+ if (cursorVersion < minVersion) throw new Error(`Cursor version ${cursorVersion} is older than minimum supported ${minVersion}. Pagination must restart.`);
191
195
  }
192
196
  /**
193
197
  * Serializes a value for cursor storage
@@ -232,7 +236,7 @@ function rehydrateValue(serialized, type) {
232
236
  * - Base64-encoded cursor tokens (standard cursor)
233
237
  * - Cursor version and sort validation
234
238
  */
235
- function resolveCursorFilter(after, sort, cursorVersion, baseFilters = {}) {
239
+ function resolveCursorFilter(after, sort, cursorVersion, baseFilters = {}, minCursorVersion = 1) {
236
240
  if (/^[a-f0-9]{24}$/i.test(after)) {
237
241
  const objectId = new mongoose.Types.ObjectId(after);
238
242
  const idOperator = (sort._id || -1) === 1 ? "$gt" : "$lt";
@@ -242,7 +246,7 @@ function resolveCursorFilter(after, sort, cursorVersion, baseFilters = {}) {
242
246
  };
243
247
  }
244
248
  const cursor = decodeCursor(after);
245
- validateCursorVersion(cursor.version, cursorVersion);
249
+ validateCursorVersion(cursor.version, cursorVersion, minCursorVersion);
246
250
  validateCursorSort(cursor.sort, sort);
247
251
  return buildKeysetFilter(baseFilters, sort, cursor.value, cursor.id, cursor.values);
248
252
  }
@@ -0,0 +1,46 @@
1
+ //#region src/utils/error.ts
2
+ /**
3
+ * Creates an error with HTTP status code
4
+ *
5
+ * @param status - HTTP status code
6
+ * @param message - Error message
7
+ * @returns Error with status property
8
+ *
9
+ * @example
10
+ * throw createError(404, 'Document not found');
11
+ * throw createError(400, 'Invalid input');
12
+ * throw createError(403, 'Access denied');
13
+ */
14
+ function createError(status, message) {
15
+ const error = new Error(message);
16
+ error.status = status;
17
+ return error;
18
+ }
19
+ /**
20
+ * Detect and convert a MongoDB E11000 duplicate-key error into
21
+ * a 409 HttpError with an actionable message.
22
+ *
23
+ * PII-safe by default: the error message lists only the conflicting field
24
+ * names, never the values. Structured field names are attached under
25
+ * `error.duplicate.fields` for downstream handlers. Pass
26
+ * `{ exposeValues: true }` to opt into including the values (dev or trusted
27
+ * server-to-server contexts only).
28
+ *
29
+ * Returns `null` when the error is not a duplicate-key error.
30
+ */
31
+ function parseDuplicateKeyError(error, options = {}) {
32
+ if (!error || typeof error !== "object") return null;
33
+ const mongoErr = error;
34
+ if (mongoErr.code !== 11e3) return null;
35
+ const fields = mongoErr.keyPattern ? Object.keys(mongoErr.keyPattern) : [];
36
+ const exposed = options.exposeValues === true;
37
+ const valuesString = exposed && mongoErr.keyValue ? Object.entries(mongoErr.keyValue).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ") : "";
38
+ const httpError = createError(409, fields.length ? `Duplicate value for ${fields.join(", ")}${valuesString ? ` (${valuesString})` : ""}` : "Duplicate key error");
39
+ httpError.duplicate = {
40
+ fields,
41
+ ...exposed && mongoErr.keyValue ? { values: { ...mongoErr.keyValue } } : {}
42
+ };
43
+ return httpError;
44
+ }
45
+ //#endregion
46
+ export { parseDuplicateKeyError as n, createError as t };
@@ -1,4 +1,4 @@
1
- import { $ as ReadPreferenceType, At as LookupOptions, D as GroupResult, V as ObjectId, W as OperationOptions, Z as PopulateSpec, _t as UpdateOptions, a as AnyDocument, b as DeleteResult, g as CreateOptions, gt as UpdateManyResult, pt as SortSpec, st as SelectSpec, vt as UpdateWithValidationResult, z as MinMaxResult } from "./types-BoH1qdHo.mjs";
1
+ import { B as MinMaxResult, D as FindOneAndUpdateOptions, G as OperationOptions, H as ObjectId, O as GroupResult, Q as PopulateSpec, _t as UpdateManyResult, a as AnyDocument, b as DeleteResult, ct as SelectSpec, et as ReadPreferenceType, g as CreateOptions, jt as LookupOptions, mt as SortSpec, vt as UpdateOptions, yt as UpdateWithValidationResult } from "./types-D57SZJeE.mjs";
2
2
  import { ClientSession, Model, PipelineStage } from "mongoose";
3
3
 
4
4
  //#region src/actions/aggregate.d.ts
@@ -213,7 +213,7 @@ declare function exists<TDoc = AnyDocument>(Model: Model<TDoc>, query: Record<st
213
213
  _id: unknown;
214
214
  } | null>;
215
215
  declare namespace update_d_exports {
216
- export { increment, pullFromArray, pushToArray, update, updateByQuery, updateMany, updateWithConstraints, updateWithValidation };
216
+ export { findOneAndUpdate, increment, pullFromArray, pushToArray, update, updateByQuery, updateMany, updateWithConstraints, updateWithValidation };
217
217
  }
218
218
  /**
219
219
  * Update by ID
@@ -255,6 +255,14 @@ declare function updateMany(Model: Model<unknown>, query: Record<string, unknown
255
255
  * Update by query
256
256
  */
257
257
  declare function updateByQuery<TDoc = AnyDocument>(Model: Model<TDoc>, query: Record<string, unknown>, data: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc | null>;
258
+ /**
259
+ * Atomic findOneAndUpdate primitive.
260
+ *
261
+ * Returns the matched document (post-update by default) or null when no doc
262
+ * matches and `upsert` is false. Used by outbox relays, distributed locks,
263
+ * and workflow semaphores that need compare-and-set in a single round-trip.
264
+ */
265
+ declare function findOneAndUpdate<TDoc = AnyDocument>(Model: Model<TDoc>, filter: Record<string, unknown>, update: Record<string, unknown> | Record<string, unknown>[], options?: FindOneAndUpdateOptions): Promise<TDoc | null>;
258
266
  /**
259
267
  * Increment field
260
268
  */
package/dist/index.d.mts CHANGED
@@ -1,13 +1,49 @@
1
- import { $ as ReadPreferenceType, A as InferDocument, At as LookupOptions, B as NonNullableFields, C as EventPayload, Ct as WithPlugins, D as GroupResult, Dt as IRequestContext, E as FieldRules, Et as IControllerResponse, F as KeysetPaginationResult, G as PaginationConfig, H as OffsetPaginationOptions, I as Logger, J as Plugin, K as PaginationResult, L as LookupPopulateOptions, M as JsonSchema, N as KeysOfType, O as HookMode, Ot as IResponseFormatter, P as KeysetPaginationOptions, Q as ReadOptions, R as LookupPopulateResult, S as EventHandlers, St as ValidatorDefinition, T as FieldPreset, Tt as IController, U as OffsetPaginationResult, V as ObjectId, W as OperationOptions, X as PluginType, Y as PluginFunction, Z as PopulateSpec, _ as CrudSchemas, _t as UpdateOptions, a as AnyDocument, at as RequiredBy, b as DeleteResult, bt as ValidationChainOptions, c as CacheOperationOptions, ct as SessionOptions, d as CacheableOptions, dt as SoftDeleteRepository, et as RepositoryContext, f as CascadeOptions, ft as SortDirection, g as CreateOptions, gt as UpdateManyResult, h as CreateInput, ht as UpdateInput, i as AllPluginMethods, it as RepositoryOptions, j as InferRawDoc, k as HttpError, kt as LookupBuilder, l as CacheOptions, lt as SoftDeleteFilterMode, m as CollationOptions, mt as Strict, n as AggregatePaginationOptions, nt as RepositoryInstance, o as AnyModel, ot as SchemaBuilderOptions, p as CascadeRelation, pt as SortSpec, q as PartialBy, r as AggregatePaginationResult, rt as RepositoryOperation, s as CacheAdapter, st as SelectSpec, t as AggregateOptions, tt as RepositoryEvent, u as CacheStats, ut as SoftDeleteOptions, v as DecodedCursor, vt as UpdateWithValidationResult, w as EventPhase, wt as WithTransactionOptions, x as DocField, xt as ValidationResult, y as DeepPartial, yt as UserContext, z as MinMaxResult } from "./types-BoH1qdHo.mjs";
2
- import { t as index_d_exports } from "./index-jzO5hMtF.mjs";
1
+ import { $ as ReadOptions, A as HttpError, At as LookupBuilder, B as MinMaxResult, C as EventPayload, Ct as ValidatorDefinition, D as FindOneAndUpdateOptions, Dt as IControllerResponse, E as FieldRules, Et as IController, F as KeysetPaginationOptions, G as OperationOptions, H as ObjectId, I as KeysetPaginationResult, J as PartialBy, K as PaginationConfig, L as Logger, M as InferRawDoc, N as JsonSchema, O as GroupResult, Ot as IRequestContext, P as KeysOfType, Q as PopulateSpec, R as LookupPopulateOptions, S as EventHandlers, St as ValidationResult, T as FieldPreset, Tt as WithTransactionOptions, U as OffsetPaginationOptions, V as NonNullableFields, W as OffsetPaginationResult, X as PluginFunction, Y as Plugin, Z as PluginType, _ as CrudSchemas, _t as UpdateManyResult, a as AnyDocument, at as RepositoryOptions, b as DeleteResult, bt as UserContext, c as CacheOperationOptions, ct as SelectSpec, d as CacheableOptions, dt as SoftDeleteOptions, et as ReadPreferenceType, f as CascadeOptions, ft as SoftDeleteRepository, g as CreateOptions, gt as UpdateInput, h as CreateInput, ht as Strict, i as AllPluginMethods, it as RepositoryOperation, j as InferDocument, jt as LookupOptions, k as HookMode, kt as IResponseFormatter, l as CacheOptions, lt as SessionOptions, m as CollationOptions, mt as SortSpec, n as AggregatePaginationOptions, nt as RepositoryEvent, o as AnyModel, ot as RequiredBy, p as CascadeRelation, pt as SortDirection, q as PaginationResult, r as AggregatePaginationResult, rt as RepositoryInstance, s as CacheAdapter, st as SchemaBuilderOptions, t as AggregateOptions, tt as RepositoryContext, u as CacheStats, ut as SoftDeleteFilterMode, v as DecodedCursor, vt as UpdateOptions, w as EventPhase, wt as WithPlugins, x as DocField, xt as ValidationChainOptions, y as DeepPartial, yt as UpdateWithValidationResult, z as LookupPopulateResult } from "./types-D57SZJeE.mjs";
2
+ import { t as index_d_exports } from "./index-DlSAFIc9.mjs";
3
3
  import { PaginationEngine } from "./pagination/PaginationEngine.mjs";
4
- import { A as dateSequentialId, B as AuditEntry, C as elasticSearchPlugin, D as PrefixedIdOptions, E as IdGenerator, F as CacheMethods, G as AuditTrailOptions, H as AuditQueryOptions, I as cachePlugin, J as auditLogPlugin, K as AuditTrailQuery, L as BatchOperationsMethods, M as prefixedId, N as sequentialId, O as SequentialIdOptions, P as cascadePlugin, R as BulkWriteResult, S as ElasticSearchOptions, T as DateSequentialIdOptions, U as AuditQueryResult, V as AuditOperation, W as AuditTrailMethods, X as aggregateHelpersPlugin, Y as AggregateHelpersMethods, _ as MongoOperationsMethods, a as uniqueField, b as methodRegistryPlugin, c as SubdocumentMethods, d as softDeletePlugin, f as ObservabilityOptions, g as multiTenantPlugin, h as MultiTenantOptions, i as requireField, j as getNextSequence, k as customIdPlugin, l as subdocumentPlugin, m as observabilityPlugin, n as blockIf, o as validationChainPlugin, p as OperationMetric, q as auditTrailPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as SoftDeleteMethods, v as mongoOperationsPlugin, w as CustomIdOptions, x as fieldFilterPlugin, z as batchOperationsPlugin } from "./validation-chain.plugin-BXx6Ogpp.mjs";
5
- import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-B19MD38s.mjs";
6
- import { n as SchemaIndexes } from "./indexes-qIBCZ3mY.mjs";
7
- import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, m as parseDuplicateKeyError, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, p as createError, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel, u as filterResponseData } from "./mongooseToJsonSchema-BvD1NDl9.mjs";
4
+ import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, T as ElasticSearchOptions, U as AuditEntry, V as BulkWriteResult, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "./validation-chain.plugin-wGF34Ym-.mjs";
5
+ import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-oM8G4b6m.mjs";
6
+ import { n as SchemaIndexes } from "./indexes-C26GNZRU.mjs";
7
+ import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, m as parseDuplicateKeyError, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, p as createError, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel, u as filterResponseData } from "./mongooseToJsonSchema-BZaKOXXI.mjs";
8
8
  import * as _$mongoose from "mongoose";
9
9
  import { ClientSession, Expression, Model, PipelineStage, PopulateOptions } from "mongoose";
10
10
 
11
+ //#region src/operations.d.ts
12
+ /**
13
+ * Where a plugin should inject its scoping filter (multi-tenant scope,
14
+ * soft-delete filter, etc.) on the RepositoryContext.
15
+ *
16
+ * - `data` — single-doc create payload (`context.data`)
17
+ * - `dataArray` — multi-doc create payload (`context.dataArray`)
18
+ * - `query` — raw filter (`context.query`); the dominant convention
19
+ * - `filters` — paginated list options' filter sub-bag (`context.filters`)
20
+ * - `operations` — bulkWrite per-sub-op (special — plugins walk each entry)
21
+ * - `none` — no scoping target (op accepts no filter input)
22
+ */
23
+ type PolicyKey = 'data' | 'dataArray' | 'query' | 'filters' | 'operations' | 'none';
24
+ interface OperationDescriptor {
25
+ /** Where multi-tenant / soft-delete plugins inject their scoping filter. */
26
+ policyKey: PolicyKey;
27
+ /** Whether this op writes to the database. Drives audit + cache invalidation. */
28
+ mutates: boolean;
29
+ /** True when `context.id` is populated by the time before/after hooks fire. */
30
+ hasIdContext: boolean;
31
+ }
32
+ /**
33
+ * The registry. Order intentionally groups by category for readability.
34
+ * Adding a new RepositoryOperation requires exactly one entry here, after
35
+ * which every bundled plugin auto-classifies it correctly.
36
+ */
37
+ declare const OP_REGISTRY: Readonly<Record<RepositoryOperation, OperationDescriptor>>;
38
+ /** All known repository operations, in registry order. */
39
+ declare const ALL_OPERATIONS: RepositoryOperation[];
40
+ /** Operations that mutate the database. Drives audit + cache invalidation lists. */
41
+ declare const MUTATING_OPERATIONS: readonly RepositoryOperation[];
42
+ /** Operations that don't mutate. Drives default cacheable ops. */
43
+ declare const READ_OPERATIONS: readonly RepositoryOperation[];
44
+ /** Filter ops by their policy injection key. */
45
+ declare function operationsByPolicyKey(key: PolicyKey): RepositoryOperation[];
46
+ //#endregion
11
47
  //#region src/query/AggregationBuilder.d.ts
12
48
  /** Options for $vectorSearch stage (Atlas only) */
13
49
  interface VectorSearchOptions {
@@ -596,15 +632,37 @@ declare class QueryParser {
596
632
  */
597
633
  private readonly _schemaIndexes;
598
634
  /**
599
- * Regex patterns that can cause catastrophic backtracking (ReDoS attacks)
600
- * Detects:
635
+ * Regex patterns that can cause catastrophic backtracking (ReDoS attacks).
636
+ * Detects common footguns:
601
637
  * - Quantifiers: {n,m}
602
638
  * - Possessive quantifiers: *+, ++, ?+
603
639
  * - Nested quantifiers: (a+)+, (a*)*
604
640
  * - Backreferences: \1, \2, etc.
605
641
  * - Complex character classes: [...]...[...]
642
+ *
643
+ * This pattern detector is FAIL-SAFE (on match, the input is escaped), but
644
+ * it is not exhaustive. {@link _regexComplexityExceedsBudget} complements
645
+ * it with a deterministic static complexity budget so patterns that happen
646
+ * to evade the detector still fail over when their unbounded-quantifier
647
+ * density is pathological.
606
648
  */
607
649
  private readonly dangerousRegexPatterns;
650
+ /**
651
+ * Static complexity budget for user-supplied regex patterns.
652
+ * Returns `true` if the pattern crosses a budget that empirically correlates
653
+ * with ReDoS risk — independent of the heuristic regex detector.
654
+ *
655
+ * Budget components (any single threshold crossed → over budget):
656
+ * - unbounded quantifiers (`*`, `+`, `?`) — hard cap 20
657
+ * - nested groups `(` — hard cap 8
658
+ * - alternations `|` — hard cap 10
659
+ * - combined: (quantifiers * groups) — hard cap 40
660
+ *
661
+ * These thresholds are intentionally permissive for legitimate queries and
662
+ * tight enough to refuse known-pathological patterns like `(a+)+b` with
663
+ * heavy repetition.
664
+ */
665
+ private _regexComplexityExceedsBudget;
608
666
  constructor(options?: QueryParserOptions);
609
667
  /**
610
668
  * Structured view of the configured schema's indexes — geo fields, text
@@ -976,6 +1034,36 @@ declare class Repository<TDoc = unknown> {
976
1034
  * Update document by ID
977
1035
  */
978
1036
  update(id: string | ObjectId, data: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc>;
1037
+ /**
1038
+ * Atomic findOneAndUpdate (compare-and-set primitive).
1039
+ *
1040
+ * Single round-trip match-and-mutate for outbox relays, distributed locks,
1041
+ * and workflow semaphores. Goes through the full hook pipeline so
1042
+ * multi-tenant scope, soft-delete, audit, and cache plugins all apply.
1043
+ *
1044
+ * Returns the matched document (post-update by default) or `null` when no
1045
+ * document matches and `upsert` is false.
1046
+ *
1047
+ * @example FIFO claim-lease for an outbox worker
1048
+ * ```ts
1049
+ * const claimed = await outboxRepo.findOneAndUpdate(
1050
+ * { status: 'pending', leaseExpiresAt: { $lt: new Date() } },
1051
+ * { $set: { status: 'processing', leaseExpiresAt: leaseUntil, leasedBy: workerId } },
1052
+ * { sort: { createdAt: 1 }, returnDocument: 'after' },
1053
+ * );
1054
+ * if (!claimed) return; // queue empty
1055
+ * ```
1056
+ *
1057
+ * @example CAS upsert
1058
+ * ```ts
1059
+ * const lock = await locksRepo.findOneAndUpdate(
1060
+ * { _id: lockKey, ownerId: { $in: [null, workerId] } },
1061
+ * { $set: { ownerId: workerId, expiresAt: leaseUntil } },
1062
+ * { upsert: true },
1063
+ * );
1064
+ * ```
1065
+ */
1066
+ findOneAndUpdate<TResult = TDoc>(filter: Record<string, unknown>, update: Record<string, unknown> | Record<string, unknown>[], options?: FindOneAndUpdateOptions): Promise<TResult | null>;
979
1067
  /**
980
1068
  * Delete a document by ID.
981
1069
  *
@@ -1210,4 +1298,4 @@ declare function isTransactionUnsupported(error: Error): boolean;
1210
1298
  */
1211
1299
  declare function createRepository<TDoc>(Model: _$mongoose.Model<TDoc, any, any, any>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions): Repository<TDoc>;
1212
1300
  //#endregion
1213
- export { type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, type AggregatePaginationResult, AggregationBuilder, type AllPluginMethods, type AnyDocument, type AnyModel, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BatchOperationsMethods, type BulkWriteResult, type CacheAdapter, type CacheMethods, type CacheOperationOptions, type CacheOptions, type CacheStats, type CacheableOptions, type CascadeOptions, type CascadeRelation, type CollationOptions, type CreateInput, type CreateOptions, type CrudSchemas, type CustomIdOptions, type DateSequentialIdOptions, type DecodedCursor, type DeepPartial, type DeleteResult, type DocField, type ElasticSearchOptions, type EventHandlers, type EventPayload, type EventPhase, type FieldPreset, type FieldRules, type FieldType, type FilterQuery, type GroupResult, HOOK_PRIORITY, type HookMode, type HttpError, type IController, type IControllerResponse, type IRequestContext, type IResponseFormatter, type IdGenerator, type InferDocument, type InferRawDoc, type JsonSchema, type KeysOfType, type KeysetPaginationOptions, type KeysetPaginationResult, type Logger, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, type MinMaxResult, type MongoOperationsMethods, type MultiTenantOptions, type NonNullableFields, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OffsetPaginationResult, type OperationMetric, type OperationOptions, type PaginationConfig, PaginationEngine, type PaginationResult, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, QueryParser, type QueryParserOptions, type ReadOptions, type ReadPreferenceType, Repository, Repository as default, type RepositoryContext, type RepositoryEvent, type RepositoryInstance, type RepositoryOperation, type RepositoryOptions, type RequiredBy, type SchemaBuilderOptions, type SchemaLike, type SearchMode, type SelectSpec, type SequentialIdOptions, type SessionOptions, type SoftDeleteFilterMode, type SoftDeleteMethods, type SoftDeleteOptions, type SoftDeleteRepository, type SortDirection, type SortSpec, type Strict, type SubdocumentMethods, type UpdateInput, type UpdateManyResult, type UpdateOptions, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidationResult, type ValidatorDefinition, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getNextSequence, getSystemManagedFields, immutableField, isFieldUpdateAllowed, isTransactionUnsupported, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validateUpdateBody, validationChainPlugin, withTransaction };
1301
+ export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, type AggregatePaginationResult, AggregationBuilder, type AllPluginMethods, type AnyDocument, type AnyModel, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BatchOperationsMethods, type BulkWriteResult, type CacheAdapter, type CacheMethods, type CacheOperationOptions, type CacheOptions, type CacheStats, type CacheableOptions, type CascadeOptions, type CascadeRelation, type CollationOptions, type CreateInput, type CreateOptions, type CrudSchemas, type CustomIdOptions, type DateSequentialIdOptions, type DecodedCursor, type DeepPartial, type DeleteResult, type DocField, type ElasticSearchOptions, type EventHandlers, type EventPayload, type EventPhase, type FieldPreset, type FieldRules, type FieldType, type FilterQuery, type FindOneAndUpdateOptions, type GroupResult, HOOK_PRIORITY, type HookMode, type HttpError, type IController, type IControllerResponse, type IRequestContext, type IResponseFormatter, type IdGenerator, type InferDocument, type InferRawDoc, type JsonSchema, type KeysOfType, type KeysetPaginationOptions, type KeysetPaginationResult, type Logger, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MUTATING_OPERATIONS, type MinMaxResult, type MongoOperationsMethods, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OffsetPaginationResult, type OperationDescriptor, type OperationMetric, type OperationOptions, type PaginationConfig, PaginationEngine, type PaginationResult, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, QueryParser, type QueryParserOptions, READ_OPERATIONS, type ReadOptions, type ReadPreferenceType, Repository, Repository as default, type RepositoryContext, type RepositoryEvent, type RepositoryInstance, type RepositoryOperation, type RepositoryOptions, type RequiredBy, type SchemaBuilderOptions, type SchemaLike, type SearchMode, type SelectSpec, type SequentialIdOptions, type SessionOptions, type SoftDeleteFilterMode, type SoftDeleteMethods, type SoftDeleteOptions, type SoftDeleteRepository, type SortDirection, type SortSpec, type Strict, type SubdocumentMethods, type TenantContext, type TenantStore, type UpdateInput, type UpdateManyResult, type UpdateOptions, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidationResult, type ValidatorDefinition, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getNextSequence, getSystemManagedFields, immutableField, isFieldUpdateAllowed, isTransactionUnsupported, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validateUpdateBody, validationChainPlugin, withTransaction };