@classytic/mongokit 3.32.0 → 3.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/{PaginationEngine-CSh-P8Nh.mjs → PaginationEngine-GdyX29tE.mjs} +13 -11
  2. package/dist/{Repository-B_1y9cCf.mjs → Repository-BArvU32x.mjs} +149 -36
  3. package/dist/{Repository-BAzymuOz.d.mts → Repository-BdSkQ8vc.d.mts} +27 -4
  4. package/dist/actions/index.d.mts +1 -1
  5. package/dist/actions/index.mjs +1 -1
  6. package/dist/ai/index.d.mts +1 -1
  7. package/dist/better-auth/index.d.mts +79 -1
  8. package/dist/better-auth/index.mjs +59 -6
  9. package/dist/{create-repository-mEdfH9sz.d.mts → create-repository-BU4x9Kb0.d.mts} +4 -4
  10. package/dist/{create-repository-a3DQ5tzt.mjs → create-repository-CPNbktFG.mjs} +2 -2
  11. package/dist/id-chunks-BmAwsqNk.mjs +67 -0
  12. package/dist/{index-Beyr1r4d.d.mts → index-DKEql4cL.d.mts} +1 -1
  13. package/dist/index.d.mts +15 -14
  14. package/dist/index.mjs +60 -54
  15. package/dist/{multi-tenant.plugin-2VtSSm1Y.d.mts → multi-tenant.plugin-BwSvTirB.d.mts} +3 -3
  16. package/dist/{operations-CuzX9m2Z.d.mts → operations-DZodvjC-.d.mts} +22 -3
  17. package/dist/outbox/index.d.mts +1 -1
  18. package/dist/outbox/index.mjs +1 -1
  19. package/dist/pagination/PaginationEngine.mjs +1 -1
  20. package/dist/{plugin-options-BsFH0sTi.d.mts → plugin-options-DXnlKM3B.d.mts} +31 -1
  21. package/dist/plugins/index.d.mts +3 -3
  22. package/dist/plugins/index.mjs +2 -2
  23. package/dist/{repository-Q16ErTu8.d.mts → repository-H_ffGgpE.d.mts} +8 -0
  24. package/dist/testkit/index.d.mts +2 -2
  25. package/dist/testkit/index.mjs +1 -1
  26. package/dist/{timestamp.plugin-CAXzHs9y.mjs → timestamp.plugin-BEc6TANm.mjs} +7 -5
  27. package/dist/{to-plain-BuX1RUZV.d.mts → to-plain-Dyz_qvuo.d.mts} +3 -3
  28. package/dist/{update-w5i-pa_c.mjs → update-jSXp3nu3.mjs} +48 -28
  29. package/dist/utils/index.d.mts +54 -2
  30. package/dist/utils/index.mjs +2 -1
  31. package/dist/{validation-chain.plugin-BihbMQRq.mjs → validation-chain.plugin-CyK1s9Ze.mjs} +219 -20
  32. package/dist/{validation-chain.plugin-BSshLJTA.d.mts → validation-chain.plugin-yQRob3Av.d.mts} +2 -2
  33. package/package.json +17 -17
@@ -1,5 +1,6 @@
1
- import { n as Repository } from "../Repository-B_1y9cCf.mjs";
1
+ import { n as Repository } from "../Repository-BArvU32x.mjs";
2
2
  import { n as createMongooseAdapter } from "../adapter-BitHg-CN.mjs";
3
+ import { asReadOnlyRepo } from "@classytic/repo-core/repository";
3
4
  import { pluralizeBetterAuthCollection, resolveBetterAuthCollections } from "@classytic/repo-core/better-auth";
4
5
  //#region src/better-auth/index.ts
5
6
  /**
@@ -39,6 +40,10 @@ function missingIndexesOn(existing, declared) {
39
40
  const existingIndexes = (existing?.schema)?.indexes?.() ?? [];
40
41
  return declared.filter((d) => !existingIndexes.some(([fields, options]) => indexFieldsKey(fields) === indexFieldsKey(d.fields) && declaredOptionsSatisfied(d.options ?? {}, options ?? {})));
41
42
  }
43
+ /** The `_id` path for a stub/overlay schema, or `{}` to inherit mongoose's ObjectId default. */
44
+ function idPath(idType) {
45
+ return idType === "string" ? { _id: { type: String } } : {};
46
+ }
42
47
  /**
43
48
  * Register stub Mongoose models for Better Auth's collections so that
44
49
  * Mongoose-based resources can `.populate()` references to BA-owned
@@ -67,7 +72,7 @@ function registerBetterAuthStubs(mongoose, options = {}) {
67
72
  const registered = [];
68
73
  for (const finalName of names) {
69
74
  if (finalName in mongoose.models) continue;
70
- const schema = new mongoose.Schema({}, {
75
+ const schema = new mongoose.Schema(idPath(options.idType), {
71
76
  strict: false,
72
77
  collection: finalName,
73
78
  timestamps: false
@@ -95,7 +100,7 @@ function registerBetterAuthStubs(mongoose, options = {}) {
95
100
  * would silently drop this call's `additionalFields` / `indexes`).
96
101
  */
