@classytic/mongokit 3.5.5 → 3.6.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 (26) hide show
  1. package/README.md +189 -1486
  2. package/dist/{PaginationEngine-CpsQl471.mjs → PaginationEngine-CunXkR9p.mjs} +100 -4
  3. package/dist/actions/index.d.mts +1 -1
  4. package/dist/ai/index.d.mts +1 -1
  5. package/dist/id-resolution-LZ-cCwR5.mjs +81 -0
  6. package/dist/{index-Buj7P006.d.mts → index-BSRSsRRx.d.mts} +1 -1
  7. package/dist/index.d.mts +81 -9
  8. package/dist/index.mjs +5 -5
  9. package/dist/{mongooseToJsonSchema-DpjhFfaO.mjs → mongooseToJsonSchema-8Cyu3NFJ.mjs} +50 -45
  10. package/dist/{mongooseToJsonSchema-BlDfbg_0.d.mts → mongooseToJsonSchema-D2YjHo7W.d.mts} +4 -3
  11. package/dist/pagination/PaginationEngine.d.mts +8 -1
  12. package/dist/pagination/PaginationEngine.mjs +1 -1
  13. package/dist/plugins/index.d.mts +1 -1
  14. package/dist/plugins/index.mjs +1 -1
  15. package/dist/query/primitives/coercion.d.mts +1 -1
  16. package/dist/query/primitives/coercion.mjs +4 -3
  17. package/dist/query/primitives/indexes.d.mts +1 -1
  18. package/dist/{types-CWUL0yiw.d.mts → types-DiQfDesD.d.mts} +29 -3
  19. package/dist/utils/index.d.mts +60 -3
  20. package/dist/utils/index.mjs +4 -3
  21. package/dist/{validation-chain.plugin-BCPh2UZG.mjs → validation-chain.plugin-3rr7dlw6.mjs} +308 -164
  22. package/dist/{validation-chain.plugin-Dn41aAbK.d.mts → validation-chain.plugin-COKNRAwp.d.mts} +15 -12
  23. package/package.json +2 -2
  24. /package/dist/{coercion-01tEHsKF.d.mts → coercion-B19MD38s.d.mts} +0 -0
  25. /package/dist/{field-selection-reyDRzXf.mjs → field-selection-C5o7jyyA.mjs} +0 -0
  26. /package/dist/{indexes-CiIT-Zli.d.mts → indexes-qIBCZ3mY.d.mts} +0 -0
@@ -1,6 +1,72 @@
1
1
  import { a as warn, t as createError } from "./error-Bpbi_NKo.mjs";
2
2
  import { n as encodeCursor, r as resolveCursorFilter } from "./cursor-CHToazHy.mjs";
3
3
  import { r as validateKeysetSort, t as getPrimaryField } from "./sort-C-BJEWUZ.mjs";
4
+ //#region src/pagination/utils/index-hint.ts
5
+ /**
6
+ * Read a Mongoose schema's declared indexes, defensively.
7
+ * Returns an empty array on any introspection failure.
8
+ */
9
+ function readSchemaIndexes(Model) {
10
+ try {
11
+ const schema = Model.schema;
12
+ if (!schema || typeof schema.indexes !== "function") return [];
13
+ const raw = schema.indexes();
14
+ return Array.isArray(raw) ? raw : [];
15
+ } catch {
16
+ return [];
17
+ }
18
+ }
19
+ /**
20
+ * Does any schema-declared compound index satisfy ESR for the given
21
+ * equality filter fields + sort?
22
+ *
23
+ * Rules:
24
+ * 1. The first N keys of the index (N = filterFields.length) must be
25
+ * exactly the equality filter fields in any order (set equality).
26
+ * 2. The next M keys (M = sortKeys.length) must match the sort keys in
27
+ * exact declared order.
28
+ * 3. Directions must all match OR all be inverse (MongoDB can walk an
29
+ * index in reverse, but only if every sort field flips together).
30
+ * 4. Indexes with more total keys than needed are still accepted when
31
+ * the above prefix holds — Mongo just ignores trailing fields.
32
+ *
33
+ * Notes:
34
+ * - Hashed / text / 2dsphere indexes are skipped (not usable for sort).
35
+ * - Unique, partial, sparse options don't affect compatibility.
36
+ * - When `filterFields` is empty, a sort-only prefix match is sufficient.
37
+ *
38
+ * @param indexes Raw `[spec, options?]` tuples from `schema.indexes()`.
39
+ * @param filterFields Top-level equality filter keys (already stripped of `$*`).
40
+ * @param sort Normalized sort spec `{ field: 1 | -1 }`.
41
+ */
42
+ function hasCompatibleKeysetIndex(indexes, filterFields, sort) {
43
+ const sortKeys = Object.keys(sort);
44
+ if (sortKeys.length === 0) return true;
45
+ const filterSet = new Set(filterFields);
46
+ const prefixLen = filterFields.length;
47
+ for (const [spec] of indexes) {
48
+ if (!spec || typeof spec !== "object") continue;
49
+ const entries = Object.entries(spec);
50
+ if (entries.length < prefixLen + sortKeys.length) continue;
51
+ if (entries.some(([, dir]) => typeof dir !== "number")) continue;
52
+ const equalityPrefix = entries.slice(0, prefixLen).map(([k]) => k);
53
+ if (equalityPrefix.length !== prefixLen) continue;
54
+ if (!(equalityPrefix.every((k) => filterSet.has(k)) && new Set(equalityPrefix).size === prefixLen)) continue;
55
+ const sortSuffix = entries.slice(prefixLen, prefixLen + sortKeys.length);
56
+ if (sortSuffix.length !== sortKeys.length) continue;
57
+ let namesMatch = true;
58
+ for (let i = 0; i < sortKeys.length; i++) if (sortSuffix[i][0] !== sortKeys[i]) {
59
+ namesMatch = false;
60
+ break;
61
+ }
62
+ if (!namesMatch) continue;
63
+ const forward = sortKeys.every((k, i) => sortSuffix[i][1] === sort[k]);
64
+ const reverse = sortKeys.every((k, i) => sortSuffix[i][1] === -sort[k]);
65
+ if (forward || reverse) return true;
66
+ }
67
+ return false;
68
+ }
69
+ //#endregion
4
70
  //#region src/pagination/utils/limits.ts
