@classytic/mongokit 3.7.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import { r as warn } from "./logger-DJ7egMnd.mjs";
2
2
  import { t as createError } from "./error-Lw9GEAro.mjs";
3
- import { n as encodeCursor, r as resolveCursorFilter } from "./cursor-DZ9JSoMM.mjs";
4
- import { r as validateKeysetSort, t as getPrimaryField } from "./sort-ffe-9JBg.mjs";
3
+ import { n as encodeCursor, r as resolveCursorFilter } from "./cursor-J84rR6wG.mjs";
4
+ import { r as validateKeysetSort, t as getPrimaryField } from "./sort-CrZjTK25.mjs";
5
5
  //#region src/pagination/utils/index-hint.ts
6
6
  /**
7
7
  * Read a Mongoose schema's declared indexes, defensively.
@@ -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-C-qZUqv8.mjs";
1
+ import { a as create_d_exports, i as delete_d_exports, n as update_d_exports, o as aggregate_d_exports, r as read_d_exports } from "../index-DlSAFIc9.mjs";
2
2
  export { aggregate_d_exports as aggregate, create_d_exports as create, delete_d_exports as deleteActions, read_d_exports as read, update_d_exports as update };
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "../chunk-CfYAbeIz.mjs";
2
- import { _ as aggregate_exports, f as delete_exports, h as create_exports, l as read_exports, r as update_exports } from "../update-BDsQ3cdy.mjs";
2
+ import { g as create_exports, i as update_exports, p as delete_exports, u as read_exports, v as aggregate_exports } from "../update-yjSoNnWy.mjs";
3
3
  //#region src/actions/index.ts
4
4
  var actions_exports = /* @__PURE__ */ __exportAll({
5
5
  aggregate: () => aggregate_exports,
@@ -1,4 +1,4 @@
1
- import { J as Plugin } from "../types-BgdeBXI1.mjs";
1
+ import { Y as Plugin } from "../types-D57SZJeE.mjs";
2
2
  import { ClientSession, PipelineStage } from "mongoose";
3
3
 
4
4
  //#region src/ai/types.d.ts
@@ -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-BgdeBXI1.mjs";
1
+ import { B as MinMaxResult, D as FindOneAndUpdateOptions, G as OperationOptions, H as ObjectId, O as GroupResult, Q as PopulateSpec, _t as UpdateManyResult, a as AnyDocument, b as DeleteResult, ct as SelectSpec, et as ReadPreferenceType, g as CreateOptions, jt as LookupOptions, mt as SortSpec, vt as UpdateOptions, yt as UpdateWithValidationResult } from "./types-D57SZJeE.mjs";
2
2
  import { ClientSession, Model, PipelineStage } from "mongoose";
3
3
 
4
4
  //#region src/actions/aggregate.d.ts
@@ -213,7 +213,7 @@ declare function exists<TDoc = AnyDocument>(Model: Model<TDoc>, query: Record<st
213
213
  _id: unknown;
214
214
  } | null>;
215
215
  declare namespace update_d_exports {
216
- export { increment, pullFromArray, pushToArray, update, updateByQuery, updateMany, updateWithConstraints, updateWithValidation };
216
+ export { findOneAndUpdate, increment, pullFromArray, pushToArray, update, updateByQuery, updateMany, updateWithConstraints, updateWithValidation };
217
217
  }
218
218
  /**
219
219
  * Update by ID
@@ -255,6 +255,14 @@ declare function updateMany(Model: Model<unknown>, query: Record<string, unknown
255
255
  * Update by query
256
256
  */
257
257
  declare function updateByQuery<TDoc = AnyDocument>(Model: Model<TDoc>, query: Record<string, unknown>, data: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc | null>;
258
+ /**
259
+ * Atomic findOneAndUpdate primitive.
260
+ *
261
+ * Returns the matched document (post-update by default) or null when no doc
262
+ * matches and `upsert` is false. Used by outbox relays, distributed locks,
263
+ * and workflow semaphores that need compare-and-set in a single round-trip.
264
+ */
265
+ declare function findOneAndUpdate<TDoc = AnyDocument>(Model: Model<TDoc>, filter: Record<string, unknown>, update: Record<string, unknown> | Record<string, unknown>[], options?: FindOneAndUpdateOptions): Promise<TDoc | null>;
258
266
  /**
259
267
  * Increment field
260
268
  */
package/dist/index.d.mts CHANGED
@@ -1,13 +1,49 @@
1
- import { $ as ReadPreferenceType, A as InferDocument, At as LookupOptions, B as NonNullableFields, C as EventPayload, Ct as WithPlugins, D as GroupResult, Dt as IRequestContext, E as FieldRules, Et as IControllerResponse, F as KeysetPaginationResult, G as PaginationConfig, H as OffsetPaginationOptions, I as Logger, J as Plugin, K as PaginationResult, L as LookupPopulateOptions, M as JsonSchema, N as KeysOfType, O as HookMode, Ot as IResponseFormatter, P as KeysetPaginationOptions, Q as ReadOptions, R as LookupPopulateResult, S as EventHandlers, St as ValidatorDefinition, T as FieldPreset, Tt as IController, U as OffsetPaginationResult, V as ObjectId, W as OperationOptions, X as PluginType, Y as PluginFunction, Z as PopulateSpec, _ as CrudSchemas, _t as UpdateOptions, a as AnyDocument, at as RequiredBy, b as DeleteResult, bt as ValidationChainOptions, c as CacheOperationOptions, ct as SessionOptions, d as CacheableOptions, dt as SoftDeleteRepository, et as RepositoryContext, f as CascadeOptions, ft as SortDirection, g as CreateOptions, gt as UpdateManyResult, h as CreateInput, ht as UpdateInput, i as AllPluginMethods, it as RepositoryOptions, j as InferRawDoc, k as HttpError, kt as LookupBuilder, l as CacheOptions, lt as SoftDeleteFilterMode, m as CollationOptions, mt as Strict, n as AggregatePaginationOptions, nt as RepositoryInstance, o as AnyModel, ot as SchemaBuilderOptions, p as CascadeRelation, pt as SortSpec, q as PartialBy, r as AggregatePaginationResult, rt as RepositoryOperation, s as CacheAdapter, st as SelectSpec, t as AggregateOptions, tt as RepositoryEvent, u as CacheStats, ut as SoftDeleteOptions, v as DecodedCursor, vt as UpdateWithValidationResult, w as EventPhase, wt as WithTransactionOptions, x as DocField, xt as ValidationResult, y as DeepPartial, yt as UserContext, z as MinMaxResult } from "./types-BgdeBXI1.mjs";
2
- import { t as index_d_exports } from "./index-C-qZUqv8.mjs";
1
+ import { $ as ReadOptions, A as HttpError, At as LookupBuilder, B as MinMaxResult, C as EventPayload, Ct as ValidatorDefinition, D as FindOneAndUpdateOptions, Dt as IControllerResponse, E as FieldRules, Et as IController, F as KeysetPaginationOptions, G as OperationOptions, H as ObjectId, I as KeysetPaginationResult, J as PartialBy, K as PaginationConfig, L as Logger, M as InferRawDoc, N as JsonSchema, O as GroupResult, Ot as IRequestContext, P as KeysOfType, Q as PopulateSpec, R as LookupPopulateOptions, S as EventHandlers, St as ValidationResult, T as FieldPreset, Tt as WithTransactionOptions, U as OffsetPaginationOptions, V as NonNullableFields, W as OffsetPaginationResult, X as PluginFunction, Y as Plugin, Z as PluginType, _ as CrudSchemas, _t as UpdateManyResult, a as AnyDocument, at as RepositoryOptions, b as DeleteResult, bt as UserContext, c as CacheOperationOptions, ct as SelectSpec, d as CacheableOptions, dt as SoftDeleteOptions, et as ReadPreferenceType, f as CascadeOptions, ft as SoftDeleteRepository, g as CreateOptions, gt as UpdateInput, h as CreateInput, ht as Strict, i as AllPluginMethods, it as RepositoryOperation, j as InferDocument, jt as LookupOptions, k as HookMode, kt as IResponseFormatter, l as CacheOptions, lt as SessionOptions, m as CollationOptions, mt as SortSpec, n as AggregatePaginationOptions, nt as RepositoryEvent, o as AnyModel, ot as RequiredBy, p as CascadeRelation, pt as SortDirection, q as PaginationResult, r as AggregatePaginationResult, rt as RepositoryInstance, s as CacheAdapter, st as SchemaBuilderOptions, t as AggregateOptions, tt as RepositoryContext, u as CacheStats, ut as SoftDeleteFilterMode, v as DecodedCursor, vt as UpdateOptions, w as EventPhase, wt as WithPlugins, x as DocField, xt as ValidationChainOptions, y as DeepPartial, yt as UpdateWithValidationResult, z as LookupPopulateResult } from "./types-D57SZJeE.mjs";
2
+ import { t as index_d_exports } from "./index-DlSAFIc9.mjs";
3
3
  import { PaginationEngine } from "./pagination/PaginationEngine.mjs";
4
- import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, T as ElasticSearchOptions, U as AuditEntry, V as BulkWriteResult, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "./validation-chain.plugin-Be9rG04B.mjs";
4
+ import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, T as ElasticSearchOptions, U as AuditEntry, V as BulkWriteResult, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "./validation-chain.plugin-wGF34Ym-.mjs";
5
5
  import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-oM8G4b6m.mjs";
6
6
  import { n as SchemaIndexes } from "./indexes-C26GNZRU.mjs";
7
- import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, m as parseDuplicateKeyError, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, p as createError, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel, u as filterResponseData } from "./mongooseToJsonSchema-Du-UopRK.mjs";
7
+ import { a as isFieldUpdateAllowed, c as configureLogger, d as getFieldsForUser, f as getMongooseProjection, i as getSystemManagedFields, l as createFieldPreset, m as parseDuplicateKeyError, n as buildCrudSchemasFromMongooseSchema, o as validateUpdateBody, p as createError, r as getImmutableFields, s as createMemoryCache, t as buildCrudSchemasFromModel, u as filterResponseData } from "./mongooseToJsonSchema-BZaKOXXI.mjs";
8
8
  import * as _$mongoose from "mongoose";
9
9
  import { ClientSession, Expression, Model, PipelineStage, PopulateOptions } from "mongoose";
10
10
 
11
+ //#region src/operations.d.ts
12
+ /**
13
+ * Where a plugin should inject its scoping filter (multi-tenant scope,
14
+ * soft-delete filter, etc.) on the RepositoryContext.
15
+ *
16
+ * - `data` — single-doc create payload (`context.data`)
17
+ * - `dataArray` — multi-doc create payload (`context.dataArray`)
18
+ * - `query` — raw filter (`context.query`); the dominant convention
19
+ * - `filters` — paginated list options' filter sub-bag (`context.filters`)
20
+ * - `operations` — bulkWrite per-sub-op (special — plugins walk each entry)
21
+ * - `none` — no scoping target (op accepts no filter input)
22
+ */
23
+ type PolicyKey = 'data' | 'dataArray' | 'query' | 'filters' | 'operations' | 'none';
24
+ interface OperationDescriptor {
25
+ /** Where multi-tenant / soft-delete plugins inject their scoping filter. */
26
+ policyKey: PolicyKey;
27
+ /** Whether this op writes to the database. Drives audit + cache invalidation. */
28
+ mutates: boolean;
29
+ /** True when `context.id` is populated by the time before/after hooks fire. */
30
+ hasIdContext: boolean;
31
+ }
32
+ /**
33
+ * The registry. Order intentionally groups by category for readability.
34
+ * Adding a new RepositoryOperation requires exactly one entry here, after
35
+ * which every bundled plugin auto-classifies it correctly.
36
+ */
37
+ declare const OP_REGISTRY: Readonly<Record<RepositoryOperation, OperationDescriptor>>;
38
+ /** All known repository operations, in registry order. */
39
+ declare const ALL_OPERATIONS: RepositoryOperation[];
40
+ /** Operations that mutate the database. Drives audit + cache invalidation lists. */
41
+ declare const MUTATING_OPERATIONS: readonly RepositoryOperation[];
42
+ /** Operations that don't mutate. Drives default cacheable ops. */
43
+ declare const READ_OPERATIONS: readonly RepositoryOperation[];
44
+ /** Filter ops by their policy injection key. */
45
+ declare function operationsByPolicyKey(key: PolicyKey): RepositoryOperation[];
46
+ //#endregion
11
47
  //#region src/query/AggregationBuilder.d.ts
12
48
  /** Options for $vectorSearch stage (Atlas only) */
13
49
  interface VectorSearchOptions {
@@ -998,6 +1034,36 @@ declare class Repository<TDoc = unknown> {
998
1034
  * Update document by ID
999
1035
  */
1000
1036
  update(id: string | ObjectId, data: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc>;
1037
+ /**
1038
+ * Atomic findOneAndUpdate (compare-and-set primitive).
1039
+ *
1040
+ * Single round-trip match-and-mutate for outbox relays, distributed locks,
1041
+ * and workflow semaphores. Goes through the full hook pipeline so
1042
+ * multi-tenant scope, soft-delete, audit, and cache plugins all apply.
1043
+ *
1044
+ * Returns the matched document (post-update by default) or `null` when no
1045
+ * document matches and `upsert` is false.
1046
+ *
1047
+ * @example FIFO claim-lease for an outbox worker
1048
+ * ```ts
1049
+ * const claimed = await outboxRepo.findOneAndUpdate(
1050
+ * { status: 'pending', leaseExpiresAt: { $lt: new Date() } },
1051
+ * { $set: { status: 'processing', leaseExpiresAt: leaseUntil, leasedBy: workerId } },
1052
+ * { sort: { createdAt: 1 }, returnDocument: 'after' },
1053
+ * );
1054
+ * if (!claimed) return; // queue empty
1055
+ * ```
1056
+ *
1057
+ * @example CAS upsert
1058
+ * ```ts
1059
+ * const lock = await locksRepo.findOneAndUpdate(
1060
+ * { _id: lockKey, ownerId: { $in: [null, workerId] } },
1061
+ * { $set: { ownerId: workerId, expiresAt: leaseUntil } },
1062
+ * { upsert: true },
1063
+ * );
1064
+ * ```
1065
+ */
1066
+ findOneAndUpdate<TResult = TDoc>(filter: Record<string, unknown>, update: Record<string, unknown> | Record<string, unknown>[], options?: FindOneAndUpdateOptions): Promise<TResult | null>;
1001
1067
  /**
1002
1068
  * Delete a document by ID.
1003
1069
  *
@@ -1232,4 +1298,4 @@ declare function isTransactionUnsupported(error: Error): boolean;
1232
1298
  */
1233
1299
  declare function createRepository<TDoc>(Model: _$mongoose.Model<TDoc, any, any, any>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions): Repository<TDoc>;
1234
1300
  //#endregion
1235
- 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 TenantContext, type TenantStore, type UpdateInput, type UpdateManyResult, type UpdateOptions, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidationResult, type ValidatorDefinition, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getNextSequence, getSystemManagedFields, immutableField, isFieldUpdateAllowed, isTransactionUnsupported, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validateUpdateBody, validationChainPlugin, withTransaction };
1301
+ export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, type AggregatePaginationResult, AggregationBuilder, type AllPluginMethods, type AnyDocument, type AnyModel, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailOptions, AuditTrailQuery, type BatchOperationsMethods, type BulkWriteResult, type CacheAdapter, type CacheMethods, type CacheOperationOptions, type CacheOptions, type CacheStats, type CacheableOptions, type CascadeOptions, type CascadeRelation, type CollationOptions, type CreateInput, type CreateOptions, type CrudSchemas, type CustomIdOptions, type DateSequentialIdOptions, type DecodedCursor, type DeepPartial, type DeleteResult, type DocField, type ElasticSearchOptions, type EventHandlers, type EventPayload, type EventPhase, type FieldPreset, type FieldRules, type FieldType, type FilterQuery, type FindOneAndUpdateOptions, type GroupResult, HOOK_PRIORITY, type HookMode, type HttpError, type IController, type IControllerResponse, type IRequestContext, type IResponseFormatter, type IdGenerator, type InferDocument, type InferRawDoc, type JsonSchema, type KeysOfType, type KeysetPaginationOptions, type KeysetPaginationResult, type Logger, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MUTATING_OPERATIONS, type MinMaxResult, type MongoOperationsMethods, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OffsetPaginationResult, type OperationDescriptor, type OperationMetric, type OperationOptions, type PaginationConfig, PaginationEngine, type PaginationResult, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, QueryParser, type QueryParserOptions, READ_OPERATIONS, type ReadOptions, type ReadPreferenceType, Repository, Repository as default, type RepositoryContext, type RepositoryEvent, type RepositoryInstance, type RepositoryOperation, type RepositoryOptions, type RequiredBy, type SchemaBuilderOptions, type SchemaLike, type SearchMode, type SelectSpec, type SequentialIdOptions, type SessionOptions, type SoftDeleteFilterMode, type SoftDeleteMethods, type SoftDeleteOptions, type SoftDeleteRepository, type SortDirection, type SortSpec, type Strict, type SubdocumentMethods, type TenantContext, type TenantStore, type UpdateInput, type UpdateManyResult, type UpdateOptions, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidationResult, type ValidatorDefinition, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getNextSequence, getSystemManagedFields, immutableField, isFieldUpdateAllowed, isTransactionUnsupported, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validateUpdateBody, validationChainPlugin, withTransaction };
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import { r as warn, t as configureLogger } from "./logger-DJ7egMnd.mjs";
2
- import { y as LookupBuilder } from "./update-BDsQ3cdy.mjs";
2
+ import { b as LookupBuilder } from "./update-yjSoNnWy.mjs";
3
3
  import { n as parseDuplicateKeyError, t as createError } from "./error-Lw9GEAro.mjs";
4
4
  import { t as actions_exports } from "./actions/index.mjs";
5
- import { t as PaginationEngine } from "./PaginationEngine-CPmZv7Ds.mjs";
6
- import { A as AuditTrailQuery, C as cachePlugin, D as withTransaction, E as isTransactionUnsupported, M as auditLogPlugin, N as aggregateHelpersPlugin, O as AggregationBuilder, S as cascadePlugin, T as Repository, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, w as HOOK_PRIORITY, x as sequentialId, y as getNextSequence } from "./validation-chain.plugin-CEyJssZl.mjs";
5
+ import { A as AuditTrailQuery, C as cachePlugin, D as withTransaction, E as isTransactionUnsupported, F as MUTATING_OPERATIONS, I as OP_REGISTRY, L as READ_OPERATIONS, M as auditLogPlugin, N as aggregateHelpersPlugin, O as AggregationBuilder, P as ALL_OPERATIONS, R as operationsByPolicyKey, S as cascadePlugin, T as Repository, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, w as HOOK_PRIORITY, x as sequentialId, y as getNextSequence } from "./validation-chain.plugin-DwoOOWse.mjs";
6
+ import { t as PaginationEngine } from "./PaginationEngine-C2_tAvAD.mjs";
7
7
  import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
8
8
  import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-dI0m4RBs.mjs";
9
9
  import { buildFieldTypeMap, coerceFieldValue, coerceHeuristic } from "./query/primitives/coercion.mjs";
@@ -986,4 +986,4 @@ function createRepository(Model, plugins = [], paginationConfig = {}, options =
986
986
  }
987
987
  var src_default = Repository;
988
988
  //#endregion
989
- 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, createTenantContext, 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 };
989
+ export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, MUTATING_OPERATIONS, OP_REGISTRY, PaginationEngine, QueryParser, READ_OPERATIONS, Repository, actions_exports as actions, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, configureLogger, createError, createFieldPreset, createMemoryCache, createRepository, createTenantContext, customIdPlugin, dateSequentialId, src_default as default, elasticSearchPlugin, fieldFilterPlugin, filterResponseData, getFieldsForUser, getImmutableFields, getMongooseProjection, getNextSequence, getSystemManagedFields, immutableField, isFieldUpdateAllowed, isTransactionUnsupported, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validateUpdateBody, validationChainPlugin, withTransaction };
@@ -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-BgdeBXI1.mjs";
1
+ import { A as HttpError, St as ValidationResult, T as FieldPreset, _ as CrudSchemas, bt as UserContext, s as CacheAdapter, st as SchemaBuilderOptions } from "./types-D57SZJeE.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-BgdeBXI1.mjs";
1
+ import { F as KeysetPaginationOptions, I as KeysetPaginationResult, K as PaginationConfig, U as OffsetPaginationOptions, W as OffsetPaginationResult, a as AnyDocument, n as AggregatePaginationOptions, r as AggregatePaginationResult } from "../types-D57SZJeE.mjs";
2
2
  import { Model } from "mongoose";
3
3
 
4
4
  //#region src/pagination/PaginationEngine.d.ts
@@ -1,2 +1,2 @@
1
- import { t as PaginationEngine } from "../PaginationEngine-CPmZv7Ds.mjs";
1
+ import { t as PaginationEngine } from "../PaginationEngine-C2_tAvAD.mjs";
2
2
  export { PaginationEngine };
@@ -1,2 +1,2 @@
1
- import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, S as MethodRegistryRepository, T as ElasticSearchOptions, U as AuditEntry, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "../validation-chain.plugin-Be9rG04B.mjs";
1
+ import { $ as aggregateHelpersPlugin, A as PrefixedIdOptions, B as BatchOperationsMethods, C as methodRegistryPlugin, D as CustomIdOptions, E as elasticSearchPlugin, F as prefixedId, G as AuditQueryOptions, H as batchOperationsPlugin, I as sequentialId, J as AuditTrailOptions, K as AuditQueryResult, L as cascadePlugin, M as customIdPlugin, N as dateSequentialId, O as DateSequentialIdOptions, P as getNextSequence, Q as AggregateHelpersMethods, R as CacheMethods, S as MethodRegistryRepository, T as ElasticSearchOptions, U as AuditEntry, W as AuditOperation, X as auditTrailPlugin, Y as AuditTrailQuery, Z as auditLogPlugin, _ as observabilityPlugin, a as uniqueField, b as MongoOperationsMethods, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as SequentialIdOptions, k as IdGenerator, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, q as AuditTrailMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MultiTenantOptions, w as fieldFilterPlugin, x as mongoOperationsPlugin, y as multiTenantPlugin, z as cachePlugin } from "../validation-chain.plugin-wGF34Ym-.mjs";
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, type TenantContext, type TenantStore, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, createTenantContext, 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 AuditTrailQuery, C as cachePlugin, M as auditLogPlugin, N as aggregateHelpersPlugin, S as cascadePlugin, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, x as sequentialId, y as getNextSequence } from "../validation-chain.plugin-CEyJssZl.mjs";
1
+ import { A as AuditTrailQuery, C as cachePlugin, M as auditLogPlugin, N as aggregateHelpersPlugin, S as cascadePlugin, _ as customIdPlugin, a as uniqueField, b as prefixedId, c as createTenantContext, d as observabilityPlugin, f as multiTenantPlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, j as auditTrailPlugin, k as batchOperationsPlugin, l as subdocumentPlugin, m as methodRegistryPlugin, n as blockIf, o as validationChainPlugin, p as mongoOperationsPlugin, r as immutableField, s as timestampPlugin, t as autoInject, u as softDeletePlugin, v as dateSequentialId, x as sequentialId, y as getNextSequence } from "../validation-chain.plugin-DwoOOWse.mjs";
2
2
  export { AuditTrailQuery, aggregateHelpersPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, fieldFilterPlugin, getNextSequence, immutableField, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
@@ -822,6 +822,31 @@ interface UpdateOptions extends OperationOptions {
822
822
  /** Array filters for positional operator $[<identifier>] updates */
823
823
  arrayFilters?: Record<string, unknown>[];
824
824
  }
825
+ /**
826
+ * Options for atomic findOneAndUpdate (compare-and-set primitive).
827
+ *
828
+ * Designed for outbox/lock/semaphore patterns that need a single round-trip
829
+ * match-and-mutate. Goes through the full hook pipeline (multi-tenant scope,
830
+ * soft-delete, audit) the same way `update()` does.
831
+ */
832
+ interface FindOneAndUpdateOptions extends OperationOptions {
833
+ /** Sort to disambiguate when filter matches multiple docs (e.g. FIFO claim). */
834
+ sort?: SortSpec;
835
+ /** Return doc state before or after the update. Default: 'after'. */
836
+ returnDocument?: 'before' | 'after';
837
+ /** Insert if no doc matches. Default: false. */
838
+ upsert?: boolean;
839
+ /** Array filters for positional `$[<identifier>]` updates. */
840
+ arrayFilters?: Record<string, unknown>[];
841
+ /** Allow aggregation-pipeline updates (array form). Default: false. */
842
+ updatePipeline?: boolean;
843
+ /** Run mongoose schema validators on the update. Default: true. */
844
+ runValidators?: boolean;
845
+ /** Collation for locale-aware string comparison. */
846
+ collation?: CollationOptions;
847
+ /** Maximum execution time in milliseconds. */
848
+ maxTimeMS?: number;
849
+ }
825
850
  /** Aggregate operation options */
826
851
  interface AggregateOptions extends ReadOptions {
827
852
  /** Allow aggregation to use disk for large sorts/groups */
@@ -1027,7 +1052,7 @@ interface RepositoryInstance {
1027
1052
  [key: string]: unknown;
1028
1053
  }
1029
1054
  /** Repository operation names */
1030
- type RepositoryOperation = 'create' | 'createMany' | 'update' | 'updateMany' | 'delete' | 'deleteMany' | 'restore' | 'getById' | 'getByQuery' | 'getOne' | 'getAll' | 'findAll' | 'getOrCreate' | 'count' | 'exists' | 'distinct' | 'aggregate' | 'aggregatePaginate' | 'lookupPopulate' | 'bulkWrite';
1055
+ type RepositoryOperation = 'create' | 'createMany' | 'update' | 'updateMany' | 'findOneAndUpdate' | 'delete' | 'deleteMany' | 'restore' | 'getById' | 'getByQuery' | 'getOne' | 'getAll' | 'findAll' | 'getOrCreate' | 'count' | 'exists' | 'distinct' | 'aggregate' | 'aggregatePaginate' | 'lookupPopulate' | 'bulkWrite';
1031
1056
  /** Event lifecycle phases */
1032
1057
  type EventPhase = 'before' | 'after' | 'error';
1033
1058
  /** Repository event names (generated from template literals) */
@@ -1173,7 +1198,7 @@ interface ValidatorDefinition {
1173
1198
  /** Validator name */
1174
1199
  name: string;
1175
1200
  /** Operations to apply validator to */
1176
- operations?: Array<'create' | 'createMany' | 'update' | 'delete'>;
1201
+ operations?: Array<'create' | 'createMany' | 'update' | 'findOneAndUpdate' | 'delete'>;
1177
1202
  /** Validation function */
1178
1203
  validate: (context: RepositoryContext, repo?: RepositoryInstance) => void | Promise<void>;
1179
1204
  }
@@ -1525,4 +1550,4 @@ type AllPluginMethods<TDoc> = {
1525
1550
  */
1526
1551
  type WithPlugins<TDoc, TRepo extends RepositoryInstance = RepositoryInstance> = TRepo & AllPluginMethods<TDoc>;
1527
1552
  //#endregion
1528
- export { ReadPreferenceType as $, InferDocument as A, LookupOptions as At, NonNullableFields as B, EventPayload as C, WithPlugins as Ct, GroupResult as D, IRequestContext as Dt, FieldRules as E, IControllerResponse as Et, KeysetPaginationResult as F, PaginationConfig as G, OffsetPaginationOptions as H, Logger as I, Plugin as J, PaginationResult as K, LookupPopulateOptions as L, JsonSchema as M, KeysOfType as N, HookMode as O, IResponseFormatter as Ot, KeysetPaginationOptions as P, ReadOptions as Q, LookupPopulateResult as R, EventHandlers as S, ValidatorDefinition as St, FieldPreset as T, IController as Tt, OffsetPaginationResult as U, ObjectId as V, OperationOptions as W, PluginType as X, PluginFunction as Y, PopulateSpec as Z, CrudSchemas as _, UpdateOptions as _t, AnyDocument as a, RequiredBy as at, DeleteResult as b, ValidationChainOptions as bt, CacheOperationOptions as c, SessionOptions as ct, CacheableOptions as d, SoftDeleteRepository as dt, RepositoryContext as et, CascadeOptions as f, SortDirection as ft, CreateOptions as g, UpdateManyResult as gt, CreateInput as h, UpdateInput as ht, AllPluginMethods as i, RepositoryOptions as it, InferRawDoc as j, HttpError as k, LookupBuilder as kt, CacheOptions as l, SoftDeleteFilterMode as lt, CollationOptions as m, Strict as mt, AggregatePaginationOptions as n, RepositoryInstance as nt, AnyModel as o, SchemaBuilderOptions as ot, CascadeRelation as p, SortSpec as pt, PartialBy as q, AggregatePaginationResult as r, RepositoryOperation as rt, CacheAdapter as s, SelectSpec as st, AggregateOptions as t, RepositoryEvent as tt, CacheStats as u, SoftDeleteOptions as ut, DecodedCursor as v, UpdateWithValidationResult as vt, EventPhase as w, WithTransactionOptions as wt, DocField as x, ValidationResult as xt, DeepPartial as y, UserContext as yt, MinMaxResult as z };
1553
+ export { ReadOptions as $, HttpError as A, LookupBuilder as At, MinMaxResult as B, EventPayload as C, ValidatorDefinition as Ct, FindOneAndUpdateOptions as D, IControllerResponse as Dt, FieldRules as E, IController as Et, KeysetPaginationOptions as F, OperationOptions as G, ObjectId as H, KeysetPaginationResult as I, PartialBy as J, PaginationConfig as K, Logger as L, InferRawDoc as M, JsonSchema as N, GroupResult as O, IRequestContext as Ot, KeysOfType as P, PopulateSpec as Q, LookupPopulateOptions as R, EventHandlers as S, ValidationResult as St, FieldPreset as T, WithTransactionOptions as Tt, OffsetPaginationOptions as U, NonNullableFields as V, OffsetPaginationResult as W, PluginFunction as X, Plugin as Y, PluginType as Z, CrudSchemas as _, UpdateManyResult as _t, AnyDocument as a, RepositoryOptions as at, DeleteResult as b, UserContext as bt, CacheOperationOptions as c, SelectSpec as ct, CacheableOptions as d, SoftDeleteOptions as dt, ReadPreferenceType as et, CascadeOptions as f, SoftDeleteRepository as ft, CreateOptions as g, UpdateInput as gt, CreateInput as h, Strict as ht, AllPluginMethods as i, RepositoryOperation as it, InferDocument as j, LookupOptions as jt, HookMode as k, IResponseFormatter as kt, CacheOptions as l, SessionOptions as lt, CollationOptions as m, SortSpec as mt, AggregatePaginationOptions as n, RepositoryEvent as nt, AnyModel as o, RequiredBy as ot, CascadeRelation as p, SortDirection as pt, PaginationResult as q, AggregatePaginationResult as r, RepositoryInstance as rt, CacheAdapter as s, SchemaBuilderOptions as st, AggregateOptions as t, RepositoryContext as tt, CacheStats as u, SoftDeleteFilterMode as ut, DecodedCursor as v, UpdateOptions as vt, EventPhase as w, WithPlugins as wt, DocField as x, ValidationChainOptions as xt, DeepPartial as y, UpdateWithValidationResult as yt, LookupPopulateResult as z };
@@ -729,6 +729,7 @@ async function exists(Model, query, options = {}) {
729
729
  //#endregion
730
730
  //#region src/actions/update.ts
731
731
  var update_exports = /* @__PURE__ */ __exportAll({
732
+ findOneAndUpdate: () => findOneAndUpdate,
732
733
  increment: () => increment,
733
734
  pullFromArray: () => pullFromArray,
734
735
  pushToArray: () => pushToArray,
@@ -861,6 +862,28 @@ async function updateByQuery(Model, query, data, options = {}) {
861
862
  return document;
862
863
  }
863
864
  /**
865
+ * Atomic findOneAndUpdate primitive.
866
+ *
867
+ * Returns the matched document (post-update by default) or null when no doc
868
+ * matches and `upsert` is false. Used by outbox relays, distributed locks,
869
+ * and workflow semaphores that need compare-and-set in a single round-trip.
870
+ */
871
+ async function findOneAndUpdate(Model, filter, update, options = {}) {
872
+ assertUpdatePipelineAllowed(update, options.updatePipeline);
873
+ const returnDocument = options.returnDocument ?? "after";
874
+ return await Model.findOneAndUpdate(filter, update, {
875
+ returnDocument,
876
+ upsert: options.upsert ?? false,
877
+ runValidators: options.runValidators ?? true,
878
+ session: options.session,
879
+ ...options.sort ? { sort: options.sort } : {},
880
+ ...options.arrayFilters ? { arrayFilters: options.arrayFilters } : {},
881
+ ...options.collation ? { collation: options.collation } : {},
882
+ ...options.maxTimeMS ? { maxTimeMS: options.maxTimeMS } : {},
883
+ ...options.updatePipeline !== void 0 ? { updatePipeline: options.updatePipeline } : {}
884
+ }).select(options.select || "").populate(parsePopulate(options.populate)).lean(options.lean ?? true);
885
+ }
886
+ /**
864
887
  * Increment field
865
888
  */
866
889
  async function increment(Model, id, field, value = 1, options = {}) {
@@ -879,4 +902,4 @@ async function pullFromArray(Model, id, field, value, options = {}) {
879
902
  return update(Model, id, { $pull: { [field]: value } }, options);
880
903
  }
881
904
  //#endregion
882
- export { aggregate_exports as _, exists as a, getOrCreate as c, deleteByQuery as d, delete_exports as f, upsert as g, create_exports as h, count as i, read_exports as l, createMany as m, updateByQuery as n, getById as o, create as p, update_exports as r, getByQuery as s, update as t, deleteById as u, distinct as v, LookupBuilder as y };
905
+ export { upsert as _, count as a, LookupBuilder as b, getByQuery as c, deleteById as d, deleteByQuery as f, create_exports as g, createMany as h, update_exports as i, getOrCreate as l, create as m, update as n, exists as o, delete_exports as p, updateByQuery as r, getById as s, findOneAndUpdate as t, read_exports as u, aggregate_exports as v, distinct as y };
@@ -1,5 +1,5 @@
1
- import { Z as PopulateSpec, pt as SortSpec, st as SelectSpec } from "../types-BgdeBXI1.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-Du-UopRK.mjs";
1
+ import { Q as PopulateSpec, ct as SelectSpec, mt as SortSpec } from "../types-D57SZJeE.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-BZaKOXXI.mjs";
3
3
 
4
4
  //#region src/utils/cache-keys.d.ts
5
5
  /**
@@ -1,12 +1,136 @@
1
1
  import { n as debug, r as warn } from "./logger-DJ7egMnd.mjs";
2
- import { a as exists, c as getOrCreate, d as deleteByQuery, g as upsert, i as count, m as createMany, n as updateByQuery, o as getById, p as create, s as getByQuery, t as update, u as deleteById, v as distinct, y as LookupBuilder } from "./update-BDsQ3cdy.mjs";
2
+ import { _ as upsert, a as count, b as LookupBuilder, c as getByQuery, d as deleteById, f as deleteByQuery, h as createMany, l as getOrCreate, m as create, n as update, o as exists, r as updateByQuery, s as getById, t as findOneAndUpdate, y as distinct } from "./update-yjSoNnWy.mjs";
3
3
  import { n as parseDuplicateKeyError, t as createError } from "./error-Lw9GEAro.mjs";
4
- import { t as PaginationEngine } from "./PaginationEngine-CPmZv7Ds.mjs";
4
+ import { t as PaginationEngine } from "./PaginationEngine-C2_tAvAD.mjs";
5
5
  import { hasNearOperator, rewriteNearForCount } from "./query/primitives/geo.mjs";
6
6
  import { r as isValidIdForType, t as getSchemaIdType } from "./id-resolution-e4BT9zAV.mjs";
7
7
  import { a as byIdKey, c as listQueryKey, l as modelPattern, o as byQueryKey, r as getFieldsForUser, u as versionKey } from "./field-selection-dI0m4RBs.mjs";
8
8
  import mongoose from "mongoose";
9
9
  import { AsyncLocalStorage } from "node:async_hooks";
10
+ //#region src/operations.ts
11
+ /**
12
+ * The registry. Order intentionally groups by category for readability.
13
+ * Adding a new RepositoryOperation requires exactly one entry here, after
14
+ * which every bundled plugin auto-classifies it correctly.
15
+ */
16
+ const OP_REGISTRY = {
17
+ create: {
18
+ policyKey: "data",
19
+ mutates: true,
20
+ hasIdContext: false
21
+ },
22
+ update: {
23
+ policyKey: "query",
24
+ mutates: true,
25
+ hasIdContext: true
26
+ },
27
+ findOneAndUpdate: {
28
+ policyKey: "query",
29
+ mutates: true,
30
+ hasIdContext: false
31
+ },
32
+ delete: {
33
+ policyKey: "query",
34
+ mutates: true,
35
+ hasIdContext: true
36
+ },
37
+ restore: {
38
+ policyKey: "query",
39
+ mutates: true,
40
+ hasIdContext: true
41
+ },
42
+ createMany: {
43
+ policyKey: "dataArray",
44
+ mutates: true,
45
+ hasIdContext: false
46
+ },
47
+ updateMany: {
48
+ policyKey: "query",
49
+ mutates: true,
50
+ hasIdContext: false
51
+ },
52
+ deleteMany: {
53
+ policyKey: "query",
54
+ mutates: true,
55
+ hasIdContext: false
56
+ },
57
+ bulkWrite: {
58
+ policyKey: "operations",
59
+ mutates: true,
60
+ hasIdContext: false
61
+ },
62
+ getById: {
63
+ policyKey: "query",
64
+ mutates: false,
65
+ hasIdContext: true
66
+ },
67
+ getByQuery: {
68
+ policyKey: "query",
69
+ mutates: false,
70
+ hasIdContext: false
71
+ },
72
+ getOne: {
73
+ policyKey: "query",
74
+ mutates: false,
75
+ hasIdContext: false
76
+ },
77
+ findAll: {
78
+ policyKey: "query",
79
+ mutates: false,
80
+ hasIdContext: false
81
+ },
82
+ getOrCreate: {
83
+ policyKey: "query",
84
+ mutates: false,
85
+ hasIdContext: false
86
+ },
87
+ count: {
88
+ policyKey: "query",
89
+ mutates: false,
90
+ hasIdContext: false
91
+ },
92
+ exists: {
93
+ policyKey: "query",
94
+ mutates: false,
95
+ hasIdContext: false
96
+ },
97
+ distinct: {
98
+ policyKey: "query",
99
+ mutates: false,
100
+ hasIdContext: false
101
+ },
102
+ aggregate: {
103
+ policyKey: "query",
104
+ mutates: false,
105
+ hasIdContext: false
106
+ },
107
+ getAll: {
108
+ policyKey: "filters",
109
+ mutates: false,
110
+ hasIdContext: false
111
+ },
112
+ aggregatePaginate: {
113
+ policyKey: "filters",
114
+ mutates: false,
115
+ hasIdContext: false
116
+ },
117
+ lookupPopulate: {
118
+ policyKey: "filters",
119
+ mutates: false,
120
+ hasIdContext: false
121
+ }
122
+ };
123
+ /** All known repository operations, in registry order. */
124
+ const ALL_OPERATIONS = Object.keys(OP_REGISTRY);
125
+ /** Operations that mutate the database. Drives audit + cache invalidation lists. */
126
+ const MUTATING_OPERATIONS = ALL_OPERATIONS.filter((op) => OP_REGISTRY[op].mutates);
127
+ /** Operations that don't mutate. Drives default cacheable ops. */
128
+ const READ_OPERATIONS = ALL_OPERATIONS.filter((op) => !OP_REGISTRY[op].mutates);
129
+ /** Filter ops by their policy injection key. */
130
+ function operationsByPolicyKey(key) {
131
+ return ALL_OPERATIONS.filter((op) => OP_REGISTRY[op].policyKey === key);
132
+ }
133
+ //#endregion
10
134
  //#region src/plugins/aggregate-helpers.plugin.ts
11
135
  /**
12
136
  * Aggregate helpers plugin
@@ -99,6 +223,22 @@ function auditLogPlugin(logger) {
99
223
  organizationId: context.organizationId
100
224
  });
101
225
  });
226
+ repo.on("after:findOneAndUpdate", ({ context, result }) => {
227
+ if (!result) return;
228
+ logger?.info?.("Document upserted/updated (findOneAndUpdate)", {
229
+ model: context.model || repo.model,
230
+ id: result?.[repo.idField || "_id"],
231
+ userId: context.user?._id || context.user?.id,
232
+ organizationId: context.organizationId
233
+ });
234
+ });
235
+ repo.on("error:findOneAndUpdate", ({ context, error }) => {
236
+ logger?.error?.("findOneAndUpdate failed", {
237
+ model: context.model || repo.model,
238
+ error: error.message,
239
+ userId: context.user?._id || context.user?.id
240
+ });
241
+ });
102
242
  repo.on("after:delete", ({ context }) => {
103
243
  logger?.info?.("Document deleted", {
104
244
  model: context.model || repo.model,
@@ -178,6 +318,7 @@ function getAuditModel(collectionName, ttlDays) {
178
318
  enum: [
179
319
  "create",
180
320
  "update",
321
+ "findOneAndUpdate",
181
322
  "delete"
182
323
  ]
183
324
  },
@@ -327,6 +468,19 @@ function auditTrailPlugin(options = {}) {
327
468
  });
328
469
  });
329
470
  }
471
+ if (opsSet.has("findOneAndUpdate")) repo.on("after:findOneAndUpdate", ({ context, result }) => {
472
+ if (!result) return;
473
+ const doc = result;
474
+ const idKey = repo.idField || "_id";
475
+ writeAudit(AuditModel, {
476
+ model: context.model || repo.model,
477
+ operation: "findOneAndUpdate",
478
+ documentId: doc?.[idKey],
479
+ userId: getUserId(context),
480
+ orgId: context.organizationId,
481
+ metadata: metadata?.(context)
482
+ });
483
+ });
330
484
  if (opsSet.has("delete")) repo.on("after:delete", ({ context }) => {
331
485
  writeAudit(AuditModel, {
332
486
  model: context.model || repo.model,
@@ -1576,10 +1730,10 @@ var Repository = class {
1576
1730
  */
1577
1731
  async findAll(filters = {}, options = {}) {
1578
1732
  const context = await this._buildContext("findAll", {
1579
- filters,
1733
+ query: filters,
1580
1734
  ...options
1581
1735
  });
1582
- const resolvedFilters = context.filters ?? filters;
1736
+ const resolvedFilters = context.query ?? filters;
1583
1737
  try {
1584
1738
  const query = this.Model.find(resolvedFilters);
1585
1739
  const sortSpec = context.sort || options.sort;
@@ -1872,6 +2026,64 @@ var Repository = class {
1872
2026
  }
1873
2027
  }
1874
2028
  /**
2029
+ * Atomic findOneAndUpdate (compare-and-set primitive).
2030
+ *
2031
+ * Single round-trip match-and-mutate for outbox relays, distributed locks,
2032
+ * and workflow semaphores. Goes through the full hook pipeline so
2033
+ * multi-tenant scope, soft-delete, audit, and cache plugins all apply.
2034
+ *
2035
+ * Returns the matched document (post-update by default) or `null` when no
2036
+ * document matches and `upsert` is false.
2037
+ *
2038
+ * @example FIFO claim-lease for an outbox worker
2039
+ * ```ts
2040
+ * const claimed = await outboxRepo.findOneAndUpdate(
2041
+ * { status: 'pending', leaseExpiresAt: { $lt: new Date() } },
2042
+ * { $set: { status: 'processing', leaseExpiresAt: leaseUntil, leasedBy: workerId } },
2043
+ * { sort: { createdAt: 1 }, returnDocument: 'after' },
2044
+ * );
2045
+ * if (!claimed) return; // queue empty
2046
+ * ```
2047
+ *
2048
+ * @example CAS upsert
2049
+ * ```ts
2050
+ * const lock = await locksRepo.findOneAndUpdate(
2051
+ * { _id: lockKey, ownerId: { $in: [null, workerId] } },
2052
+ * { $set: { ownerId: workerId, expiresAt: leaseUntil } },
2053
+ * { upsert: true },
2054
+ * );
2055
+ * ```
2056
+ */
2057
+ async findOneAndUpdate(filter, update, options = {}) {
2058
+ const context = await this._buildContext("findOneAndUpdate", {
2059
+ query: filter,
2060
+ data: update,
2061
+ ...options
2062
+ });
2063
+ try {
2064
+ const finalQuery = context.query || filter;
2065
+ const finalUpdate = context.data || update;
2066
+ const result = await findOneAndUpdate(this.Model, finalQuery, finalUpdate, {
2067
+ ...options,
2068
+ sort: options.sort,
2069
+ returnDocument: options.returnDocument ?? "after",
2070
+ upsert: options.upsert ?? false,
2071
+ session: options.session
2072
+ });
2073
+ await this._emitHook("after:findOneAndUpdate", {
2074
+ context,
2075
+ result
2076
+ });
2077
+ return result;
2078
+ } catch (error) {
2079
+ await this._emitErrorHook("error:findOneAndUpdate", {
2080
+ context,
2081
+ error
2082
+ });
2083
+ throw this._handleError(error);
2084
+ }
2085
+ }
2086
+ /**
1875
2087
  * Delete a document by ID.
1876
2088
  *
1877
2089
  * By default the behavior is decided by the plugin stack:
@@ -2100,9 +2312,9 @@ var Repository = class {
2100
2312
  };
2101
2313
  if (isKeyset && sort) {
2102
2314
  const parsedSort = this._parseSort(sort);
2103
- const { validateKeysetSort } = await import("./sort-ffe-9JBg.mjs").then((n) => n.n);
2104
- const { encodeCursor, resolveCursorFilter } = await import("./cursor-DZ9JSoMM.mjs").then((n) => n.t);
2105
- const { getPrimaryField } = await import("./sort-ffe-9JBg.mjs").then((n) => n.n);
2315
+ const { validateKeysetSort } = await import("./sort-CrZjTK25.mjs").then((n) => n.n);
2316
+ const { encodeCursor, resolveCursorFilter } = await import("./cursor-J84rR6wG.mjs").then((n) => n.t);
2317
+ const { getPrimaryField } = await import("./sort-CrZjTK25.mjs").then((n) => n.n);
2106
2318
  const normalizedSort = validateKeysetSort(parsedSort, this._pagination.config.strictKeysetSortFields);
2107
2319
  const cursorVersion = this._pagination.config.cursorVersion ?? 1;
2108
2320
  const minCursorVersion = this._pagination.config.minCursorVersion ?? 1;
@@ -2823,6 +3035,22 @@ function cachePlugin(options) {
2823
3035
  await bumpVersion();
2824
3036
  });
2825
3037
  /**
3038
+ * after:findOneAndUpdate — atomic CAS may insert (upsert) or mutate.
3039
+ * Result carries the matched/inserted doc; invalidate that ID and bump
3040
+ * version. Null result (no match, no upsert) is a no-op.
3041
+ */
3042
+ repo.on("after:findOneAndUpdate", async (payload) => {
3043
+ const { result } = payload;
3044
+ if (!result) return;
3045
+ const idKey = repo.idField || "_id";
3046
+ const id = result?.[idKey];
3047
+ if (id == null) {
3048
+ await bumpVersion();
3049
+ return;
3050
+ }
3051
+ await Promise.all([invalidateById(String(id)), bumpVersion()]);
3052
+ });
3053
+ /**
2826
3054
  * after:delete - Invalidate by ID and bump version
2827
3055
  */
2828
3056
  repo.on("after:delete", async (payload) => {
@@ -3781,45 +4009,20 @@ function mongoOperationsPlugin() {
3781
4009
  */
3782
4010
  function multiTenantPlugin(options = {}) {
3783
4011
  const { tenantField = "organizationId", contextKey = "organizationId", required = true, skipOperations = [], skipWhen, resolveContext, fieldType = "string" } = options;
3784
- const filterOps = [
3785
- "getAll",
3786
- "findAll",
3787
- "aggregatePaginate",
3788
- "lookupPopulate"
3789
- ];
3790
- const queryReadOps = [
3791
- "getById",
3792
- "getByQuery",
3793
- "getOne",
3794
- "count",
3795
- "exists",
3796
- "getOrCreate",
3797
- "distinct",
3798
- "aggregate"
3799
- ];
3800
- const constrainedWriteOps = [
3801
- "update",
3802
- "delete",
3803
- "restore"
3804
- ];
3805
- const filterReadOps = ["getDeleted"];
3806
- const createOps = ["create", "createMany"];
3807
- const batchQueryOps = ["updateMany", "deleteMany"];
3808
- const bulkOps = ["bulkWrite"];
3809
- const allOps = [
3810
- ...filterOps,
3811
- ...filterReadOps,
3812
- ...queryReadOps,
3813
- ...constrainedWriteOps,
3814
- ...createOps,
3815
- ...batchQueryOps,
3816
- ...bulkOps
3817
- ];
4012
+ const extraOps = [{
4013
+ op: "getDeleted",
4014
+ policyKey: "filters"
4015
+ }];
3818
4016
  return {
3819
4017
  name: "multi-tenant",
3820
4018
  apply(repo) {
3821
- for (const op of allOps) {
4019
+ const allOps = [...ALL_OPERATIONS.map((op) => ({
4020
+ op,
4021
+ policyKey: OP_REGISTRY[op].policyKey
4022
+ })), ...extraOps];
4023
+ for (const { op, policyKey } of allOps) {
3822
4024
  if (skipOperations.includes(op)) continue;
4025
+ if (policyKey === "none") continue;
3823
4026
  repo.on(`before:${op}`, (context) => {
3824
4027
  if (skipWhen?.(context, op)) return;
3825
4028
  let tenantId = context[contextKey];
@@ -3830,45 +4033,49 @@ function multiTenantPlugin(options = {}) {
3830
4033
  if (!tenantId && required) throw new Error(`[mongokit] Multi-tenant: Missing '${contextKey}' in context for '${op}'. Pass it via options or set required: false.`);
3831
4034
  if (!tenantId) return;
3832
4035
  const castId = fieldType === "objectId" ? new mongoose.Types.ObjectId(tenantId) : tenantId;
3833
- if (filterOps.includes(op) || filterReadOps.includes(op)) context.filters = {
3834
- ...context.filters,
3835
- [tenantField]: castId
3836
- };
3837
- if (queryReadOps.includes(op)) context.query = {
3838
- ...context.query,
3839
- [tenantField]: castId
3840
- };
3841
- if (op === "create" && context.data) context.data[tenantField] = castId;
3842
- if (op === "createMany" && context.dataArray) {
3843
- for (const doc of context.dataArray) if (doc && typeof doc === "object") doc[tenantField] = castId;
3844
- }
3845
- if (constrainedWriteOps.includes(op)) context.query = {
3846
- ...context.query,
3847
- [tenantField]: castId
3848
- };
3849
- if (batchQueryOps.includes(op)) context.query = {
3850
- ...context.query,
3851
- [tenantField]: castId
3852
- };
3853
- if (op === "bulkWrite" && context.operations) {
3854
- const ops = context.operations;
3855
- for (const subOp of ops) {
3856
- for (const key of [
3857
- "updateOne",
3858
- "updateMany",
3859
- "deleteOne",
3860
- "deleteMany",
3861
- "replaceOne"
3862
- ]) {
3863
- const opBody = subOp[key];
3864
- if (opBody?.filter) opBody.filter = {
3865
- ...opBody.filter,
3866
- [tenantField]: castId
3867
- };
4036
+ switch (policyKey) {
4037
+ case "filters":
4038
+ context.filters = {
4039
+ ...context.filters,
4040
+ [tenantField]: castId
4041
+ };
4042
+ break;
4043
+ case "query":
4044
+ context.query = {
4045
+ ...context.query,
4046
+ [tenantField]: castId
4047
+ };
4048
+ break;
4049
+ case "data":
4050
+ if (context.data) context.data[tenantField] = castId;
4051
+ break;
4052
+ case "dataArray":
4053
+ if (context.dataArray) {
4054
+ for (const doc of context.dataArray) if (doc && typeof doc === "object") doc[tenantField] = castId;
3868
4055
  }
3869
- const insertBody = subOp.insertOne;
3870
- if (insertBody?.document) insertBody.document[tenantField] = castId;
3871
- }
4056
+ break;
4057
+ case "operations":
4058
+ if (context.operations) {
4059
+ const ops = context.operations;
4060
+ for (const subOp of ops) {
4061
+ for (const key of [
4062
+ "updateOne",
4063
+ "updateMany",
4064
+ "deleteOne",
4065
+ "deleteMany",
4066
+ "replaceOne"
4067
+ ]) {
4068
+ const opBody = subOp[key];
4069
+ if (opBody?.filter) opBody.filter = {
4070
+ ...opBody.filter,
4071
+ [tenantField]: castId
4072
+ };
4073
+ }
4074
+ const insertBody = subOp.insertOne;
4075
+ if (insertBody?.document) insertBody.document[tenantField] = castId;
4076
+ }
4077
+ }
4078
+ break;
3872
4079
  }
3873
4080
  }, { priority: HOOK_PRIORITY.POLICY });
3874
4081
  }
@@ -3877,24 +4084,26 @@ function multiTenantPlugin(options = {}) {
3877
4084
  }
3878
4085
  //#endregion
3879
4086
  //#region src/plugins/observability.plugin.ts
3880
- const DEFAULT_OPS = [
3881
- "create",
3882
- "createMany",
3883
- "update",
3884
- "delete",
3885
- "getById",
3886
- "getByQuery",
3887
- "getOne",
3888
- "getAll",
3889
- "findAll",
3890
- "getOrCreate",
3891
- "count",
3892
- "exists",
3893
- "distinct",
3894
- "aggregate",
3895
- "aggregatePaginate",
3896
- "lookupPopulate"
3897
- ];
4087
+ /**
4088
+ * Observability Plugin
4089
+ *
4090
+ * Adds operation timing, structured metrics, and APM hook points.
4091
+ * Works with any monitoring system — just provide an onMetric callback.
4092
+ *
4093
+ * @example
4094
+ * ```typescript
4095
+ * const repo = new Repository(User, [
4096
+ * observabilityPlugin({
4097
+ * onMetric: (metric) => {
4098
+ * // Send to DataDog, New Relic, OpenTelemetry, or console
4099
+ * console.log(`${metric.operation} took ${metric.durationMs}ms`);
4100
+ * statsd.histogram('mongokit.operation', metric.durationMs, { op: metric.operation });
4101
+ * },
4102
+ * }),
4103
+ * ]);
4104
+ * ```
4105
+ */
4106
+ const DEFAULT_OPS = ALL_OPERATIONS;
3898
4107
  const timers = /* @__PURE__ */ new WeakMap();
3899
4108
  function observabilityPlugin(options) {
3900
4109
  const { onMetric, slowThresholdMs } = options;
@@ -4065,22 +4274,19 @@ function softDeletePlugin(options = {}) {
4065
4274
  };
4066
4275
  }
4067
4276
  };
4068
- for (const op of [
4069
- "getAll",
4070
- "findAll",
4071
- "aggregatePaginate",
4072
- "lookupPopulate"
4073
- ]) repo.on(`before:${op}`, injectDeleteFilterToFilters, { priority: HOOK_PRIORITY.POLICY });
4074
- for (const op of [
4075
- "getById",
4076
- "getOne",
4077
- "getByQuery",
4078
- "count",
4079
- "exists",
4080
- "getOrCreate",
4081
- "distinct",
4082
- "aggregate"
4083
- ]) repo.on(`before:${op}`, injectDeleteFilterToQuery, { priority: HOOK_PRIORITY.POLICY });
4277
+ const SOFT_DELETE_SPECIAL = new Set([
4278
+ "updateMany",
4279
+ "deleteMany",
4280
+ "bulkWrite"
4281
+ ]);
4282
+ for (const op of ALL_OPERATIONS) {
4283
+ if (SOFT_DELETE_SPECIAL.has(op)) continue;
4284
+ const desc = OP_REGISTRY[op];
4285
+ const isFilterableMutation = op === "findOneAndUpdate";
4286
+ if (desc.mutates && !isFilterableMutation) continue;
4287
+ if (desc.policyKey === "filters") repo.on(`before:${op}`, injectDeleteFilterToFilters, { priority: HOOK_PRIORITY.POLICY });
4288
+ else if (desc.policyKey === "query") repo.on(`before:${op}`, injectDeleteFilterToQuery, { priority: HOOK_PRIORITY.POLICY });
4289
+ }
4084
4290
  repo.on("before:updateMany", (context) => {
4085
4291
  if (options.soft !== false) {
4086
4292
  const deleteFilter = buildDeletedFilter(deletedField, filterMode, !!context.includeDeleted);
@@ -4102,24 +4308,6 @@ function softDeletePlugin(options = {}) {
4102
4308
  context.softDeleted = true;
4103
4309
  }
4104
4310
  }, { priority: HOOK_PRIORITY.POLICY });
4105
- repo.on("before:aggregate", (context) => {
4106
- if (options.soft !== false) {
4107
- const deleteFilter = buildDeletedFilter(deletedField, filterMode, !!context.includeDeleted);
4108
- if (Object.keys(deleteFilter).length > 0) context.query = {
4109
- ...context.query || {},
4110
- ...deleteFilter
4111
- };
4112
- }
4113
- }, { priority: HOOK_PRIORITY.POLICY });
4114
- repo.on("before:aggregatePaginate", (context) => {
4115
- if (options.soft !== false) {
4116
- const deleteFilter = buildDeletedFilter(deletedField, filterMode, !!context.includeDeleted);
4117
- if (Object.keys(deleteFilter).length > 0) context.filters = {
4118
- ...context.filters || {},
4119
- ...deleteFilter
4120
- };
4121
- }
4122
- }, { priority: HOOK_PRIORITY.POLICY });
4123
4311
  if (addRestoreMethod) {
4124
4312
  const restoreMethod = async function(id, restoreOptions = {}) {
4125
4313
  const context = await this._buildContext("restore", {
@@ -4348,6 +4536,20 @@ function timestampPlugin() {
4348
4536
  if (!context.data) return;
4349
4537
  context.data.updatedAt = /* @__PURE__ */ new Date();
4350
4538
  });
4539
+ repo.on("before:findOneAndUpdate", (context) => {
4540
+ if (!context.data) return;
4541
+ const now = /* @__PURE__ */ new Date();
4542
+ if (Array.isArray(context.data)) return;
4543
+ const update = context.data;
4544
+ if (Object.keys(update).some((k) => k.startsWith("$"))) {
4545
+ const set = update.$set || {};
4546
+ set.updatedAt = now;
4547
+ update.$set = set;
4548
+ const setOnInsert = update.$setOnInsert || {};
4549
+ if (!("createdAt" in setOnInsert)) setOnInsert.createdAt = now;
4550
+ update.$setOnInsert = setOnInsert;
4551
+ } else update.updatedAt = now;
4552
+ });
4351
4553
  }
4352
4554
  };
4353
4555
  }
@@ -4374,6 +4576,7 @@ function validationChainPlugin(validators = [], options = {}) {
4374
4576
  const validatorsByOperation = {
4375
4577
  create: [],
4376
4578
  update: [],
4579
+ findOneAndUpdate: [],
4377
4580
  delete: [],
4378
4581
  createMany: []
4379
4582
  };
@@ -4412,6 +4615,7 @@ function validationChainPlugin(validators = [], options = {}) {
4412
4615
  repo.on("before:create", async (context) => runValidators("create", context));
4413
4616
  repo.on("before:createMany", async (context) => runValidators("createMany", context));
4414
4617
  repo.on("before:update", async (context) => runValidators("update", context));
4618
+ repo.on("before:findOneAndUpdate", async (context) => runValidators("findOneAndUpdate", context));
4415
4619
  repo.on("before:delete", async (context) => runValidators("delete", context));
4416
4620
  }
4417
4621
  };
@@ -4500,4 +4704,4 @@ function uniqueField(field, errorMessage) {
4500
4704
  };
4501
4705
  }
4502
4706
  //#endregion
4503
- export { AuditTrailQuery as A, cachePlugin as C, withTransaction as D, isTransactionUnsupported as E, auditLogPlugin as M, aggregateHelpersPlugin as N, AggregationBuilder as O, cascadePlugin as S, Repository as T, customIdPlugin as _, uniqueField as a, prefixedId as b, createTenantContext as c, observabilityPlugin as d, multiTenantPlugin as f, elasticSearchPlugin as g, fieldFilterPlugin as h, requireField as i, auditTrailPlugin as j, batchOperationsPlugin as k, subdocumentPlugin as l, methodRegistryPlugin as m, blockIf as n, validationChainPlugin as o, mongoOperationsPlugin as p, immutableField as r, timestampPlugin as s, autoInject as t, softDeletePlugin as u, dateSequentialId as v, HOOK_PRIORITY as w, sequentialId as x, getNextSequence as y };
4707
+ export { AuditTrailQuery as A, cachePlugin as C, withTransaction as D, isTransactionUnsupported as E, MUTATING_OPERATIONS as F, OP_REGISTRY as I, READ_OPERATIONS as L, auditLogPlugin as M, aggregateHelpersPlugin as N, AggregationBuilder as O, ALL_OPERATIONS as P, operationsByPolicyKey as R, cascadePlugin as S, Repository as T, customIdPlugin as _, uniqueField as a, prefixedId as b, createTenantContext as c, observabilityPlugin as d, multiTenantPlugin as f, elasticSearchPlugin as g, fieldFilterPlugin as h, requireField as i, auditTrailPlugin as j, batchOperationsPlugin as k, subdocumentPlugin as l, methodRegistryPlugin as m, blockIf as n, validationChainPlugin as o, mongoOperationsPlugin as p, immutableField as r, timestampPlugin as s, autoInject as t, softDeletePlugin as u, dateSequentialId as v, HOOK_PRIORITY as w, sequentialId as x, getNextSequence as y };
@@ -1,4 +1,4 @@
1
- import { I as Logger, J as Plugin, St as ValidatorDefinition, T as FieldPreset, U as OffsetPaginationResult, V as ObjectId, Z as PopulateSpec, bt as ValidationChainOptions, et as RepositoryContext, f as CascadeOptions, l as CacheOptions, nt as RepositoryInstance, pt as SortSpec, st as SelectSpec, u as CacheStats, ut as SoftDeleteOptions } from "./types-BgdeBXI1.mjs";
1
+ import { Ct as ValidatorDefinition, H as ObjectId, L as Logger, Q as PopulateSpec, T as FieldPreset, W as OffsetPaginationResult, Y as Plugin, ct as SelectSpec, dt as SoftDeleteOptions, f as CascadeOptions, l as CacheOptions, mt as SortSpec, rt as RepositoryInstance, tt as RepositoryContext, u as CacheStats, xt as ValidationChainOptions } from "./types-D57SZJeE.mjs";
2
2
  import mongoose, { ClientSession } from "mongoose";
3
3
  import { AsyncLocalStorage } from "node:async_hooks";
4
4
 
@@ -119,7 +119,7 @@ interface AuditTrailOptions {
119
119
  */
120
120
  excludeFields?: string[];
121
121
  }
122
- type AuditOperation = 'create' | 'update' | 'delete';
122
+ type AuditOperation = 'create' | 'update' | 'delete' | 'findOneAndUpdate';
123
123
  interface AuditEntry {
124
124
  model: string;
125
125
  operation: AuditOperation;
@@ -1095,7 +1095,7 @@ declare function createTenantContext(): TenantContext;
1095
1095
  declare function timestampPlugin(): Plugin;
1096
1096
  //#endregion
1097
1097
  //#region src/plugins/validation-chain.plugin.d.ts
1098
- type OperationType = 'create' | 'createMany' | 'update' | 'delete';
1098
+ type OperationType = 'create' | 'createMany' | 'update' | 'findOneAndUpdate' | 'delete';
1099
1099
  /**
1100
1100
  * Validation chain plugin
1101
1101
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/mongokit",
3
- "version": "3.7.0",
3
+ "version": "3.8.0",
4
4
  "description": "Production-grade MongoDB repositories with zero dependencies - smart pagination, events, and plugins",
5
5
  "type": "module",
6
6
  "sideEffects": false,
File without changes