97
102
  async function createBetterAuthOverlay(options) {
98
- const { auth, mongoose, collection, additionalFields = {}, indexes = [], usePlural = false, RepositoryClass, schemaGenerator } = options;
103
+ const { auth, mongoose, collection, additionalFields = {}, indexes = [], usePlural = false, RepositoryClass, schemaGenerator, unsafeWritable = false } = options;
99
104
  const ctx = await auth.$context;
100
105
  const tableConfig = ctx.tables[collection];
101
106
  if (!tableConfig) throw new Error(`[mongokit:better-auth] Better Auth has no table named '${collection}'. Available: ${Object.keys(ctx.tables).join(", ")}. Did you enable the right plugin in your betterAuth() config?`);
@@ -134,7 +139,10 @@ async function createBetterAuthOverlay(options) {
134
139
  }
135
140
  let Model = mongoose.models[finalName];
136
141
  if (!Model) {
137
- const schema = new mongoose.Schema(additionalFields, {
142
+ const schema = new mongoose.Schema({
143
+ ...idPath(options.idType),
144
+ ...additionalFields
145
+ }, {
138
146
  strict: false,
139
147
  collection: finalName,
140
148
  timestamps: false
@@ -142,7 +150,8 @@ async function createBetterAuthOverlay(options) {
142
150
  for (const idx of indexes) schema.index(idx.fields, idx.options);
143
151
  Model = mongoose.model(finalName, schema);
144
152
  }
145
- const repository = new (RepositoryClass ?? Repository)(Model);
153
+ const built = new (RepositoryClass ?? Repository)(Model);
154
+ const repository = unsafeWritable ? built : asReadOnlyRepo(built, { reason: `Better Auth owns writes to '${finalName}' (hashing, session revocation, org cascades, plugin hooks) — mutate via auth.api, not generic CRUD. Pass \`unsafeWritable: true\` to this overlay if raw administrative writes are required` });
146
155
  return createMongooseAdapter({
147
156
  model: Model,
148
157
  repository,
@@ -150,6 +159,50 @@ async function createBetterAuthOverlay(options) {
150
159
  });
151
160
  }
152
161
  /**
162
+ * Minimal structural surface for the one write we need. Satisfied by BOTH a
163
+ * native MongoDB `Collection` (`db.collection('session')`) and a Mongoose
164
+ * `Model` — so the host passes whichever it has, and mongokit keeps mongoose +
165
+ * the mongodb driver as peer deps with zero runtime import.
166
+ */
167
+ /**
168
+ * Ensure the TTL index that reaps expired Better Auth sessions.
169
+ *
170
+ * BA stamps `expiresAt` on every session (tunable via `session.expiresIn`,
171
+ * default 7 days) but its mongo adapter creates NO index for it, and nothing
172
+ * else sweeps the collection — so `session` grows without bound on the hottest
173
+ * auth table. Measured on two live deployments before this existed: 568 rows
174
+ * (233 expired) and 909 rows of which EVERY ONE was expired, the oldest five
175
+ * months old.
176
+ *
177
+ * It lives here rather than in a host or a spine module because this kit is
178
+ * what knows Better Auth's collection names — a host-side fix only protects
179
+ * that host, and every other consumer keeps leaking.
180
+ *
181
+ * `expireAfterSeconds: 0` means "delete once `expiresAt` has passed", so
182
+ * mongo's background task keeps the collection bounded with no application
183
+ * code. Idempotent — re-creating an existing index is a no-op.
184
+ *
185
+ * NOT called automatically by {@link registerBetterAuthStubs}: that function is
186
+ * synchronous and writes nothing to the database, and silently issuing DDL from
187
+ * a model-registration helper would be a surprise. Call this once at boot.
188
+ *
189
+ * @returns `true` when an index was created or already present, `false` when
190
+ * the attempt failed (logged by the caller, never thrown — a missing
191
+ * TTL degrades storage, and refusing to boot over it is worse).
192
+ */
193
+ async function ensureBetterAuthSessionTtl(connection, options = {}) {
194
+ const name = options.collection ?? "session";
195
+ try {
196
+ await connection.collection(name).createIndex({ expiresAt: 1 }, {
197
+ expireAfterSeconds: 0,
198
+ name: options.indexName ?? "ttl_session_expiresAt"
199
+ });
200
+ return true;
201
+ } catch {
202
+ return false;
203
+ }
204
+ }
205
+ /**
153
206
  * Clear a dangling `activeOrganizationId` from Better Auth `session` rows.
154
207
  *
155
208
  * WHY THIS EXISTS: Better Auth purges its `member` / `invitation` rows when an
@@ -180,4 +233,4 @@ async function clearActiveOrganizationFromSessions(sessions, organizationId, opt
180
233
  return (await sessions.updateMany(filter, { $set: { activeOrganizationId: null } })).modifiedCount ?? 0;
181
234
  }
182
235
  //#endregion
183
- export { clearActiveOrganizationFromSessions, createBetterAuthOverlay, registerBetterAuthStubs };
236
+ export { clearActiveOrganizationFromSessions, createBetterAuthOverlay, ensureBetterAuthSessionTtl, registerBetterAuthStubs };
@@ -1,8 +1,8 @@
1
1
  import { c as PaginationConfig } from "./pagination-jSUdrACK.mjs";
2
- import { _ as RepositoryOptions, d as PluginType } from "./repository-Q16ErTu8.mjs";
3
- import { T as AuditTrailOptions, _ as RepositoryCachePluginOptions, i as CustomIdOptions, t as MultiTenantOptions } from "./multi-tenant.plugin-2VtSSm1Y.mjs";
4
- import { n as Repository } from "./Repository-BAzymuOz.mjs";
5
- import { i as Logger, o as SoftDeleteOptions } from "./plugin-options-BsFH0sTi.mjs";
2
+ import { _ as RepositoryOptions, d as PluginType } from "./repository-H_ffGgpE.mjs";
3
+ import { T as AuditTrailOptions, _ as RepositoryCachePluginOptions, i as CustomIdOptions, t as MultiTenantOptions } from "./multi-tenant.plugin-BwSvTirB.mjs";
4
+ import { n as Repository } from "./Repository-BdSkQ8vc.mjs";
5
+ import { i as Logger, o as SoftDeleteOptions } from "./plugin-options-DXnlKM3B.mjs";
6
6
  import { Model } from "mongoose";
7
7
  //#region src/create-repository.d.ts
8
8
  /**
@@ -1,5 +1,5 @@
1
- import { n as Repository } from "./Repository-B_1y9cCf.mjs";
2
- import { a as customIdPlugin, f as auditTrailPlugin, i as multiTenantPlugin, m as auditLogPlugin, n as softDeletePlugin, t as timestampPlugin, u as cachePlugin } from "./timestamp.plugin-CAXzHs9y.mjs";
1
+ import { n as Repository } from "./Repository-BArvU32x.mjs";
2
+ import { a as customIdPlugin, f as auditTrailPlugin, i as multiTenantPlugin, m as auditLogPlugin, n as softDeletePlugin, t as timestampPlugin, u as cachePlugin } from "./timestamp.plugin-BEc6TANm.mjs";
3
3
  import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "./method-registry.plugin-DblM7V6j.mjs";
4
4
  //#region src/create-repository.ts
5
5
  function isLoggerAudit(value) {
@@ -0,0 +1,67 @@
1
+ //#region src/utils/id-chunks.ts
2
+ /**
3
+ * Default slice for `$in` fan-out. Larger than `runChunkedPurge`'s 1000 on purpose:
4
+ * a purge chunk bounds the WRITE LOCK SCOPE of each progressive strategy step, while a
5
+ * snapshot slice bounds only the query-document size and the per-call working set —
6
+ * 10k ObjectIds is ~120 KB of `$in`, far under every limit, at 10× fewer round trips.
7
+ */
8
+ const DEFAULT_ID_CHUNK = 1e4;
9
+ /**
10
+ * Collect the bare id values matching `filter`, streaming via a driver cursor so the
11
+ * only O(n) allocation is the returned array itself (a `find().lean()` materializes an
12
+ * intermediate array of `{_id}` documents first — roughly double the peak for large n).
13
+ */
14
+ async function collectIds(model, filter, options = {}) {
15
+ const idField = options.idField ?? "_id";
16
+ const ids = [];
17
+ const cursor = model.find(filter, { [idField]: 1 }).session(options.session ?? null).lean().cursor();
18
+ for await (const doc of cursor) ids.push(doc[idField]);
19
+ return ids;
20
+ }
21
+ /**
22
+ * Yield `ids` in consecutive slices of at most `size`. Yields nothing for an empty
23
+ * array. `size` must be a positive integer — sliced `$in` correctness depends on it,
24
+ * so a nonsense size throws instead of silently degrading to one giant slice.
25
+ */
26
+ function* idChunks(ids, size) {
27
+ if (!Number.isInteger(size) || size < 1) throw new Error(`idChunks: size must be a positive integer, got ${String(size)}`);
28
+ for (let i = 0; i < ids.length; i += size) yield ids.slice(i, i + size);
29
+ }
30
+ /**
31
+ * Compose the keyset-progressed select filter: the base predicate, narrowed to rows past
32
+ * the last committed `_id`. `cursor == null` means "from the start". Selection paired
33
+ * with this must sort `_id`-ascending ({@link selectKeysetChunk} does), so `$gt: cursor`
34
+ * never revisits a processed row and never skips an unprocessed one. Never mutates
35
+ * `baseFilter`.
36
+ */
37
+ function keysetFilter(baseFilter, cursor) {
38
+ return cursor == null ? baseFilter : {
39
+ ...baseFilter,
40
+ _id: { $gt: cursor }
41
+ };
42
+ }
43
+ /**
44
+ * The `_id`-ascending bounded chunk select both action ports share:
45
+ * `find(filter).sort({_id: 1}).limit(limit).lean()`. Lean on purpose — chunk consumers
46
+ * want plain objects, not hydrated documents. Pass `projection: {_id: 1}` when only ids
47
+ * are needed (the purge port's common path); omit it for full docs (archive's readChunk,
48
+ * anonymize-functional).
49
+ */
50
+ async function selectKeysetChunk(model, filter, limit, options = {}) {
51
+ return await model.find(filter, options.projection).sort({ _id: 1 }).limit(limit).session(options.session ?? null).lean().exec();
52
+ }
53
+ /**
54
+ * Re-assert the base predicate on an id-narrowed WRITE: `{...base, _id: {$in: ids}}`.
55
+ * The `$in` alone would be enough to address the chunk, but carrying the base predicate
56
+ * defends against a row whose scope fields changed between the chunk select and the
57
+ * write — such a row is silently skipped instead of written outside its cohort. Both
58
+ * action ports use this shape; use it for any select-then-write over a chunk.
59
+ */
60
+ function narrowToIds(baseFilter, ids) {
61
+ return {
62
+ ...baseFilter,
63
+ _id: { $in: ids }
64
+ };
65
+ }
66
+ //#endregion
67
+ export { narrowToIds as a, keysetFilter as i, collectIds as n, selectKeysetChunk as o, idChunks as r, DEFAULT_ID_CHUNK as t };
@@ -1,4 +1,4 @@
1
- import { c as GroupResult, d as MinMaxResult, f as OperationOptions, g as UpdateOptions, h as UpdateManyResult, i as CreateOptions, o as DeleteResult, s as FindOneAndUpdateOptions, y as LookupOptions } from "./operations-CuzX9m2Z.mjs";
1
+ import { _ as UpdateOptions, a as CreateOptions, b as LookupOptions, c as FindOneAndUpdateOptions, f as MinMaxResult, g as UpdateManyResult, l as GroupResult, p as OperationOptions, s as DeleteResult } from "./operations-DZodvjC-.mjs";
2
2
  import { a as PopulateSpec, i as ObjectId, l as SortSpec, o as ReadPreferenceType, s as SelectSpec, t as AnyDocument } from "./core-62oo3N1C.mjs";
3
3
  import { p as UpdateWithValidationResult } from "./type-utils-CLzRFtIA.mjs";
4
4
  import { Model, PipelineStage } from "mongoose";
package/dist/index.d.mts CHANGED
@@ -1,18 +1,18 @@
1
- import { _ as WithTransactionOptions, c as GroupResult, d as MinMaxResult, f as OperationOptions, g as UpdateOptions, h as UpdateManyResult, i as CreateOptions, l as LookupPopulateOptions, m as SessionOptions, n as CacheOperationOptions, o as DeleteResult, p as ReadOptions, r as CacheableOptions, s as FindOneAndUpdateOptions, t as AggregateOptions, u as LookupPopulateResult, v as LookupBuilder, y as LookupOptions } from "./operations-CuzX9m2Z.mjs";
1
+ import { _ as UpdateOptions, a as CreateOptions, b as LookupOptions, c as FindOneAndUpdateOptions, d as LookupPopulateResult, f as MinMaxResult, g as UpdateManyResult, h as SessionOptions, i as ConvenientTransactionOptions, l as GroupResult, m as ReadOptions, n as CacheOperationOptions, p as OperationOptions, r as CacheableOptions, s as DeleteResult, t as AggregateOptions, u as LookupPopulateOptions, v as WithTransactionOptions, y as LookupBuilder } from "./operations-DZodvjC-.mjs";
2
2
  import { a as PopulateSpec, c as SortDirection, i as ObjectId, l as SortSpec$1, n as AnyModel, o as ReadPreferenceType, s as SelectSpec, t as AnyDocument } from "./core-62oo3N1C.mjs";
3
3
  import { a as DecodedCursor, c as PaginationConfig, i as CursorPayload, l as ValueType, n as BasePaginationOptions, o as KeysetPaginationOptions, r as CollationOptions, s as OffsetPaginationOptions, t as AggregatePaginationOptions } from "./pagination-jSUdrACK.mjs";
4
- import { t as index_d_exports } from "./index-Beyr1r4d.mjs";
4
+ import { t as index_d_exports } from "./index-DKEql4cL.mjs";
5
5
  import { a as InferRawDoc, c as NonNullableFields, d as Strict, f as UpdatePatch, i as InferDocument, l as PartialBy, n as DeepPartial, o as KeysOfType, p as UpdateWithValidationResult, r as DocField, s as MongoOperatorUpdate, t as CreateInput, u as RequiredBy } from "./type-utils-CLzRFtIA.mjs";
6
- import { S as RepositoryPluginName, _ as RepositoryOptions, a as HookMode, b as MONGOKIT_PLUGIN_NAMES, c as MinimalRepoView, d as PluginType, f as PrioritizedHook, g as RepositoryOperation, h as RepositoryInstance, i as EventPhase, l as Plugin, m as RepositoryEvent, n as EventHandlers, o as Middleware, p as RepositoryContext, r as EventPayload, s as MiddlewareContext, t as AllPluginMethods, u as PluginFunction, v as UserContext, x as MongoKitPluginName, y as WithPlugins } from "./repository-Q16ErTu8.mjs";
7
- import { A as ensureAuditTrailReady, C as AuditTrailMethods, D as AuditTrailQueryOptions, E as AuditTrailQuery, O as EnsureAuditTrailReadyOptions, S as AuditQueryResult, T as AuditTrailOptions, _ as RepositoryCachePluginOptions, a as DateSequentialIdOptions, b as AuditOperation, c as SequentialIdOptions, d as getNextSequence, f as prefixedId, g as RepositoryCacheHandle, h as CacheOptions, i as CustomIdOptions, k as auditTrailPlugin, l as customIdPlugin, m as CacheAdapter, n as adminBypass, o as IdGenerator, p as sequentialId, r as multiTenantPlugin, s as PrefixedIdOptions, t as MultiTenantOptions, u as dateSequentialId, v as cachePlugin, w as AuditTrailMode, x as AuditQueryOptions, y as AuditEntry } from "./multi-tenant.plugin-2VtSSm1Y.mjs";
6
+ import { S as RepositoryPluginName, _ as RepositoryOptions, a as HookMode, b as MONGOKIT_PLUGIN_NAMES, c as MinimalRepoView, d as PluginType, f as PrioritizedHook, g as RepositoryOperation, h as RepositoryInstance, i as EventPhase, l as Plugin, m as RepositoryEvent, n as EventHandlers, o as Middleware, p as RepositoryContext, r as EventPayload, s as MiddlewareContext, t as AllPluginMethods, u as PluginFunction, v as UserContext, x as MongoKitPluginName, y as WithPlugins } from "./repository-H_ffGgpE.mjs";
7
+ import { A as ensureAuditTrailReady, C as AuditTrailMethods, D as AuditTrailQueryOptions, E as AuditTrailQuery, O as EnsureAuditTrailReadyOptions, S as AuditQueryResult, T as AuditTrailOptions, _ as RepositoryCachePluginOptions, a as DateSequentialIdOptions, b as AuditOperation, c as SequentialIdOptions, d as getNextSequence, f as prefixedId, g as RepositoryCacheHandle, h as CacheOptions, i as CustomIdOptions, k as auditTrailPlugin, l as customIdPlugin, m as CacheAdapter, n as adminBypass, o as IdGenerator, p as sequentialId, r as multiTenantPlugin, s as PrefixedIdOptions, t as MultiTenantOptions, u as dateSequentialId, v as cachePlugin, w as AuditTrailMode, x as AuditQueryOptions, y as AuditEntry } from "./multi-tenant.plugin-BwSvTirB.mjs";
8
8
  import { t as PaginationEngine } from "./PaginationEngine-CBS1XGzp.mjs";
9
- import { a as AggregationPlan, c as SortOrder, i as AggregationBuilder, l as SortSpec, n as Repository, o as GroupSpec, r as TransitionMachine, s as ProjectionSpec, t as HOOK_PRIORITY, u as VectorSearchOptions } from "./Repository-BAzymuOz.mjs";
10
- import { a as SoftDeleteFilterMode, c as ValidationChainOptions, i as Logger, l as ValidatorDefinition, n as CascadeRelation, o as SoftDeleteOptions, r as FieldPreset, s as SoftDeleteRepository, t as CascadeOptions } from "./plugin-options-BsFH0sTi.mjs";
11
- import { n as CreateRepositoryConfig, r as createRepository, t as AuditConfig } from "./create-repository-mEdfH9sz.mjs";
12
- import { A as IdempotencyStoreMethods, B as auditLogPlugin, C as LeaseMethods, D as ImmutableStatesPluginOptions, E as ImmutableClaimView, F as elasticSearchPlugin, H as appendOnlyPlugin, I as cascadePlugin, L as BatchOperationsMethods, M as idempotencyStorePlugin, N as fieldFilterPlugin, O as immutableStatesPlugin, P as ElasticSearchOptions, R as BulkWriteResult, S as methodRegistryPlugin, T as leasePlugin, U as AggregateHelpersMethods, V as AppendOnlyPluginOptions, W as aggregateHelpersPlugin, _ as OperationMetric, a as requireField, b as mongoOperationsPlugin, c as timestampPlugin, d as createTenantContext, f as SubdocumentMethods, g as ObservabilityOptions, h as softDeletePlugin, i as immutableField, j as IdempotencyStorePluginOptions, k as IdempotencyClaim, l as TenantContext, m as SoftDeleteMethods, n as autoInject, o as uniqueField, p as subdocumentPlugin, r as blockIf, s as validationChainPlugin, t as OperationType, u as TenantStore, v as observabilityPlugin, w as LeasePluginOptions, y as MongoOperationsMethods, z as batchOperationsPlugin } from "./validation-chain.plugin-BSshLJTA.mjs";
9
+ import { a as AggregationPlan, c as SortOrder, i as AggregationBuilder, l as SortSpec, n as Repository, o as GroupSpec, r as TransitionMachine, s as ProjectionSpec, t as HOOK_PRIORITY, u as VectorSearchOptions } from "./Repository-BdSkQ8vc.mjs";
10
+ import { a as SoftDeleteFilterMode, c as ValidationChainOptions, i as Logger, l as ValidatorDefinition, n as CascadeRelation, o as SoftDeleteOptions, r as FieldPreset, s as SoftDeleteRepository, t as CascadeOptions } from "./plugin-options-DXnlKM3B.mjs";
11
+ import { n as CreateRepositoryConfig, r as createRepository, t as AuditConfig } from "./create-repository-BU4x9Kb0.mjs";
12
+ import { A as IdempotencyStoreMethods, B as auditLogPlugin, C as LeaseMethods, D as ImmutableStatesPluginOptions, E as ImmutableClaimView, F as elasticSearchPlugin, H as appendOnlyPlugin, I as cascadePlugin, L as BatchOperationsMethods, M as idempotencyStorePlugin, N as fieldFilterPlugin, O as immutableStatesPlugin, P as ElasticSearchOptions, R as BulkWriteResult, S as methodRegistryPlugin, T as leasePlugin, U as AggregateHelpersMethods, V as AppendOnlyPluginOptions, W as aggregateHelpersPlugin, _ as OperationMetric, a as requireField, b as mongoOperationsPlugin, c as timestampPlugin, d as createTenantContext, f as SubdocumentMethods, g as ObservabilityOptions, h as softDeletePlugin, i as immutableField, j as IdempotencyStorePluginOptions, k as IdempotencyClaim, l as TenantContext, m as SoftDeleteMethods, n as autoInject, o as uniqueField, p as subdocumentPlugin, r as blockIf, s as validationChainPlugin, t as OperationType, u as TenantStore, v as observabilityPlugin, w as LeasePluginOptions, y as MongoOperationsMethods, z as batchOperationsPlugin } from "./validation-chain.plugin-yQRob3Av.mjs";
13
13
  import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-BjdPX3YF.mjs";
14
14
  import { n as SchemaIndexes } from "./indexes-DIjMpYES.mjs";
15
- import { S as parseDuplicateKeyError, _ as getMongooseProjection, a as createMemoryCache, b as createError, g as getFieldsForUser, h as filterResponseData, i as buildCrudSchemasFromMongooseSchema, m as createFieldPreset, n as CrudSchemasFramework, o as LoggerConfig, r as buildCrudSchemasFromModel, s as configureLogger, t as toPlain, u as idVariants, v as CreateErrorOptions, x as isDuplicateKeyError, y as ParseDuplicateKeyOptions } from "./to-plain-BuX1RUZV.mjs";
15
+ import { S as parseDuplicateKeyError, _ as getMongooseProjection, a as createMemoryCache, b as createError, g as getFieldsForUser, h as filterResponseData, i as buildCrudSchemasFromMongooseSchema, m as createFieldPreset, n as CrudSchemasFramework, o as LoggerConfig, r as buildCrudSchemasFromModel, s as configureLogger, t as toPlain, u as idVariants, v as CreateErrorOptions, x as isDuplicateKeyError, y as ParseDuplicateKeyOptions } from "./to-plain-Dyz_qvuo.mjs";
16
16
  import { RepoCapabilities } from "@classytic/repo-core/repository";
17
17
  import { ClientSession, Connection, Model, PipelineStage, Schema } from "mongoose";
18
18
  //#region src/actions/cascade-references.d.ts
@@ -144,10 +144,11 @@ declare function transactionResolutionOf(caps: RepoCapabilities): CapabilityReso
144
144
  /**
145
145
  * What MongoDB supports through mongokit's surface, DECLARED (not observed).
146
146
  *
147
- * Deployment-dependent flags (`transactions`, `nestedTransactions`,
148
- * `changeStreams` — all three need a replica set or mongos) are `true` here
149
- * because they describe the product, not the server you happen to be pointed
150
- * at. **Do not read this constant to decide whether a live connection can run
147
+ * Deployment-dependent flags (`transactions`, `changeStreams` — both need a
148
+ * replica set or mongos) are `true` here because they describe the product,
149
+ * not the server you happen to be pointed at. `nestedTransactions` is NOT one
150
+ * of them: it is `false` unconditionally, because the refusal comes from
151
+ * mongokit's own tx-bound proxy rather than from the deployment. **Do not read this constant to decide whether a live connection can run
151
152
  * a transaction** — that is {@link resolveMongoCapabilities}, which is what
152
153
  * `Repository#capabilities` returns. This one is for the cross-kit conformance
153
154
  * harness (which overrides environment-specific limits where it is
@@ -971,4 +972,4 @@ declare function createOptionsExtractor<TCtx extends Record<string, unknown>>(fi
971
972
  */
972
973
  declare function systemContext(extra?: SessionOptions): SessionOptions;
973
974
  //#endregion
974
- export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, AggregationBuilder, type AggregationPlan, type SortOrder as AggregationSortOrder, type SortSpec as AggregationSortSpec, type AllPluginMethods, type AnyDocument, type AnyModel, type AppendOnlyPluginOptions, type AuditConfig, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailMode, type AuditTrailOptions, AuditTrailQuery, type AuditTrailQueryOptions, type BasePaginationOptions, type BatchOperationsMethods, type BulkWriteResult, type CacheAdapter, type CacheOperationOptions, type CacheOptions, type CacheableOptions, type CapabilityConnectionLike, type CapabilityProbeOptions, type CapabilityResolution, type CascadeOptions, type CascadePurgeReferencesOptions, type CascadeRelation, type CollationOptions, type CollectionRWLike, type CreateErrorOptions, type CreateInput, type CreateOptions, type CreateRepositoryConfig, type CrudSchemasFramework, type CursorPayload, type CustomIdOptions, type DateSequentialIdOptions, type DecodedCursor, type DeepPartial, type DefineModelOptions, type DeleteResult, type DocField, type ElasticSearchOptions, type EnsureAuditTrailReadyOptions, type EventHandlers, type EventPayload, type EventPhase, type ExistingModelPolicy, type FieldPreset, type FieldType, type FilterQuery, type FindOneAndUpdateOptions, type GroupResult, type GroupSpec, HOOK_PRIORITY, type HookMode, type IdGenerator, type IdempotencyClaim, type IdempotencyStoreMethods, type IdempotencyStorePluginOptions, type ImmutableClaimView, type ImmutableStatesPluginOptions, type InferDocument, type InferRawDoc, type KeysOfType, type KeysetPaginationOptions, type LeaseMethods, type LeasePluginOptions, type Logger, type LoggerConfig, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, type Middleware, type MiddlewareContext, type MinMaxResult, type MinimalRepoView, type ModelBlueprint, ModelCollisionError, type ModelSpec, type MongoKitPluginName, type MongoOperationsMethods, type MongoOperatorUpdate, type MongoRepoCapabilities, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OperationDescriptor, type OperationMetric, type OperationOptions, type OperationType, type PaginationConfig, PaginationEngine, type ParseDuplicateKeyOptions, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, type PrioritizedHook, type ProjectionSpec, type PurgeMode, type PurgeableRepoLike, QueryParser, type QueryParserOptions, type QuerySchema, READ_OPERATIONS, type ReadOptions, type ReadPreferenceType, type ReferencePurgeResult, type ReferenceRelation, Repository, Repository as default, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type RepositoryContext, type RepositoryEvent, type RepositoryInstance, type RepositoryOperation, type RepositoryOptions, type RepositoryPluginName, type RequiredBy, type SchemaLike, type SearchMode, type SelectSpec, type SequentialIdOptions, type SessionOptions, type SessionStarter, type SoftDeleteFilterMode, type SoftDeleteMethods, type SoftDeleteOptions, type SoftDeleteRepository, type SortDirection, type SortSpec$1 as SortSpec, type Strict, type SubdocumentMethods, type TenantContext, type TenantStore, type TransactionSupport, type TransitionMachine, type UpdateManyResult, type UpdateOptions, type UpdatePatch, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidatorDefinition, type ValueType, type VectorSearchOptions, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, capabilitiesForSupport, cascadePlugin, cascadePurgeReferences, classifyHelloReply, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, defineModel, defineModels, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isDuplicateKeyError, isModelRegistered, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, probeMongoCapabilities, repoOptionsFromCtx, requireField, resetCapabilityProbeCache, resolveMongoCapabilities, resolveTransactionSupport, sequentialId, softDeletePlugin, subdocumentPlugin, supportsTransactions, systemContext, timestampPlugin, toPlain, transactionResolutionOf, uniqueField, validationChainPlugin, withTransaction };
975
+ export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, AggregationBuilder, type AggregationPlan, type SortOrder as AggregationSortOrder, type SortSpec as AggregationSortSpec, type AllPluginMethods, type AnyDocument, type AnyModel, type AppendOnlyPluginOptions, type AuditConfig, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailMode, type AuditTrailOptions, AuditTrailQuery, type AuditTrailQueryOptions, type BasePaginationOptions, type BatchOperationsMethods, type BulkWriteResult, type CacheAdapter, type CacheOperationOptions, type CacheOptions, type CacheableOptions, type CapabilityConnectionLike, type CapabilityProbeOptions, type CapabilityResolution, type CascadeOptions, type CascadePurgeReferencesOptions, type CascadeRelation, type CollationOptions, type CollectionRWLike, type ConvenientTransactionOptions, type CreateErrorOptions, type CreateInput, type CreateOptions, type CreateRepositoryConfig, type CrudSchemasFramework, type CursorPayload, type CustomIdOptions, type DateSequentialIdOptions, type DecodedCursor, type DeepPartial, type DefineModelOptions, type DeleteResult, type DocField, type ElasticSearchOptions, type EnsureAuditTrailReadyOptions, type EventHandlers, type EventPayload, type EventPhase, type ExistingModelPolicy, type FieldPreset, type FieldType, type FilterQuery, type FindOneAndUpdateOptions, type GroupResult, type GroupSpec, HOOK_PRIORITY, type HookMode, type IdGenerator, type IdempotencyClaim, type IdempotencyStoreMethods, type IdempotencyStorePluginOptions, type ImmutableClaimView, type ImmutableStatesPluginOptions, type InferDocument, type InferRawDoc, type KeysOfType, type KeysetPaginationOptions, type LeaseMethods, type LeasePluginOptions, type Logger, type LoggerConfig, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, type Middleware, type MiddlewareContext, type MinMaxResult, type MinimalRepoView, type ModelBlueprint, ModelCollisionError, type ModelSpec, type MongoKitPluginName, type MongoOperationsMethods, type MongoOperatorUpdate, type MongoRepoCapabilities, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OperationDescriptor, type OperationMetric, type OperationOptions, type OperationType, type PaginationConfig, PaginationEngine, type ParseDuplicateKeyOptions, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, type PrioritizedHook, type ProjectionSpec, type PurgeMode, type PurgeableRepoLike, QueryParser, type QueryParserOptions, type QuerySchema, READ_OPERATIONS, type ReadOptions, type ReadPreferenceType, type ReferencePurgeResult, type ReferenceRelation, Repository, Repository as default, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type RepositoryContext, type RepositoryEvent, type RepositoryInstance, type RepositoryOperation, type RepositoryOptions, type RepositoryPluginName, type RequiredBy, type SchemaLike, type SearchMode, type SelectSpec, type SequentialIdOptions, type SessionOptions, type SessionStarter, type SoftDeleteFilterMode, type SoftDeleteMethods, type SoftDeleteOptions, type SoftDeleteRepository, type SortDirection, type SortSpec$1 as SortSpec, type Strict, type SubdocumentMethods, type TenantContext, type TenantStore, type TransactionSupport, type TransitionMachine, type UpdateManyResult, type UpdateOptions, type UpdatePatch, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidatorDefinition, type ValueType, type VectorSearchOptions, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, capabilitiesForSupport, cascadePlugin, cascadePurgeReferences, classifyHelloReply, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, defineModel, defineModels, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isDuplicateKeyError, isModelRegistered, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, probeMongoCapabilities, repoOptionsFromCtx, requireField, resetCapabilityProbeCache, resolveMongoCapabilities, resolveTransactionSupport, sequentialId, softDeletePlugin, subdocumentPlugin, supportsTransactions, systemContext, timestampPlugin, toPlain, transactionResolutionOf, uniqueField, validationChainPlugin, withTransaction };
package/dist/index.mjs CHANGED
@@ -1,16 +1,16 @@
1
1
  import { n as idVariants } from "./id-resolution-BqDrk0S2.mjs";
2
- import { _ as resetCapabilityProbeCache, a as supportsTransactions, b as transactionResolutionOf, c as ALL_OPERATIONS, d as READ_OPERATIONS, f as operationsByPolicyKey, g as probeMongoCapabilities, h as classifyHelloReply, i as isTransactionUnsupported, l as MUTATING_OPERATIONS, m as capabilitiesForSupport, n as Repository, o as withTransaction, p as MONGOKIT_CAPABILITIES, r as batchTransaction, s as AggregationBuilder, t as HOOK_PRIORITY, u as OP_REGISTRY, v as resolveMongoCapabilities, x as createMongoPurgePort, y as resolveTransactionSupport } from "./Repository-B_1y9cCf.mjs";
3
- import { b as LookupBuilder, x as compileFilterToMongo } from "./update-w5i-pa_c.mjs";
2
+ import { _ as resetCapabilityProbeCache, a as supportsTransactions, b as transactionResolutionOf, c as ALL_OPERATIONS, d as READ_OPERATIONS, f as operationsByPolicyKey, g as probeMongoCapabilities, h as classifyHelloReply, i as isTransactionUnsupported, l as MUTATING_OPERATIONS, m as capabilitiesForSupport, n as Repository, o as withTransaction, p as MONGOKIT_CAPABILITIES, r as batchTransaction, s as AggregationBuilder, t as HOOK_PRIORITY, u as OP_REGISTRY, v as resolveMongoCapabilities, x as createMongoPurgePort, y as resolveTransactionSupport } from "./Repository-BArvU32x.mjs";
3
+ import { b as LookupBuilder, x as compileFilterToMongo } from "./update-jSXp3nu3.mjs";
4
4
  import { n as warn, t as configureLogger } from "./logger-K7dPmu2U.mjs";
5
5
  import { n as isDuplicateKeyError, r as parseDuplicateKeyError, t as createError } from "./error-BuByZwKU.mjs";
6
6
  import { t as actions_exports } from "./actions/index.mjs";
7
- import { a as customIdPlugin, c as prefixedId, d as AuditTrailQuery, f as auditTrailPlugin, i as multiTenantPlugin, l as sequentialId, m as auditLogPlugin, n as softDeletePlugin, o as dateSequentialId, p as ensureAuditTrailReady, r as adminBypass, s as getNextSequence, t as timestampPlugin, u as cachePlugin } from "./timestamp.plugin-CAXzHs9y.mjs";
7
+ import { a as customIdPlugin, c as prefixedId, d as AuditTrailQuery, f as auditTrailPlugin, i as multiTenantPlugin, l as sequentialId, m as auditLogPlugin, n as softDeletePlugin, o as dateSequentialId, p as ensureAuditTrailReady, r as adminBypass, s as getNextSequence, t as timestampPlugin, u as cachePlugin } from "./timestamp.plugin-BEc6TANm.mjs";
8
8
  import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "./method-registry.plugin-DblM7V6j.mjs";
9
- import { t as PaginationEngine } from "./PaginationEngine-CSh-P8Nh.mjs";
9
+ import { t as PaginationEngine } from "./PaginationEngine-GdyX29tE.mjs";
10
10
  import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
11
- import { t as createRepository } from "./create-repository-a3DQ5tzt.mjs";
11
+ import { t as createRepository } from "./create-repository-CPNbktFG.mjs";
12
12
  import { n as defineModel, r as isModelRegistered, t as ModelCollisionError } from "./model-registry-ClKtWiHa.mjs";
13
- import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "./validation-chain.plugin-BihbMQRq.mjs";
13
+ import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "./validation-chain.plugin-CyK1s9Ze.mjs";
14
14
  import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-FSQFLpsU.mjs";
15
15
  import { buildFieldTypeMap, coerceFieldValue, coerceHeuristic } from "./query/primitives/coercion.mjs";
16
16
  import { extractSchemaIndexes } from "./query/primitives/indexes.mjs";
@@ -536,11 +536,13 @@ function handleOperatorSyntax(rt, filters, regexFields, operatorMatch, value) {
536
536
  }
537
537
  if (operator.toLowerCase() === "options" && regexFields[field]) {
538
538
  const fieldValue = filters[field];
539
- if (typeof fieldValue === "object" && fieldValue !== null && "$regex" in fieldValue) if (typeof value === "string" && /^[imsx]+$/.test(value)) fieldValue.$options = value;
540
- else rt.reject(`Blocked invalid regex $options value: ${String(value)}. Allowed flags: i, m, s, x`, {
541
- field,
542
- value: String(value)
543
- });
539
+ if (typeof fieldValue === "object" && fieldValue !== null && "$regex" in fieldValue) {
540
+ if (typeof value === "string" && /^[imsx]+$/.test(value)) fieldValue.$options = value;
541
+ else rt.reject(`Blocked invalid regex $options value: ${String(value)}. Allowed flags: i, m, s, x`, {
542
+ field,
543
+ value: String(value)
544
+ });
545
+ }
544
546
  return;
545
547
  }
546
548
  if (operator.toLowerCase() === "between") {
@@ -594,26 +596,27 @@ function handleOperatorSyntax(rt, filters, regexFields, operatorMatch, value) {
594
596
  "gte",
595
597
  "lt",
596
598
  "lte"
597
- ].includes(op)) if (rt.fieldTypes.has(field)) {
598
- processedValue = coerceFieldValue(field, value, rt.fieldTypes);
599
- if (typeof processedValue === "number" && Number.isNaN(processedValue)) {
600
- rt.reject(`Invalid value for ${field}[${op}]: ${String(value)}`, {
601
- field,
602
- op
603
- });
604
- return;
605
- }
606
- } else {
607
- processedValue = parseFloat(String(value));
608
- if (Number.isNaN(processedValue)) {
609
- rt.reject(`Non-numeric value for ${field}[${op}]: ${String(value)}`, {
610
- field,
611
- op
612
- });
613
- return;
599
+ ].includes(op)) {
600
+ if (rt.fieldTypes.has(field)) {
601
+ processedValue = coerceFieldValue(field, value, rt.fieldTypes);
602
+ if (typeof processedValue === "number" && Number.isNaN(processedValue)) {
603
+ rt.reject(`Invalid value for ${field}[${op}]: ${String(value)}`, {
604
+ field,
605
+ op
606
+ });
607
+ return;
608
+ }
609
+ } else {
610
+ processedValue = parseFloat(String(value));
611
+ if (Number.isNaN(processedValue)) {
612
+ rt.reject(`Non-numeric value for ${field}[${op}]: ${String(value)}`, {
613
+ field,
614
+ op
615
+ });
616
+ return;
617
+ }
614
618
  }
615
- }
616
- else if (op === "in" || op === "nin") processedValue = (Array.isArray(value) ? value : String(value).split(",").map((v) => v.trim())).map((elem) => coerceFieldValue(field, elem, rt.fieldTypes));
619
+ } else if (op === "in" || op === "nin") processedValue = (Array.isArray(value) ? value : String(value).split(",").map((v) => v.trim())).map((elem) => coerceFieldValue(field, elem, rt.fieldTypes));
617
620
  else processedValue = coerceFieldValue(field, value, rt.fieldTypes);
618
621
  if (typeof filters[field] !== "object" || filters[field] === null || Array.isArray(filters[field])) filters[field] = {};
619
622
  filters[field][mongoOperator] = processedValue;
@@ -672,26 +675,27 @@ function handleBracketSyntax(rt, field, operators, parsedFilters, depth = 0) {
672
675
  "gte",
673
676
  "lt",
674
677
  "lte"
675
- ].includes(operator)) if (rt.fieldTypes.has(field)) {
676
- processedValue = coerceFieldValue(field, value, rt.fieldTypes);
677
- if (typeof processedValue === "number" && Number.isNaN(processedValue)) {
678
- rt.reject(`Invalid value for ${field}[${operator}]: ${String(value)}`, {
679
- field,
680
- operator
681
- });
682
- continue;
683
- }
684
- } else {
685
- processedValue = parseFloat(String(value));
686
- if (Number.isNaN(processedValue)) {
687
- rt.reject(`Non-numeric value for ${field}[${operator}]: ${String(value)}`, {
688
- field,
689
- operator
690
- });
691
- continue;
678
+ ].includes(operator)) {
679
+ if (rt.fieldTypes.has(field)) {
680
+ processedValue = coerceFieldValue(field, value, rt.fieldTypes);
681
+ if (typeof processedValue === "number" && Number.isNaN(processedValue)) {
682
+ rt.reject(`Invalid value for ${field}[${operator}]: ${String(value)}`, {
683
+ field,
684
+ operator
685
+ });
686
+ continue;
687
+ }
688
+ } else {
689
+ processedValue = parseFloat(String(value));
690
+ if (Number.isNaN(processedValue)) {
691
+ rt.reject(`Non-numeric value for ${field}[${operator}]: ${String(value)}`, {
692
+ field,
693
+ operator
694
+ });
695
+ continue;
696
+ }
692
697
  }
693
- }
694
- else if (operator === "in" || operator === "nin") processedValue = (Array.isArray(value) ? value : String(value).split(",").map((v) => v.trim())).map((elem) => coerceFieldValue(field, elem, rt.fieldTypes));
698
+ } else if (operator === "in" || operator === "nin") processedValue = (Array.isArray(value) ? value : String(value).split(",").map((v) => v.trim())).map((elem) => coerceFieldValue(field, elem, rt.fieldTypes));
695
699
  else if (operator === "like" || operator === "contains" || operator === "regex") {
696
700
  const safeRegex = createSafeRegex(rt, value, "i", operator === "regex" ? "reject" : "escape");
697
701
  if (!safeRegex) continue;
@@ -1460,11 +1464,13 @@ var QueryParser = class {
1460
1464
  const parsedPage = parsePositiveInt(rt, page, "page");
1461
1465
  if (parsedPage !== void 0) parsed.page = parsedPage;
1462
1466
  const orGroup = parseOr(rt, query);
1463
- if (orGroup) if (parsed.filters.$or) {
1464
- const existingOr = parsed.filters.$or;
1465
- delete parsed.filters.$or;
1466
- parsed.filters.$and = [{ $or: existingOr }, { $or: orGroup }];
1467
- } else parsed.filters.$or = orGroup;
1467
+ if (orGroup) {
1468
+ if (parsed.filters.$or) {
1469
+ const existingOr = parsed.filters.$or;
1470
+ delete parsed.filters.$or;
1471
+ parsed.filters.$and = [{ $or: existingOr }, { $or: orGroup }];
1472
+ } else parsed.filters.$or = orGroup;
1473
+ }
1468
1474
  parsed.filters = enhanceWithBetween(rt, parsed.filters);
1469
1475
  return parsed;
1470
1476
  }
@@ -1,7 +1,7 @@
1
1
  import { i as ObjectId } from "./core-62oo3N1C.mjs";
2
- import { l as Plugin, p as RepositoryContext } from "./repository-Q16ErTu8.mjs";
2
+ import { l as Plugin, p as RepositoryContext } from "./repository-H_ffGgpE.mjs";
3
3
  import mongoose, { Connection } from "mongoose";
4
- import { CacheAdapter as CacheAdapter$1, CacheOptions, RepositoryCacheHandle, RepositoryCachePluginOptions, cachePlugin } from "@classytic/repo-core/cache";
4
+ import { CacheAdapter as CacheAdapter$1, CacheOptions, RepositoryCacheHandle as RepositoryCacheHandle$1, RepositoryCachePluginOptions, cachePlugin } from "@classytic/repo-core/cache";
5
5
  import { TenantConfig } from "@classytic/repo-core/tenant";
6
6
  //#region src/plugins/audit-trail.plugin.d.ts
7
7
  /**
@@ -582,4 +582,4 @@ declare function adminBypass(options: {
582
582
  adminRoles: readonly string[];
583
583
  }): (context: RepositoryContext, operation: string) => boolean;
584
584
  //#endregion
585
- export { ensureAuditTrailReady as A, AuditTrailMethods as C, AuditTrailQueryOptions as D, AuditTrailQuery as E, EnsureAuditTrailReadyOptions as O, AuditQueryResult as S, AuditTrailOptions as T, RepositoryCachePluginOptions as _, DateSequentialIdOptions as a, AuditOperation as b, SequentialIdOptions as c, getNextSequence as d, prefixedId as f, RepositoryCacheHandle as g, CacheOptions as h, CustomIdOptions as i, auditTrailPlugin as k, customIdPlugin as l, CacheAdapter$1 as m, adminBypass as n, IdGenerator as o, sequentialId as p, multiTenantPlugin as r, PrefixedIdOptions as s, MultiTenantOptions as t, dateSequentialId as u, cachePlugin as v, AuditTrailMode as w, AuditQueryOptions as x, AuditEntry as y };
585
+ export { ensureAuditTrailReady as A, AuditTrailMethods as C, AuditTrailQueryOptions as D, AuditTrailQuery as E, EnsureAuditTrailReadyOptions as O, AuditQueryResult as S, AuditTrailOptions as T, RepositoryCachePluginOptions as _, DateSequentialIdOptions as a, AuditOperation as b, SequentialIdOptions as c, getNextSequence as d, prefixedId as f, RepositoryCacheHandle$1 as g, CacheOptions as h, CustomIdOptions as i, auditTrailPlugin as k, customIdPlugin as l, CacheAdapter$1 as m, adminBypass as n, IdGenerator as o, sequentialId as p, multiTenantPlugin as r, PrefixedIdOptions as s, MultiTenantOptions as t, dateSequentialId as u, cachePlugin as v, AuditTrailMode as w, AuditQueryOptions as x, AuditEntry as y };
@@ -361,14 +361,25 @@ interface CacheOperationOptions {
361
361
  /** Per-call cache override forwarded to the unified cache plugin. */
362
362
  cache?: import('@classytic/repo-core/cache').CacheOptions;
363
363
  }
364
+ /**
365
+ * `session.withTransaction()`'s own options param — `TransactionOptions` (readConcern,
366
+ * writeConcern, readPreference, maxCommitTimeMS) PLUS the driver's `timeoutMS`, which is
367
+ * NOT a field on `TransactionOptions` itself (verified against the installed mongodb
368
+ * driver's `ClientSession.withTransaction` overload, mongodb.d.ts). `timeoutMS` bounds the
369
+ * WHOLE `withTransaction` call, including its built-in retry loop (default 120s) — distinct
370
+ * from `maxCommitTimeMS`, which only bounds a single `commitTransaction` server call.
371
+ */
372
+ type ConvenientTransactionOptions = import('mongoose').mongo.TransactionOptions & {
373
+ timeoutMS?: number;
374
+ };
364
375
  /** withTransaction options */
365
376
  interface WithTransactionOptions {
366
377
  /** Allow non-transactional fallback when transactions are unsupported */
367
378
  allowFallback?: boolean;
368
379
  /** Optional hook to observe fallback triggers */
369
380
  onFallback?: (error: Error) => void;
370
- /** MongoDB transaction options (readConcern, writeConcern, readPreference, maxCommitTimeMS) */
371
- transactionOptions?: import('mongoose').mongo.TransactionOptions;
381
+ /** MongoDB transaction options (readConcern, writeConcern, readPreference, maxCommitTimeMS, timeoutMS) */
382
+ transactionOptions?: ConvenientTransactionOptions;
372
383
  }
373
384
  /** Create operation options */
374
385
  interface CreateOptions extends SessionOptions {
@@ -381,6 +392,14 @@ interface CreateOptions extends SessionOptions {
381
392
  interface UpdateOptions extends OperationOptions {
382
393
  /** Enable update pipeline syntax */
383
394
  updatePipeline?: boolean;
395
+ /**
396
+ * Optimistic-concurrency CAS (repo-core `WriteOptions.ifVersion`): apply
397
+ * only if the stored version field equals this value; the write also
398
+ * `$inc`s the version. Stale version → `VersionConflictError` (never
399
+ * `null` — null stays "not found"). Version field: `RepositoryOptions.versionField`,
400
+ * default `'__v'`.
401
+ */
402
+ ifVersion?: number;
384
403
  /** Array filters for positional operator $[<identifier>] updates */
385
404
  arrayFilters?: Record<string, unknown>[];
386
405
  }
@@ -499,4 +518,4 @@ interface MinMaxResult {
499
518
  max: unknown;
500
519
  }
501
520
  //#endregion
502
- export { WithTransactionOptions as _, DeleteManyResult as a, GroupResult as c, MinMaxResult as d, OperationOptions as f, UpdateOptions as g, UpdateManyResult as h, CreateOptions as i, LookupPopulateOptions as l, SessionOptions as m, CacheOperationOptions as n, DeleteResult as o, ReadOptions as p, CacheableOptions as r, FindOneAndUpdateOptions as s, AggregateOptions as t, LookupPopulateResult as u, LookupBuilder as v, LookupOptions as y };
521
+ export { UpdateOptions as _, CreateOptions as a, LookupOptions as b, FindOneAndUpdateOptions as c, LookupPopulateResult as d, MinMaxResult as f, UpdateManyResult as g, SessionOptions as h, ConvenientTransactionOptions as i, GroupResult as l, ReadOptions as m, CacheOperationOptions as n, DeleteManyResult as o, OperationOptions as p, CacheableOptions as r, DeleteResult as s, AggregateOptions as t, LookupPopulateOptions as u, WithTransactionOptions as v, LookupBuilder as y };
@@ -1,4 +1,4 @@
1
- import { n as Repository } from "../Repository-BAzymuOz.mjs";
1
+ import { n as Repository } from "../Repository-BdSkQ8vc.mjs";
2
2
  import { Connection, Model } from "mongoose";
3
3
  //#region src/outbox/index.d.ts
4
4
  /** Delivery states. `dead_letter` is terminal and operator-visible. */
@@ -1,4 +1,4 @@
1
- import { n as Repository } from "../Repository-B_1y9cCf.mjs";
1
+ import { n as Repository } from "../Repository-BArvU32x.mjs";
2
2
  import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-DblM7V6j.mjs";
3
3
  import { Schema } from "mongoose";
4
4
  //#region src/outbox/index.ts
@@ -1,2 +1,2 @@
1
- import { t as PaginationEngine } from "../PaginationEngine-CSh-P8Nh.mjs";
1
+ import { t as PaginationEngine } from "../PaginationEngine-GdyX29tE.mjs";
2
2
  export { PaginationEngine };
@@ -1,5 +1,5 @@
1
1
  import { a as PopulateSpec, i as ObjectId, l as SortSpec, s as SelectSpec } from "./core-62oo3N1C.mjs";
2
- import { h as RepositoryInstance, p as RepositoryContext } from "./repository-Q16ErTu8.mjs";
2
+ import { h as RepositoryInstance, p as RepositoryContext } from "./repository-H_ffGgpE.mjs";
3
3
  import { OffsetPaginationResult } from "@classytic/repo-core/pagination";
4
4
  //#region src/types/plugin-options.d.ts
5
5
  /** Field preset configuration (field-filter plugin) */
@@ -111,6 +111,27 @@ interface CascadeRelation {
111
111
  foreignKey: string;
112
112
  /** Whether to use soft delete if available (default: follows parent behavior) */
113
113
  softDelete?: boolean;
114
+ /**
115
+ * What happens to the referencing documents when the parent is deleted.
116
+ *
117
+ * MongoDB has no foreign keys, so this is the application-level equivalent of an
118
+ * RDBMS `ON DELETE` clause. Odoo — which hard-deletes almost everything — relies on
119
+ * Postgres `ondelete='restrict'` in 31 places in its accounting module alone; that
120
+ * enforcement is what lets it skip soft delete entirely. Without an equivalent here, a
121
+ * hard delete leaves dangling references that surface later as reports with missing
122
+ * names, and the usual workaround is to soft-delete the parent forever — which hides
123
+ * the row rather than refusing the operation.
124
+ *
125
+ * - `cascade` (DEFAULT, and the pre-existing behaviour) — delete the children too.
126
+ * - `restrict` — REFUSE the parent delete while any reference remains. Runs
127
+ * `before:delete`, because refusing after the parent is gone is not a refusal.
128
+ * - `detach` — clear the reference (`$unset`) and keep the child. The RDBMS
129
+ * `SET NULL`; only valid where the field is optional.
130
+ *
131
+ * `restrict` and `detach` issue a query per relation on delete, so `foreignKey`
132
+ * SHOULD be indexed — Mongo gives no index for free the way a FK constraint does.
133
+ */
134
+ onDelete?: 'cascade' | 'restrict' | 'detach';
114
135
  }
115
136
  /** Cascade delete plugin options */
116
137
  interface CascadeOptions {
@@ -118,6 +139,15 @@ interface CascadeOptions {
118
139
  relations: CascadeRelation[];
119
140
  /** Run cascade deletes in parallel (default: true) */
120
141
  parallel?: boolean;
142
+ /**
143
+ * Upper bound on every `$in` the bulk (`deleteMany`) path issues — restrict counts,
144
+ * detach `$unset`s and cascade deletes are all sliced to at most this many parent ids
145
+ * per query. Bounds the query-document size (Mongo rejects documents past 16 MB, which
146
+ * an unsliced `$in` reaches around a million ObjectIds) and the per-call working set.
147
+ * Default 10_000 (`DEFAULT_ID_CHUNK`); see `utils/id-chunks.ts` for why this is larger
148
+ * than `runChunkedPurge`'s 1000.
149
+ */
150
+ batchSize?: number;
121
151
  /** Logger for cascade operations */
122
152
  logger?: Logger;
123
153
  }
@@ -1,6 +1,6 @@
1
- import { S as RepositoryPluginName, b as MONGOKIT_PLUGIN_NAMES, l as Plugin, x as MongoKitPluginName } from "../repository-Q16ErTu8.mjs";
2
- import { A as ensureAuditTrailReady, C as AuditTrailMethods, D as AuditTrailQueryOptions, E as AuditTrailQuery, O as EnsureAuditTrailReadyOptions, S as AuditQueryResult, T as AuditTrailOptions, _ as RepositoryCachePluginOptions, a as DateSequentialIdOptions, b as AuditOperation, c as SequentialIdOptions, d as getNextSequence, f as prefixedId, g as RepositoryCacheHandle, h as CacheOptions, i as CustomIdOptions, k as auditTrailPlugin, l as customIdPlugin, m as CacheAdapter, o as IdGenerator, p as sequentialId, r as multiTenantPlugin, s as PrefixedIdOptions, t as MultiTenantOptions, u as dateSequentialId, v as cachePlugin, w as AuditTrailMode, x as AuditQueryOptions, y as AuditEntry } from "../multi-tenant.plugin-2VtSSm1Y.mjs";
3
- import { A as IdempotencyStoreMethods, B as auditLogPlugin, C as LeaseMethods, D as ImmutableStatesPluginOptions, E as ImmutableClaimView, F as elasticSearchPlugin, H as appendOnlyPlugin, I as cascadePlugin, L as BatchOperationsMethods, M as idempotencyStorePlugin, N as fieldFilterPlugin, O as immutableStatesPlugin, P as ElasticSearchOptions, S as methodRegistryPlugin, T as leasePlugin, U as AggregateHelpersMethods, V as AppendOnlyPluginOptions, W as aggregateHelpersPlugin, _ as OperationMetric, a as requireField, b as mongoOperationsPlugin, c as timestampPlugin, d as createTenantContext, f as SubdocumentMethods, g as ObservabilityOptions, h as softDeletePlugin, i as immutableField, j as IdempotencyStorePluginOptions, k as IdempotencyClaim, l as TenantContext, m as SoftDeleteMethods, n as autoInject, o as uniqueField, p as subdocumentPlugin, r as blockIf, s as validationChainPlugin, t as OperationType, u as TenantStore, v as observabilityPlugin, w as LeasePluginOptions, x as MethodRegistryRepository, y as MongoOperationsMethods, z as batchOperationsPlugin } from "../validation-chain.plugin-BSshLJTA.mjs";
1
+ import { S as RepositoryPluginName, b as MONGOKIT_PLUGIN_NAMES, l as Plugin, x as MongoKitPluginName } from "../repository-H_ffGgpE.mjs";
2
+ import { A as ensureAuditTrailReady, C as AuditTrailMethods, D as AuditTrailQueryOptions, E as AuditTrailQuery, O as EnsureAuditTrailReadyOptions, S as AuditQueryResult, T as AuditTrailOptions, _ as RepositoryCachePluginOptions, a as DateSequentialIdOptions, b as AuditOperation, c as SequentialIdOptions, d as getNextSequence, f as prefixedId, g as RepositoryCacheHandle, h as CacheOptions, i as CustomIdOptions, k as auditTrailPlugin, l as customIdPlugin, m as CacheAdapter, o as IdGenerator, p as sequentialId, r as multiTenantPlugin, s as PrefixedIdOptions, t as MultiTenantOptions, u as dateSequentialId, v as cachePlugin, w as AuditTrailMode, x as AuditQueryOptions, y as AuditEntry } from "../multi-tenant.plugin-BwSvTirB.mjs";
3
+ import { A as IdempotencyStoreMethods, B as auditLogPlugin, C as LeaseMethods, D as ImmutableStatesPluginOptions, E as ImmutableClaimView, F as elasticSearchPlugin, H as appendOnlyPlugin, I as cascadePlugin, L as BatchOperationsMethods, M as idempotencyStorePlugin, N as fieldFilterPlugin, O as immutableStatesPlugin, P as ElasticSearchOptions, S as methodRegistryPlugin, T as leasePlugin, U as AggregateHelpersMethods, V as AppendOnlyPluginOptions, W as aggregateHelpersPlugin, _ as OperationMetric, a as requireField, b as mongoOperationsPlugin, c as timestampPlugin, d as createTenantContext, f as SubdocumentMethods, g as ObservabilityOptions, h as softDeletePlugin, i as immutableField, j as IdempotencyStorePluginOptions, k as IdempotencyClaim, l as TenantContext, m as SoftDeleteMethods, n as autoInject, o as uniqueField, p as subdocumentPlugin, r as blockIf, s as validationChainPlugin, t as OperationType, u as TenantStore, v as observabilityPlugin, w as LeasePluginOptions, x as MethodRegistryRepository, y as MongoOperationsMethods, z as batchOperationsPlugin } from "../validation-chain.plugin-yQRob3Av.mjs";
4
4
  import { ChangeLogStore } from "@classytic/repo-core/sync";
5
5
  //#region src/plugins/change-log.plugin.d.ts
6
6
  interface ChangeLogPluginOptions {