5
71
  /**
6
72
  * Validates and sanitizes limit value
@@ -65,6 +131,24 @@ function calculateTotalPages(total, limit) {
65
131
  }
66
132
  //#endregion
67
133
  //#region src/pagination/PaginationEngine.ts
134
+ /**
135
+ * Strip a trailing `_id` tiebreaker from a normalized sort spec.
136
+ *
137
+ * `validateKeysetSort` always appends `_id` to the end of the sort object as
138
+ * a stable-order guarantee. For index-compatibility matching we only care
139
+ * about the primary ordering fields — an index covering those is efficient
140
+ * even without `_id` in the index itself.
141
+ *
142
+ * Returns the sort unchanged if `_id` is the only field (degenerate case).
143
+ */
144
+ function stripTrailingIdTiebreaker(sort) {
145
+ const keys = Object.keys(sort);
146
+ if (keys.length <= 1) return sort;
147
+ if (keys[keys.length - 1] !== "_id") return sort;
148
+ const out = {};
149
+ for (let i = 0; i < keys.length - 1; i++) out[keys[i]] = sort[keys[i]];
150
+ return out;
151
+ }
68
152
  function ensureKeysetSelectIncludesCursorFields(select, sort) {
69
153
  if (!select) return select;
70
154
  const requiredFields = new Set([...Object.keys(sort), "_id"]);
@@ -95,6 +179,11 @@ var PaginationEngine = class {
95
179
  Model;
96
180
  config;
97
181
  /**
182
+ * Lazily-cached schema index snapshot used by stream() to decide whether
183
+ * to emit the "missing compound index" warning. Computed on first use.
184
+ */
185
+ _cachedSchemaIndexes = null;
186
+ /**
98
187
  * Create a new pagination engine
99
188
  *
100
189
  * @param Model - Mongoose model to paginate
@@ -111,6 +200,12 @@ var PaginationEngine = class {
111
200
  useEstimatedCount: config.useEstimatedCount ?? false
112
201
  };
113
202
  }
203
+ /** Memoized schema index lookup — avoids re-walking schema on every stream(). */
204
+ _getSchemaIndexes() {
205
+ if (this._cachedSchemaIndexes !== null) return this._cachedSchemaIndexes;
206
+ this._cachedSchemaIndexes = readSchemaIndexes(this.Model);
207
+ return this._cachedSchemaIndexes;
208
+ }
114
209
  /**
115
210
  * Offset-based pagination using skip/limit
116
211
  * Best for small datasets and when users need random page access
@@ -204,11 +299,12 @@ var PaginationEngine = class {
204
299
  if (!sort) throw createError(400, "sort is required for keyset pagination");
205
300
  const sanitizedLimit = validateLimit(limit, this.config);
206
301
  const normalizedSort = validateKeysetSort(sort);
302
+ const sortWithoutIdTail = stripTrailingIdTiebreaker(normalizedSort);
207
303
  const filterKeys = Object.keys(filters).filter((k) => !k.startsWith("$"));
208
- const sortFields = Object.keys(normalizedSort);
209
- if (filterKeys.length > 0 && sortFields.length > 0) {
210
- const indexFields = [...filterKeys.map((f) => `${f}: 1`), ...sortFields.map((f) => `${f}: ${normalizedSort[f]}`)];
211
- warn(`[mongokit] Keyset pagination with filters [${filterKeys.join(", ")}] and sort [${sortFields.join(", ")}] requires a compound index for O(1) performance. Ensure index exists: { ${indexFields.join(", ")} }`);
304
+ const effectiveSortFields = Object.keys(sortWithoutIdTail);
305
+ if (process.env.NODE_ENV !== "test" && filterKeys.length > 0 && effectiveSortFields.length > 0 && !hasCompatibleKeysetIndex(this._getSchemaIndexes(), filterKeys, sortWithoutIdTail)) {
306
+ const indexFields = [...filterKeys.map((f) => `${f}: 1`), ...effectiveSortFields.map((f) => `${f}: ${sortWithoutIdTail[f]}`)];
307
+ 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.)`);
212
308
  }
213
309
  let query = { ...filters };
214
310
  if (after) query = resolveCursorFilter(after, normalizedSort, this.config.cursorVersion, query);
@@ -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-Buj7P006.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-BSRSsRRx.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,4 +1,4 @@
1
- import { J as Plugin } from "../types-CWUL0yiw.mjs";
1
+ import { J as Plugin } from "../types-DiQfDesD.mjs";
2
2
  import { ClientSession, PipelineStage } from "mongoose";
3
3
 
4
4
  //#region src/ai/types.d.ts
@@ -0,0 +1,81 @@
1
+ import mongoose from "mongoose";
2
+ //#region src/utils/id-resolution.ts
3
+ /**
4
+ * ID Resolution Primitive
5
+ *
6
+ * Pure functions for detecting a Mongoose schema's _id type and validating
7
+ * id values against it. Used by Repository.getById to decide whether to
8
+ * run ObjectId validation (for ObjectId schemas) or skip it (for String /
9
+ * Number / UUID schemas).
10
+ *
11
+ * Also usable standalone — Arc controllers, custom repositories, migration
12
+ * scripts, or test harnesses can import these directly to make the same
13
+ * decision without depending on Repository.
14
+ *
15
+ * Design: no `this`, no Model, no DB calls. Only reads `schema.paths._id.instance`.
16
+ */
17
+ /**
18
+ * Check if a Mongoose SchemaType `instance` string is an ObjectId variant.
19
+ * Mongoose 9.x uses `'ObjectId'`, older versions use `'ObjectID'`. This
20
+ * is the single source of truth — use it instead of inline
21
+ * `instance === 'ObjectId' || instance === 'ObjectID'` checks.
22
+ */
23
+ function isObjectIdInstance(instance) {
24
+ return instance === "ObjectId" || instance === "ObjectID";
25
+ }
26
+ const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
27
+ /**
28
+ * Detect the _id path type from a Mongoose schema.
29
+ *
30
+ * Returns 'objectid' as the safe default when:
31
+ * - No schema is provided (mock models, test harnesses)
32
+ * - The schema has no `paths._id` (auto-generated ObjectId)
33
+ * - The instance string is unrecognized
34
+ *
35
+ * This safe-default means existing callers that don't set _id explicitly
36
+ * get the same ObjectId validation they always had — the behavioral change
37
+ * is strictly opt-in via schema declaration.
38
+ */
39
+ function getSchemaIdType(schema) {
40
+ const instance = schema?.paths?._id?.instance;
41
+ if (!instance) return "objectid";
42
+ if (isObjectIdInstance(instance)) return "objectid";
43
+ switch (instance) {
44
+ case "String": return "string";
45
+ case "Number": return "number";
46
+ case "UUID": return "uuid";
47
+ default: return "unknown";
48
+ }
49
+ }
50
+ /**
51
+ * Validate an id value against a known _id type — WITHOUT hitting the DB.
52
+ *
53
+ * Returns `true` when the value is structurally valid for the given type.
54
+ * Returns `false` when it's definitely invalid (Repository should return
55
+ * 404 / null immediately rather than wasting a round-trip).
56
+ *
57
+ * For `'string'` and `'unknown'` types, validation is intentionally
58
+ * permissive: any truthy, non-empty value is accepted. The DB is the
59
+ * authority for whether the document exists — we only reject values that
60
+ * are structurally impossible (null, undefined, empty string).
61
+ */
62
+ function isValidIdForType(id, idType) {
63
+ if (id === null || id === void 0) return false;
64
+ switch (idType) {
65
+ case "objectid": {
66
+ if (typeof id === "object") return mongoose.Types.ObjectId.isValid(id);
67
+ const s = String(id);
68
+ return s.length > 0 && mongoose.Types.ObjectId.isValid(s);
69
+ }
70
+ case "string": return String(id).length > 0;
71
+ case "number": {
72
+ if (typeof id === "number") return Number.isFinite(id);
73
+ const n = Number(id);
74
+ return Number.isFinite(n);
75
+ }
76
+ case "uuid": return typeof id === "string" && UUID_RE.test(id);
77
+ case "unknown": return String(id).length > 0;
78
+ }
79
+ }
80
+ //#endregion
81
+ export { isObjectIdInstance as n, isValidIdForType as r, getSchemaIdType 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-CWUL0yiw.mjs";
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-DiQfDesD.mjs";
2
2
  import { ClientSession, Model, PipelineStage } from "mongoose";
3
3
 
4
4
  //#region src/actions/aggregate.d.ts
package/dist/index.d.mts CHANGED
@@ -1,10 +1,10 @@
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-CWUL0yiw.mjs";
2
- import { t as index_d_exports } from "./index-Buj7P006.mjs";
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-DiQfDesD.mjs";
2
+ import { t as index_d_exports } from "./index-BSRSsRRx.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-Dn41aAbK.mjs";
5
- import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-01tEHsKF.mjs";
6
- import { n as SchemaIndexes } from "./indexes-CiIT-Zli.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-BlDfbg_0.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-COKNRAwp.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-D2YjHo7W.mjs";
8
8
  import * as _$mongoose from "mongoose";
9
9
  import { ClientSession, Expression, Model, PipelineStage, PopulateOptions } from "mongoose";
10
10
 
@@ -977,10 +977,29 @@ declare class Repository<TDoc = unknown> {
977
977
  */
978
978
  update(id: string | ObjectId, data: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc>;
979
979
  /**
980
- * Delete document by ID
980
+ * Delete a document by ID.
981
+ *
982
+ * By default the behavior is decided by the plugin stack:
983
+ * - With `softDeletePlugin` wired → soft delete (sets `deletedAt`)
984
+ * - Without the plugin → physical delete
985
+ *
986
+ * Pass `mode: 'hard'` to force physical deletion regardless of plugins.
987
+ * All policy hooks (multi-tenant scope, audit trails, cache invalidation,
988
+ * cascade) still fire — only the soft-delete interception is skipped. This
989
+ * is the GDPR / admin-cleanup path.
990
+ *
991
+ * @example
992
+ * ```ts
993
+ * // Respects softDeletePlugin — soft if wired
994
+ * await userRepo.delete(userId);
995
+ *
996
+ * // GDPR erasure — physical delete, audit hooks still fire
997
+ * await userRepo.delete(userId, { mode: 'hard', organizationId: 'org_123' });
998
+ * ```
981
999
  */
982
1000
  delete(id: string | ObjectId, options?: SessionOptions & {
983
1001
  idField?: string;
1002
+ mode?: 'hard' | 'soft';
984
1003
  }): Promise<DeleteResult>;
985
1004
  /**
986
1005
  * Execute aggregation pipeline
@@ -1096,7 +1115,6 @@ declare class Repository<TDoc = unknown> {
1096
1115
  * ```
1097
1116
  */
1098
1117
  withTransaction<T>(callback: (session: ClientSession) => Promise<T>, options?: WithTransactionOptions): Promise<T>;
1099
- private _isTransactionUnsupported;
1100
1118
  /**
1101
1119
  * Execute custom query with event emission
1102
1120
  */
@@ -1111,6 +1129,21 @@ declare class Repository<TDoc = unknown> {
1111
1129
  * 4. DEFAULT (500) — user-registered hooks
1112
1130
  */
1113
1131
  _buildContext(operation: string, options: Record<string, unknown> | object): Promise<RepositoryContext>;
1132
+ /**
1133
+ * Resolve the effective sort for getAll. Handles $near conflict detection
1134
+ * (warns + drops explicit sort when $near is in filters).
1135
+ */
1136
+ private _resolveSort;
1137
+ /**
1138
+ * Detect whether to use keyset (cursor) or offset (page) pagination.
1139
+ */
1140
+ private _detectPaginationMode;
1141
+ /**
1142
+ * Build the MongoDB query filter with search merged in. Handles text
1143
+ * search ($text), regex search ($or of $regex), and the search-resolver
1144
+ * plugin contract (search already cleared by a before:getAll hook).
1145
+ */
1146
+ private _buildSearchQuery;
1114
1147
  /**
1115
1148
  * Parse sort string or object
1116
1149
  */
@@ -1125,6 +1158,45 @@ declare class Repository<TDoc = unknown> {
1125
1158
  _handleError(error: Error): HttpError;
1126
1159
  }
1127
1160
  //#endregion
1161
+ //#region src/transaction.d.ts
1162
+ /** Minimal shape we need from a Mongoose connection. */
1163
+ interface SessionStarter {
1164
+ startSession(): Promise<ClientSession>;
1165
+ }
1166
+ /**
1167
+ * Run a callback inside a MongoDB transaction on the given connection.
1168
+ *
1169
+ * - Starts a session, runs `session.withTransaction(callback)` (which auto-retries
1170
+ * on `TransientTransactionError` and `UnknownTransactionCommitResult`), and
1171
+ * always ends the session in `finally`.
1172
+ * - When `allowFallback` is true and the deployment doesn't support transactions
1173
+ * (e.g. standalone MongoDB in dev), the callback runs once without a
1174
+ * transaction on the same session. `onFallback` is invoked with the original
1175
+ * error so hosts can log the degradation.
1176
+ *
1177
+ * @example
1178
+ * ```ts
1179
+ * import mongoose from 'mongoose';
1180
+ * import { withTransaction } from '@classytic/mongokit';
1181
+ *
1182
+ * await withTransaction(mongoose.connection, async (session) => {
1183
+ * const order = await orderRepo.create(data, { session });
1184
+ * await inventoryRepo.decrement(order.items, { session });
1185
+ * return order;
1186
+ * });
1187
+ * ```
1188
+ */
1189
+ declare function withTransaction<T>(connection: SessionStarter, callback: (session: ClientSession) => Promise<T>, options?: WithTransactionOptions): Promise<T>;
1190
+ /**
1191
+ * Detect whether an error indicates the MongoDB deployment does not support
1192
+ * multi-document transactions (standalone server, older topology, etc.).
1193
+ *
1194
+ * Checks MongoDB error codes first — 263 (standalone) and 20 (unsupported
1195
+ * topology) — with a message-matching fallback for edge cases surfaced by
1196
+ * older driver versions.
1197
+ */
1198
+ declare function isTransactionUnsupported(error: Error): boolean;
1199
+ //#endregion
1128
1200
  //#region src/index.d.ts
1129
1201
  /**
1130
1202
  * Factory function to create a repository instance
@@ -1138,4 +1210,4 @@ declare class Repository<TDoc = unknown> {
1138
1210
  */
1139
1211
  declare function createRepository<TDoc>(Model: _$mongoose.Model<TDoc, any, any, any>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions): Repository<TDoc>;
1140
1212
  //#endregion
1141
- 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, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validateUpdateBody, validationChainPlugin };
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 };
package/dist/index.mjs CHANGED
@@ -1,13 +1,13 @@
1
1
  import { a as warn, n as parseDuplicateKeyError, r as configureLogger, t as createError } from "./error-Bpbi_NKo.mjs";
2
2
  import { y as LookupBuilder } from "./update-AVfKWNGt.mjs";
3
3
  import { t as actions_exports } from "./actions/index.mjs";
4
- import { t as PaginationEngine } from "./PaginationEngine-CpsQl471.mjs";
5
- import { A as aggregateHelpersPlugin, C as HOOK_PRIORITY, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as cachePlugin, T as AggregationBuilder, _ as dateSequentialId, a as uniqueField, b as sequentialId, c as subdocumentPlugin, d as multiTenantPlugin, f as mongoOperationsPlugin, g as customIdPlugin, h as elasticSearchPlugin, i as requireField, k as auditLogPlugin, l as softDeletePlugin, m as fieldFilterPlugin, n as blockIf, o as validationChainPlugin, p as methodRegistryPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as observabilityPlugin, v as getNextSequence, w as Repository, x as cascadePlugin, y as prefixedId } from "./validation-chain.plugin-BCPh2UZG.mjs";
4
+ import { t as PaginationEngine } from "./PaginationEngine-CunXkR9p.mjs";
5
+ import { A as auditTrailPlugin, C as HOOK_PRIORITY, D as AggregationBuilder, E as withTransaction, M as aggregateHelpersPlugin, O as batchOperationsPlugin, S as cachePlugin, T as isTransactionUnsupported, _ as dateSequentialId, a as uniqueField, b as sequentialId, c as subdocumentPlugin, d as multiTenantPlugin, f as mongoOperationsPlugin, g as customIdPlugin, h as elasticSearchPlugin, i as requireField, j as auditLogPlugin, k as AuditTrailQuery, l as softDeletePlugin, m as fieldFilterPlugin, n as blockIf, o as validationChainPlugin, p as methodRegistryPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as observabilityPlugin, v as getNextSequence, w as Repository, x as cascadePlugin, y as prefixedId } from "./validation-chain.plugin-3rr7dlw6.mjs";
6
6
  import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
7
- import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-reyDRzXf.mjs";
7
+ import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-C5o7jyyA.mjs";
8
8
  import { buildFieldTypeMap, coerceFieldValue, coerceHeuristic } from "./query/primitives/coercion.mjs";
9
9
  import { extractSchemaIndexes } from "./query/primitives/indexes.mjs";
10
- import { a as isFieldUpdateAllowed, i as getSystemManagedFields, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-DpjhFfaO.mjs";
10
+ import { a as isFieldUpdateAllowed, i as getSystemManagedFields, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-8Cyu3NFJ.mjs";
11
11
  //#region src/query/QueryParser.ts
12
12
  /**
13
13
  * Modern Query Parser
@@ -945,4 +945,4 @@ function createRepository(Model, plugins = [], paginationConfig = {}, options =
945
945
  }
946
946
  var src_default = Repository;
947
947
  //#endregion
948
- export { AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, PaginationEngine, QueryParser, Repository, actions_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, customIdPlugin, dateSequentialId, src_default as default, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getNextSequence, getSystemManagedFields, immutableField, isFieldUpdateAllowed, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validateUpdateBody, validationChainPlugin };
948
+ export { AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, PaginationEngine, QueryParser, Repository, actions_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, customIdPlugin, dateSequentialId, src_default as default, 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 };
@@ -1,3 +1,4 @@
1
+ import { n as isObjectIdInstance } from "./id-resolution-LZ-cCwR5.mjs";
1
2
  import "mongoose";
2
3
  //#region src/utils/memory-cache.ts
3
4
  /**
@@ -93,28 +94,56 @@ function buildCrudSchemasFromModel(mongooseModel, options = {}) {
93
94
  return buildCrudSchemasFromMongooseSchema(mongooseModel.schema, options);
94
95
  }
95
96
  /**
96
- * Get fields that are immutable (cannot be updated)
97
+ * Collect fields to omit from a generated schema based on field rules and
98
+ * explicit omit lists. This is the single source of truth for "which fields
99
+ * should NOT appear in the create/update body" — called by both the
100
+ * path-based and tree-based schema builders.
101
+ */
102
+ function collectFieldsToOmit(options, purpose) {
103
+ const result = new Set([
104
+ "createdAt",
105
+ "updatedAt",
106
+ "__v"
107
+ ]);
108
+ const rules = options?.fieldRules || {};
109
+ for (const [field, rule] of Object.entries(rules)) {
110
+ if (rule.systemManaged) result.add(field);
111
+ if (purpose === "update" && (rule.immutable || rule.immutableAfterCreate)) result.add(field);
112
+ }
113
+ const explicit = purpose === "create" ? options?.create?.omitFields : options?.update?.omitFields;
114
+ if (explicit) for (const f of explicit) result.add(f);
115
+ return result;
116
+ }
117
+ /**
118
+ * Apply omissions + optional-overrides to a built JSON schema in-place.
119
+ * Removes properties and adjusts the `required` array.
120
+ */
121
+ function applyFieldRules(schema, fieldsToOmit, options) {
122
+ for (const field of fieldsToOmit) {
123
+ if (schema.properties?.[field]) delete schema.properties[field];
124
+ if (schema.required) schema.required = schema.required.filter((k) => k !== field);
125
+ }
126
+ const rules = options?.fieldRules || {};
127
+ for (const [field, rule] of Object.entries(rules)) if (rule.optional && schema.required) schema.required = schema.required.filter((k) => k !== field);
128
+ }
129
+ /**
130
+ * Get fields that are immutable (cannot be updated).
131
+ * Delegates to `collectFieldsToOmit` internally.
97
132
  */
98
133
  function getImmutableFields(options = {}) {
99
134
  const immutable = [];
100
- const fieldRules = options?.fieldRules || {};
101
- Object.entries(fieldRules).forEach(([field, rules]) => {
102
- if (rules.immutable || rules.immutableAfterCreate) immutable.push(field);
103
- });
104
- (options?.update?.omitFields || []).forEach((f) => {
105
- if (!immutable.includes(f)) immutable.push(f);
106
- });
135
+ const rules = options?.fieldRules || {};
136
+ for (const [field, rule] of Object.entries(rules)) if (rule.immutable || rule.immutableAfterCreate) immutable.push(field);
137
+ for (const f of options?.update?.omitFields || []) if (!immutable.includes(f)) immutable.push(f);
107
138
  return immutable;
108
139
  }
109
140
  /**
110
- * Get fields that are system-managed (cannot be set by users)
141
+ * Get fields that are system-managed (cannot be set by users).
111
142
  */
112
143
  function getSystemManagedFields(options = {}) {
113
144
  const systemManaged = [];
114
- const fieldRules = options?.fieldRules || {};
115
- Object.entries(fieldRules).forEach(([field, rules]) => {
116
- if (rules.systemManaged) systemManaged.push(field);
117
- });
145
+ const rules = options?.fieldRules || {};
146
+ for (const [field, rule] of Object.entries(rules)) if (rule.systemManaged) systemManaged.push(field);
118
147
  return systemManaged;
119
148
  }
120
149
  /**
@@ -161,7 +190,11 @@ function buildJsonSchemaFromPaths(mongooseSchema, options) {
161
190
  };
162
191
  const rootFields = /* @__PURE__ */ new Map();
163
192
  for (const [path, schemaType] of Object.entries(paths)) {
164
- if (path === "_id" || path === "__v") continue;
193
+ if (path === "__v") continue;
194
+ if (path === "_id") {
195
+ const instance = schemaType.instance;
196
+ if (!instance || isObjectIdInstance(instance)) continue;
197
+ }
165
198
  const rootField = path.split(".")[0];
166
199
  if (!rootFields.has(rootField)) rootFields.set(rootField, []);
167
200
  rootFields.get(rootField)?.push({
@@ -183,30 +216,12 @@ function buildJsonSchemaFromPaths(mongooseSchema, options) {
183
216
  properties
184
217
  };
185
218
  if (required.length) schema.required = required;
186
- const fieldsToOmit = new Set([
187
- "createdAt",
188
- "updatedAt",
189
- "__v"
190
- ]);
191
- (options?.create?.omitFields || []).forEach((f) => {
192
- fieldsToOmit.add(f);
193
- });
194
- const fieldRules = options?.fieldRules || {};
195
- Object.entries(fieldRules).forEach(([field, rules]) => {
196
- if (rules.systemManaged) fieldsToOmit.add(field);
197
- });
198
- fieldsToOmit.forEach((field) => {
199
- if (schema.properties?.[field]) delete schema.properties[field];
200
- if (schema.required) schema.required = schema.required.filter((k) => k !== field);
201
- });
219
+ applyFieldRules(schema, collectFieldsToOmit(options, "create"), options);
202
220
  const reqOv = options?.create?.requiredOverrides || {};
203
221
  const optOv = options?.create?.optionalOverrides || {};
204
222
  schema.required = schema.required || [];
205
223
  for (const [k, v] of Object.entries(reqOv)) if (v && !schema.required.includes(k)) schema.required.push(k);
206
224
  for (const [k, v] of Object.entries(optOv)) if (v && schema.required) schema.required = schema.required.filter((x) => x !== k);
207
- Object.entries(fieldRules).forEach(([field, rules]) => {
208
- if (rules.optional && schema.required) schema.required = schema.required.filter((x) => x !== field);
209
- });
210
225
  const schemaOverrides = options?.create?.schemaOverrides || {};
211
226
  for (const [k, override] of Object.entries(schemaOverrides)) if (schema.properties?.[k]) schema.properties[k] = override;
212
227
  if (options?.strictAdditionalProperties === true) schema.additionalProperties = false;
@@ -270,7 +285,7 @@ function schemaTypeToJsonSchema(schemaType) {
270
285
  else if (instance === "Date") {
271
286
  result.type = "string";
272
287
  result.format = "date-time";
273
- } else if (instance === "ObjectId" || instance === "ObjectID") {
288
+ } else if (isObjectIdInstance(instance)) {
274
289
  result.type = "string";
275
290
  result.pattern = "^[0-9a-fA-F]{24}$";
276
291
  } else if (instance === "Array") {
@@ -285,17 +300,7 @@ function schemaTypeToJsonSchema(schemaType) {
285
300
  function buildJsonSchemaForUpdate(createJson, options) {
286
301
  const clone = JSON.parse(JSON.stringify(createJson));
287
302
  delete clone.required;
288
- const fieldsToOmit = /* @__PURE__ */ new Set();
289
- (options?.update?.omitFields || []).forEach((f) => {
290
- fieldsToOmit.add(f);
291
- });
292
- const fieldRules = options?.fieldRules || {};
293
- Object.entries(fieldRules).forEach(([field, rules]) => {
294
- if (rules.immutable || rules.immutableAfterCreate) fieldsToOmit.add(field);
295
- });
296
- fieldsToOmit.forEach((field) => {
297
- if (clone.properties?.[field]) delete clone.properties[field];
298
- });
303
+ applyFieldRules(clone, collectFieldsToOmit(options, "update"), options);
299
304
  if (options?.strictAdditionalProperties === true) clone.additionalProperties = false;
300
305
  if (options?.update?.requireAtLeastOne === true) clone.minProperties = 1;
301
306
  return clone;
@@ -1,4 +1,4 @@
1
- import { T as FieldPreset, _ as CrudSchemas, k as HttpError, ot as SchemaBuilderOptions, s as CacheAdapter, xt as ValidationResult, yt as UserContext } from "./types-CWUL0yiw.mjs";
1
+ import { T as FieldPreset, _ as CrudSchemas, k as HttpError, ot as SchemaBuilderOptions, s as CacheAdapter, xt as ValidationResult, yt as UserContext } from "./types-DiQfDesD.mjs";
2
2
  import mongoose, { Schema } from "mongoose";
3
3
 
4
4
  //#region src/utils/error.d.ts
@@ -146,11 +146,12 @@ declare function buildCrudSchemasFromMongooseSchema(mongooseSchema: Schema, opti
146
146
  */
147
147
  declare function buildCrudSchemasFromModel(mongooseModel: mongoose.Model<unknown>, options?: SchemaBuilderOptions): CrudSchemas;
148
148
  /**
149
- * Get fields that are immutable (cannot be updated)
149
+ * Get fields that are immutable (cannot be updated).
150
+ * Delegates to `collectFieldsToOmit` internally.
150
151
  */
151
152
  declare function getImmutableFields(options?: SchemaBuilderOptions): string[];
152
153
  /**
153
- * Get fields that are system-managed (cannot be set by users)
154
+ * Get fields that are system-managed (cannot be set by users).
154
155
  */
155
156
  declare function getSystemManagedFields(options?: SchemaBuilderOptions): string[];
156
157
  /**
@@ -1,4 +1,4 @@
1
- import { F as KeysetPaginationResult, G as PaginationConfig, H as OffsetPaginationOptions, P as KeysetPaginationOptions, U as OffsetPaginationResult, a as AnyDocument, n as AggregatePaginationOptions, r as AggregatePaginationResult } from "../types-CWUL0yiw.mjs";
1
+ import { F as KeysetPaginationResult, G as PaginationConfig, H as OffsetPaginationOptions, P as KeysetPaginationOptions, U as OffsetPaginationResult, a as AnyDocument, n as AggregatePaginationOptions, r as AggregatePaginationResult } from "../types-DiQfDesD.mjs";
2
2
  import { Model } from "mongoose";
3
3
 
4
4
  //#region src/pagination/PaginationEngine.d.ts
@@ -20,6 +20,11 @@ interface ResolvedPaginationConfig {
20
20
  declare class PaginationEngine<TDoc = AnyDocument> {
21
21
  readonly Model: Model<TDoc>;
22
22
  readonly config: ResolvedPaginationConfig;
23
+ /**
24
+ * Lazily-cached schema index snapshot used by stream() to decide whether
25
+ * to emit the "missing compound index" warning. Computed on first use.
26
+ */
27
+ private _cachedSchemaIndexes;
23
28
  /**
24
29
  * Create a new pagination engine
25
30
  *
@@ -27,6 +32,8 @@ declare class PaginationEngine<TDoc = AnyDocument> {
27
32
  * @param config - Pagination configuration
28
33
  */
29
34
  constructor(Model: Model<TDoc, any, any, any>, config?: PaginationConfig);
35
+ /** Memoized schema index lookup — avoids re-walking schema on every stream(). */
36
+ private _getSchemaIndexes;
30
37
  /**
31
38
  * Offset-based pagination using skip/limit
32
39
  * Best for small datasets and when users need random page access
@@ -1,2 +1,2 @@
1
- import { t as PaginationEngine } from "../PaginationEngine-CpsQl471.mjs";
1
+ import { t as PaginationEngine } from "../PaginationEngine-CunXkR9p.mjs";
2
2
  export { PaginationEngine };
@@ -1,2 +1,2 @@
1
- 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, 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, y as MethodRegistryRepository, z as batchOperationsPlugin } from "../validation-chain.plugin-Dn41aAbK.mjs";
1
+ 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, 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, y as MethodRegistryRepository, z as batchOperationsPlugin } from "../validation-chain.plugin-COKNRAwp.mjs";
2
2
  export { type AggregateHelpersMethods, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BatchOperationsMethods, type CacheMethods, type CustomIdOptions, type DateSequentialIdOptions, type ElasticSearchOptions, type IdGenerator, type MethodRegistryRepository, type MongoOperationsMethods, type MultiTenantOptions, type ObservabilityOptions, type OperationMetric, type PrefixedIdOptions, type SequentialIdOptions, type SoftDeleteMethods, type SubdocumentMethods, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
@@ -1,2 +1,2 @@
1
- import { A as aggregateHelpersPlugin, D as AuditTrailQuery, E as batchOperationsPlugin, O as auditTrailPlugin, S as cachePlugin, _ as dateSequentialId, a as uniqueField, b as sequentialId, c as subdocumentPlugin, d as multiTenantPlugin, f as mongoOperationsPlugin, g as customIdPlugin, h as elasticSearchPlugin, i as requireField, k as auditLogPlugin, l as softDeletePlugin, m as fieldFilterPlugin, n as blockIf, o as validationChainPlugin, p as methodRegistryPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as observabilityPlugin, v as getNextSequence, x as cascadePlugin, y as prefixedId } from "../validation-chain.plugin-BCPh2UZG.mjs";
1
+ import { A as auditTrailPlugin, M as aggregateHelpersPlugin, O as batchOperationsPlugin, S as cachePlugin, _ as dateSequentialId, a as uniqueField, b as sequentialId, c as subdocumentPlugin, d as multiTenantPlugin, f as mongoOperationsPlugin, g as customIdPlugin, h as elasticSearchPlugin, i as requireField, j as auditLogPlugin, k as AuditTrailQuery, l as softDeletePlugin, m as fieldFilterPlugin, n as blockIf, o as validationChainPlugin, p as methodRegistryPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as observabilityPlugin, v as getNextSequence, x as cascadePlugin, y as prefixedId } from "../validation-chain.plugin-3rr7dlw6.mjs";
2
2
  export { AuditTrailQuery, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };