@classytic/mongokit 3.5.6 → 3.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-DmVbUzDL.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-BwO4u3JN.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
@@ -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-BwO4u3JN.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-BwO4u3JN.mjs";
2
- import { t as index_d_exports } from "./index-DmVbUzDL.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-BtmT-FFB.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-mI2Z19lM.mjs";
5
5
  import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-B19MD38s.mjs";
6
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-DQMU0Dlv.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
  */
@@ -1140,6 +1158,45 @@ declare class Repository<TDoc = unknown> {
1140
1158
  _handleError(error: Error): HttpError;
1141
1159
  }
1142
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
1143
1200
  //#region src/index.d.ts
1144
1201
  /**
1145
1202
  * Factory function to create a repository instance
@@ -1153,4 +1210,4 @@ declare class Repository<TDoc = unknown> {
1153
1210
  */
1154
1211
  declare function createRepository<TDoc>(Model: _$mongoose.Model<TDoc, any, any, any>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions): Repository<TDoc>;
1155
1212
  //#endregion
1156
- 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,8 +1,8 @@
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-DCYjnPau.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-DoOEyM_g.mjs";
6
6
  import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
7
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";
@@ -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,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-BwO4u3JN.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
@@ -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-BwO4u3JN.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-BtmT-FFB.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-mI2Z19lM.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-DCYjnPau.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-DoOEyM_g.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 };
@@ -928,6 +928,17 @@ interface RepositoryContext {
928
928
  softDeleted?: boolean;
929
929
  /** Include soft-deleted documents in queries */
930
930
  includeDeleted?: boolean;
931
+ /**
932
+ * Caller-requested delete semantics, read by softDeletePlugin.
933
+ *
934
+ * - `undefined` (default) — plugin decides: soft if wired, hard otherwise.
935
+ * - `'hard'` — bypass soft-delete and physically remove the document.
936
+ * All policy hooks (multi-tenant scope, audit trails, cache invalidation)
937
+ * still fire; only the soft-delete interception is skipped.
938
+ * - `'soft'` — reserved. Currently treated as default since softDeletePlugin
939
+ * is the only source of soft semantics.
940
+ */
941
+ deleteMode?: 'hard' | 'soft';
931
942
  /** Skip cache for this operation */
932
943
  skipCache?: boolean;
933
944
  /** Custom TTL for this operation (seconds) */
@@ -982,7 +993,7 @@ interface RepositoryInstance {
982
993
  [key: string]: unknown;
983
994
  }
984
995
  /** Repository operation names */
985
- type RepositoryOperation = 'create' | 'createMany' | 'update' | 'updateMany' | 'delete' | 'deleteMany' | 'getById' | 'getByQuery' | 'getOne' | 'getAll' | 'findAll' | 'getOrCreate' | 'count' | 'exists' | 'distinct' | 'aggregate' | 'aggregatePaginate' | 'lookupPopulate' | 'bulkWrite';
996
+ type RepositoryOperation = 'create' | 'createMany' | 'update' | 'updateMany' | 'delete' | 'deleteMany' | 'restore' | 'getById' | 'getByQuery' | 'getOne' | 'getAll' | 'findAll' | 'getOrCreate' | 'count' | 'exists' | 'distinct' | 'aggregate' | 'aggregatePaginate' | 'lookupPopulate' | 'bulkWrite';
986
997
  /** Event lifecycle phases */
987
998
  type EventPhase = 'before' | 'after' | 'error';
988
999
  /** Repository event names (generated from template literals) */
@@ -1263,8 +1274,23 @@ interface CacheStats {
1263
1274
  }
1264
1275
  /** Cascade relation definition */
1265
1276
  interface CascadeRelation {
1266
- /** Model name to cascade delete to */
1267
- model: string;
1277
+ /**
1278
+ * Preferred: a Repository instance for the target collection. When present,
1279
+ * cascade routes deletes through `repo.delete` / `repo.deleteMany`, so the
1280
+ * target's `before:delete` / `before:deleteMany` hooks fire — meaning
1281
+ * multi-tenant scoping, audit logging, cache invalidation, and the target's
1282
+ * own soft-delete plugin (with its configured `deletedField`) all run
1283
+ * correctly. Strongly recommended for any target that has policy plugins.
1284
+ */
1285
+ repo?: RepositoryInstance;
1286
+ /**
1287
+ * Legacy: a Mongoose model name. When set without `repo`, cascade writes
1288
+ * directly via `RelatedModel.updateMany` / `RelatedModel.deleteMany`. This
1289
+ * path bypasses the target's hooks — it will NOT enforce tenant scoping,
1290
+ * fire audit events, or honor a custom `deletedField`. Only safe for
1291
+ * trivial targets with no policy plugins. Prefer `repo` for new code.
1292
+ */
1293
+ model?: string;
1268
1294
  /** Foreign key field in the related model that references the deleted document */
1269
1295
  foreignKey: string;
1270
1296
  /** Whether to use soft delete if available (default: follows parent behavior) */
@@ -1,5 +1,5 @@
1
- import { Z as PopulateSpec, pt as SortSpec, st as SelectSpec } from "../types-BwO4u3JN.mjs";
2
- import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, p as createError, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel, u as filterResponseData } from "../mongooseToJsonSchema-DQMU0Dlv.mjs";
1
+ import { Z as PopulateSpec, pt as SortSpec, st as SelectSpec } from "../types-DiQfDesD.mjs";
2
+ import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, 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";
3
3
 
4
4
  //#region src/utils/cache-keys.d.ts
5
5
  /**