@classytic/mongokit 3.36.0 → 3.39.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 (47) hide show
  1. package/dist/{PaginationEngine-CBS1XGzp.d.mts → PaginationEngine-D-P19Y9m.d.mts} +34 -9
  2. package/dist/PaginationEngine-DQ7beTCR.mjs +1182 -0
  3. package/dist/{Repository-BArvU32x.mjs → Repository-D7fDpyFx.mjs} +405 -37
  4. package/dist/{Repository-BdSkQ8vc.d.mts → Repository-IxI983_m.d.mts} +55 -8
  5. package/dist/actions/index.d.mts +1 -1
  6. package/dist/actions/index.mjs +1 -1
  7. package/dist/adapter/index.d.mts +21 -0
  8. package/dist/adapter/index.mjs +1 -1
  9. package/dist/{adapter-Bfqz8yHE.mjs → adapter-BrVfPFSW.mjs} +45 -1
  10. package/dist/ai/index.d.mts +1 -1
  11. package/dist/better-auth/index.d.mts +78 -1
  12. package/dist/better-auth/index.mjs +233 -3
  13. package/dist/{create-repository-BU4x9Kb0.d.mts → create-repository-CRXjDa3N.d.mts} +5 -5
  14. package/dist/{create-repository-CPNbktFG.mjs → create-repository-DOe9E_5F.mjs} +3 -3
  15. package/dist/{index-DKEql4cL.d.mts → index-CmjNyulu.d.mts} +67 -8
  16. package/dist/index.d.mts +77 -13
  17. package/dist/index.mjs +31 -8
  18. package/dist/indexes/verify-indexes.d.mts +69 -0
  19. package/dist/indexes/verify-indexes.mjs +76 -0
  20. package/dist/{method-registry.plugin-DblM7V6j.mjs → method-registry.plugin-C63pGn1d.mjs} +54 -4
  21. package/dist/{multi-tenant.plugin-BwSvTirB.d.mts → multi-tenant.plugin-B-tzNeYL.d.mts} +60 -4
  22. package/dist/{operations-DZodvjC-.d.mts → operations-CrRIoc8Z.d.mts} +22 -7
  23. package/dist/outbox/index.d.mts +1 -1
  24. package/dist/outbox/index.mjs +2 -2
  25. package/dist/pagination/PaginationEngine.d.mts +1 -1
  26. package/dist/pagination/PaginationEngine.mjs +1 -1
  27. package/dist/pagination/utils/limits.d.mts +47 -0
  28. package/dist/pagination/utils/limits.mjs +85 -0
  29. package/dist/pagination-BpIv2bdS.d.mts +364 -0
  30. package/dist/{plugin-options-DXnlKM3B.d.mts → plugin-options-BuvtOSZf.d.mts} +2 -2
  31. package/dist/plugins/index.d.mts +3 -3
  32. package/dist/plugins/index.mjs +3 -3
  33. package/dist/{repository-H_ffGgpE.d.mts → repository-ZbLtL7Nk.d.mts} +30 -4
  34. package/dist/tenant/index.d.mts +89 -0
  35. package/dist/tenant/index.mjs +94 -0
  36. package/dist/testkit/index.d.mts +2 -2
  37. package/dist/testkit/index.mjs +1 -1
  38. package/dist/{timestamp.plugin-BEc6TANm.mjs → timestamp.plugin-B5eiaItQ.mjs} +75 -8
  39. package/dist/{to-plain-Dyz_qvuo.d.mts → to-plain-B8ufk3Qz.d.mts} +2 -2
  40. package/dist/{update-jSXp3nu3.mjs → update-c_01A0if.mjs} +126 -73
  41. package/dist/utils/index.d.mts +1 -1
  42. package/dist/{validation-chain.plugin-CyK1s9Ze.mjs → validation-chain.plugin-Bju1D1Sn.mjs} +2 -2
  43. package/dist/{validation-chain.plugin-yQRob3Av.d.mts → validation-chain.plugin-DvrLJIbJ.d.mts} +3 -19
  44. package/package.json +15 -3
  45. package/dist/PaginationEngine-GdyX29tE.mjs +0 -797
  46. package/dist/core-62oo3N1C.d.mts +0 -50
  47. package/dist/pagination-jSUdrACK.d.mts +0 -158
@@ -1,5 +1,5 @@
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
- import { a as PopulateSpec, i as ObjectId, l as SortSpec, o as ReadPreferenceType, s as SelectSpec, t as AnyDocument } from "./core-62oo3N1C.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-CrRIoc8Z.mjs";
2
+ import { _ as PopulateSpec, g as ObjectId, p as AnyDocument, v as ReadPreferenceType, x as SortSpec, y as SelectSpec } from "./pagination-BpIv2bdS.mjs";
3
3
  import { p as UpdateWithValidationResult } from "./type-utils-CLzRFtIA.mjs";
4
4
  import { Model, PipelineStage } from "mongoose";
5
5
  declare namespace aggregate_d_exports {
@@ -12,9 +12,18 @@ declare function aggregate<TResult = unknown>(Model: Model<any>, pipeline: Pipel
12
12
  session?: unknown;
13
13
  }): Promise<TResult[]>;
14
14
  /**
15
- * Aggregate with pagination using native MongoDB $facet
16
- * WARNING: $facet results must be <16MB. For larger results (limit >1000),
17
- * consider using Repository.aggregatePaginate() or splitting into separate queries.
15
+ * Aggregate with pagination.
16
+ *
17
+ * Page and count run as TWO concurrent pipelines rather than one `$facet`.
18
+ * `$facet` bundles every returned document plus the count into a SINGLE output
19
+ * document, and no stage may exceed 16MB — so a page of large documents failed
20
+ * with `BSONObjectTooLarge` instead of paginating, and the only defence was a
21
+ * warning above an arbitrary limit of 1000 that told the caller to go
22
+ * elsewhere. `$facet` also cannot use an index for the stages inside it.
23
+ *
24
+ * The trade: two reads, so a concurrent write between them can leave `total`
25
+ * a document out of step with `data` — the same trade `PaginationEngine`
26
+ * already makes on both its offset and aggregate paths.
18
27
  */
19
28
  declare function aggregatePaginate<TDoc = AnyDocument>(Model: Model<TDoc>, pipeline: PipelineStage[], options?: {
20
29
  page?: number;
@@ -91,14 +100,64 @@ declare function minMax(Model: Model<any>, field: string, query?: Record<string,
91
100
  session?: unknown;
92
101
  }): Promise<MinMaxResult>;
93
102
  declare namespace create_d_exports {
94
- export { create, createDefault, createMany, upsert };
103
+ export { CreateManyFailure, CreateManyPartial, CreateManyPartialError, create, createDefault, createMany, isCreateManyPartialError, upsert };
95
104
  }
96
105
  /**
97
106
  * Create single document
98
107
  */
99
108
  declare function create<TDoc = AnyDocument>(Model: Model<TDoc>, data: Record<string, unknown>, options?: CreateOptions): Promise<TDoc>;
100
109
  /**
101
- * Create multiple documents
110
+ * One rejected input, keyed by its position in the array that was passed in.
111
+ *
112
+ * The index is the load-bearing part: it is the only way to tie a failure back
113
+ * to the caller's own record — the driver reports failures positionally, and a
114
+ * rejected document never comes back with an `_id` to match on.
115
+ */
116
+ interface CreateManyFailure {
117
+ /** Position in the ORIGINAL `dataArray`. */
118
+ index: number;
119
+ /** The input that was rejected, as supplied. */
120
+ doc: Record<string, unknown>;
121
+ /** MongoDB error code, e.g. 11000 for a duplicate key. */
122
+ code?: number;
123
+ message: string;
124
+ }
125
+ /**
126
+ * What a partially-successful `createMany` actually did.
127
+ *
128
+ * Attached to the thrown error as `partial`. `createMany` still REJECTS — that
129
+ * is unchanged and deliberate — but the rejection now says which documents
130
+ * landed and which did not, instead of leaving the caller to dig through a
131
+ * driver error to find out.
132
+ */
133
+ interface CreateManyPartial<TDoc = AnyDocument> {
134
+ /** Documents the server accepted. */
135
+ inserted: TDoc[];
136
+ /** Every rejected input, in input order. */
137
+ failed: CreateManyFailure[];
138
+ }
139
+ /** The error `createMany` throws when some documents landed and some did not. */
140
+ type CreateManyPartialError<TDoc = AnyDocument> = Error & {
141
+ partial?: CreateManyPartial<TDoc>;
142
+ };
143
+ /** Narrow a caught error to one carrying a partial-write report. */
144
+ declare function isCreateManyPartialError<TDoc = AnyDocument>(err: unknown): err is CreateManyPartialError<TDoc> & {
145
+ partial: CreateManyPartial<TDoc>;
146
+ };
147
+ /**
148
+ * Create multiple documents.
149
+ *
150
+ * **A rejection does NOT mean nothing was written.** Neither ordering does:
151
+ * `ordered: true` stops at the first failure and keeps everything before it,
152
+ * `ordered: false` keeps everything valid. Measured on a six-document batch
153
+ * with a conflict in the middle — ordered wrote 3, unordered wrote 5. Only a
154
+ * transaction gives all-or-nothing.
155
+ *
156
+ * So the rejection carries a `partial` report naming what landed and what did
157
+ * not, because the alternative is a caller retrying the whole batch and
158
+ * double-writing everything that had already succeeded. Use it to retry only
159
+ * `failed`, or to reconcile — and prefer a unique index or an idempotency key
160
+ * for anything that will be retried automatically.
102
161
  */
103
162
  declare function createMany<TDoc = AnyDocument>(Model: Model<TDoc>, dataArray: Record<string, unknown>[], options?: CreateOptions): Promise<TDoc[]>;
104
163
  /**
@@ -301,4 +360,4 @@ declare namespace index_d_exports {
301
360
  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 };
302
361
  }
303
362
  //#endregion
304
- export { create_d_exports as a, delete_d_exports as i, update_d_exports as n, aggregate_d_exports as o, read_d_exports as r, index_d_exports as t };
363
+ export { CreateManyFailure as a, create_d_exports as c, delete_d_exports as i, isCreateManyPartialError as l, update_d_exports as n, CreateManyPartial as o, read_d_exports as r, CreateManyPartialError as s, index_d_exports as t, aggregate_d_exports as u };
package/dist/index.d.mts CHANGED
@@ -1,18 +1,17 @@
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
- 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
- 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-DKEql4cL.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-CrRIoc8Z.mjs";
2
+ import { _ as PopulateSpec, a as CursorPayload, b as SortDirection, c as MongokitPageExtras, d as ValueType, f as CursorSecret, g as ObjectId, i as CountStrategy, l as OffsetPaginationOptions, m as AnyModel, n as BasePaginationOptions, o as DecodedCursor, p as AnyDocument, r as CollationOptions, s as KeysetPaginationOptions, t as AggregatePaginationOptions, u as PaginationConfig, v as ReadPreferenceType, x as SortSpec$1, y as SelectSpec } from "./pagination-BpIv2bdS.mjs";
3
+ import { a as CreateManyFailure, l as isCreateManyPartialError, o as CreateManyPartial, s as CreateManyPartialError, t as index_d_exports } from "./index-CmjNyulu.mjs";
5
4
  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-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
- 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-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";
5
+ 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-ZbLtL7Nk.mjs";
6
+ 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-B-tzNeYL.mjs";
7
+ import { t as PaginationEngine } from "./PaginationEngine-D-P19Y9m.mjs";
8
+ 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-IxI983_m.mjs";
9
+ 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-BuvtOSZf.mjs";
10
+ import { n as CreateRepositoryConfig, r as createRepository, t as AuditConfig } from "./create-repository-CRXjDa3N.mjs";
11
+ 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-DvrLJIbJ.mjs";
13
12
  import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-BjdPX3YF.mjs";
14
13
  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-Dyz_qvuo.mjs";
14
+ 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-B8ufk3Qz.mjs";
16
15
  import { RepoCapabilities } from "@classytic/repo-core/repository";
17
16
  import { ClientSession, Connection, Model, PipelineStage, Schema } from "mongoose";
18
17
  //#region src/actions/cascade-references.d.ts
@@ -396,6 +395,71 @@ declare const READ_OPERATIONS: readonly RepositoryOperation[];
396
395
  /** Filter ops by their policy injection key. */
397
396
  declare function operationsByPolicyKey(key: PolicyKey): RepositoryOperation[];
398
397
  //#endregion
398
+ //#region src/pagination/defaults.d.ts
399
+ /**
400
+ * The subset of {@link PaginationConfig} that describes a deployment rather
401
+ * than one collection's shape.
402
+ *
403
+ * Deliberately NOT `defaultLimit` / `maxLimit` / `cursorVersion`: those are
404
+ * per-collection judgements (a line-item list and an audit log want different
405
+ * page sizes), and a process-wide override of them would be a surprise rather
406
+ * than a policy. What is here is the scale policy — how a page is walked, and
407
+ * whether the rows behind it are counted — plus the one SECURITY setting that
408
+ * is deployment-wide by nature: a signing key is one key for the process, and a
409
+ * repository that quietly declined to use it would be the hole.
410
+ */
411
+ interface PaginationDefaults {
412
+ /** See {@link PaginationConfig.defaultCountStrategy}. */
413
+ defaultCountStrategy?: CountStrategy;
414
+ /**
415
+ * See {@link PaginationConfig.defaultCountLimit}.
416
+ *
417
+ * Belongs to the deployment for the same reason the strategy does: it is the
418
+ * point past which this business stops caring about an exact row count, which
419
+ * is one answer for the whole process, not per collection.
420
+ */
421
+ defaultCountLimit?: number;
422
+ /**
423
+ * See {@link PaginationConfig.cursorSecret}.
424
+ *
425
+ * The reason this one MUST be reachable here: a kernel builds its own
426
+ * repositories, so without the policy seam a host could sign the cursors it
427
+ * mints itself while every engine-owned list kept issuing unsigned ones — a
428
+ * half-signed deployment, which is an unsigned deployment.
429
+ */
430
+ cursorSecret?: CursorSecret;
431
+ /** See {@link PaginationConfig.defaultMode}. */
432
+ defaultMode?: 'offset' | 'keyset';
433
+ /**
434
+ * See {@link PaginationConfig.maxPage}.
435
+ *
436
+ * The other half of the offset cost: `skip(n)` walks n index entries before
437
+ * returning anything, so an unbounded page number is an unbounded read. A
438
+ * fleet that has moved to keyset can lower this to make the offset path's
439
+ * remaining callers fail loudly instead of quietly costing a scan.
440
+ */
441
+ maxPage?: number;
442
+ }
443
+ /**
444
+ * Set the deployment's pagination policy. Merges into whatever is already set,
445
+ * so two call sites configuring different keys do not clobber each other.
446
+ *
447
+ * Call it at boot, before composing modules. Ordering is not load-bearing —
448
+ * repositories read this at query time, not construction time — but a call
449
+ * made after traffic starts changes behaviour mid-flight, which is rarely what
450
+ * anyone means.
451
+ */
452
+ declare function configurePaginationDefaults(defaults: PaginationDefaults): void;
453
+ /**
454
+ * The current deployment policy. Empty until something configures it.
455
+ *
456
+ * A COPY — `Readonly<T>` is erased at runtime, and handing out the live object
457
+ * would let a caller mutate the policy through a getter that promised not to.
458
+ */
459
+ declare function getPaginationDefaults(): Readonly<PaginationDefaults>;
460
+ /** Back to library defaults. For tests — a global that cannot be reset is a leak between them. */
461
+ declare function resetPaginationDefaults(): void;
462
+ //#endregion
399
463
  //#region src/query/parser/types.d.ts
400
464
  type SortSpec$2 = Record<string, 1 | -1>;
401
465
  type FilterQuery = Record<string, unknown>;
@@ -972,4 +1036,4 @@ declare function createOptionsExtractor<TCtx extends Record<string, unknown>>(fi
972
1036
  */
973
1037
  declare function systemContext(extra?: SessionOptions): SessionOptions;
974
1038
  //#endregion
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 };
1039
+ 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 CountStrategy, type CreateErrorOptions, type CreateInput, type CreateManyFailure, type CreateManyPartial, type CreateManyPartialError, type CreateOptions, type CreateRepositoryConfig, type CrudSchemasFramework, type CursorPayload, type CursorSecret, 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 MongokitPageExtras, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OperationDescriptor, type OperationMetric, type OperationOptions, type OperationType, type PaginationConfig, type PaginationDefaults, 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, configurePaginationDefaults, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, defineModel, defineModels, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, getPaginationDefaults, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isCreateManyPartialError, isDuplicateKeyError, isModelRegistered, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, probeMongoCapabilities, repoOptionsFromCtx, requireField, resetCapabilityProbeCache, resetPaginationDefaults, 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-BArvU32x.mjs";
3
- import { b as LookupBuilder, x as compileFilterToMongo } from "./update-jSXp3nu3.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-D7fDpyFx.mjs";
3
+ import { _ as compileFilterToMongo, g as LookupBuilder, x as isCreateManyPartialError } from "./update-c_01A0if.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-BEc6TANm.mjs";
8
- import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "./method-registry.plugin-DblM7V6j.mjs";
9
- import { t as PaginationEngine } from "./PaginationEngine-GdyX29tE.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-B5eiaItQ.mjs";
8
+ import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "./method-registry.plugin-C63pGn1d.mjs";
9
+ import { a as getPaginationDefaults, i as configurePaginationDefaults, o as resetPaginationDefaults, t as PaginationEngine } from "./PaginationEngine-DQ7beTCR.mjs";
10
10
  import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
11
- import { t as createRepository } from "./create-repository-CPNbktFG.mjs";
11
+ import { t as createRepository } from "./create-repository-DOe9E_5F.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-CyK1s9Ze.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-Bju1D1Sn.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";
@@ -509,6 +509,11 @@ function parseFilters(rt, filters, depth = 0) {
509
509
  rt.reject(`Blocked filter field not in allowlist: ${baseField}`, { field: baseField });
510
510
  continue;
511
511
  }
512
+ if (key === "$and") {
513
+ const branches = parseCompoundBranches(rt, value, depth);
514
+ if (branches.length > 0) parsedFilters.$and = branches;
515
+ continue;
516
+ }
512
517
  if (operatorMatch) {
513
518
  const [, , operator] = operatorMatch;
514
519
  if (rt.dangerousOperators.includes(`$${operator}`)) {
@@ -707,6 +712,24 @@ function handleBracketSyntax(rt, field, operators, parsedFilters, depth = 0) {
707
712
  if (typeof parsedFilters[field] === "object" && Object.keys(parsedFilters[field]).length === 0) delete parsedFilters[field];
708
713
  }
709
714
  /** Parse `?or=[...]` / `?OR=[...]` / `?$or=[...]` groups into an $or array. */
715
+ /**
716
+ * Parse the branches of a compound operator (`$and`, and the body of `$or`).
717
+ *
718
+ * Every branch goes back through `parseFilters`, so the operator block, the allowlist and the
719
+ * regex budget all apply inside it. Branches that parse to `{}` are DROPPED: a branch holding
720
+ * only a blocked operator would otherwise become match-all, which widens the query instead of
721
+ * failing it — the exact shape `parseOr` documents below.
722
+ */
723
+ function parseCompoundBranches(rt, raw, depth) {
724
+ const items = Array.isArray(raw) ? raw : typeof raw === "object" && raw ? Object.values(raw) : [];
725
+ const branches = [];
726
+ for (const item of items) {
727
+ if (typeof item !== "object" || !item) continue;
728
+ const parsed = parseFilters(rt, item, depth + 1);
729
+ if (Object.keys(parsed).length > 0) branches.push(parsed);
730
+ }
731
+ return branches;
732
+ }
710
733
  function parseOr(rt, query) {
711
734
  const orArray = [];
712
735
  const raw = query?.or || query?.OR || query?.$or;
@@ -1758,4 +1781,4 @@ function systemContext(extra) {
1758
1781
  */
1759
1782
  var src_default = Repository;
1760
1783
  //#endregion
1761
- export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, ModelCollisionError, OP_REGISTRY, PaginationEngine, QueryParser, READ_OPERATIONS, Repository, actions_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, src_default as default, 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 };
1784
+ export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, ModelCollisionError, OP_REGISTRY, PaginationEngine, QueryParser, READ_OPERATIONS, Repository, actions_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, capabilitiesForSupport, cascadePlugin, cascadePurgeReferences, classifyHelloReply, compileFilterToMongo, configureLogger, configurePaginationDefaults, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, src_default as default, defineModel, defineModels, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, getPaginationDefaults, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isCreateManyPartialError, isDuplicateKeyError, isModelRegistered, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, probeMongoCapabilities, repoOptionsFromCtx, requireField, resetCapabilityProbeCache, resetPaginationDefaults, resolveMongoCapabilities, resolveTransactionSupport, sequentialId, softDeletePlugin, subdocumentPlugin, supportsTransactions, systemContext, timestampPlugin, toPlain, transactionResolutionOf, uniqueField, validationChainPlugin, withTransaction };
@@ -0,0 +1,69 @@
1
+ //#region src/indexes/verify-indexes.d.ts
2
+ /**
3
+ * Compare DECLARED indexes against what a database actually has.
4
+ *
5
+ * `unique: true` on a schema is a correctness constraint only if the index was
6
+ * built. A skipped deploy step is otherwise SILENT — one deployment ran with
7
+ * 401 declared indexes absent, accumulated duplicate rows on a "unique" field,
8
+ * and nothing anywhere said the constraint did not exist. mongokit owns the
9
+ * declaration, so it owns the means to check it.
10
+ *
11
+ * Read-only. It NEVER builds: index builds belong to a deploy step, not to a
12
+ * serving process.
13
+ *
14
+ * Missing UNIQUE indexes are reported separately from missing regular ones —
15
+ * the first silently admits duplicate data, the second only costs a scan.
16
+ *
17
+ * DELIBERATELY ONE-DIRECTIONAL: an index present in the database but absent
18
+ * from a schema is NOT drift. Auth libraries and migrations manage indexes
19
+ * outside the ODM, and treating those as removable is exactly what makes a
20
+ * `syncIndexes()` unsafe.
21
+ *
22
+ * Sibling of `query/primitives/indexes`, which reads DECLARATIONS only and
23
+ * touches no connection. This module is the live comparison.
24
+ */
25
+ /** The slice of a Mongoose-like connection this needs. Structural on purpose. */
26
+ interface IndexVerifiableConnection {
27
+ modelNames(): string[];
28
+ model(name: string): IndexVerifiableModel;
29
+ }
30
+ interface IndexVerifiableModel {
31
+ schema: {
32
+ indexes(): Array<[Record<string, unknown>, Record<string, unknown>?]>;
33
+ };
34
+ collection: {
35
+ name: string;
36
+ listIndexes(): {
37
+ toArray(): Promise<Array<Record<string, unknown>>>;
38
+ };
39
+ };
40
+ }
41
+ interface MissingIndex {
42
+ model: string;
43
+ collection: string;
44
+ /** The declared key, JSON-stringified — stable to log and to diff. */
45
+ key: string;
46
+ }
47
+ interface IndexVerifyReport {
48
+ modelsChecked: number;
49
+ /** Declared `unique` indexes with nothing behind them. Duplicates can already exist. */
50
+ missingUnique: MissingIndex[];
51
+ missingRegular: MissingIndex[];
52
+ /** Models whose indexes could not be read — collection absent, or no permission. */
53
+ unreadable: string[];
54
+ }
55
+ interface VerifyIndexesOptions {
56
+ /**
57
+ * Include a model in the sweep. Default: every model.
58
+ *
59
+ * A kernel that materialises different model sets per mode must skip exactly
60
+ * the set its deploy step skips — indexing a model CREATES its collection, so
61
+ * a mismatch here reports permanent phantom drift instead of a real gap.
62
+ */
63
+ includeModel?: (modelName: string) => boolean;
64
+ }
65
+ declare function verifyIndexes(connection: IndexVerifiableConnection, options?: VerifyIndexesOptions): Promise<IndexVerifyReport>;
66
+ /** One-line summary for a boot log. */
67
+ declare function formatIndexReport(report: IndexVerifyReport): string;
68
+ //#endregion
69
+ export { IndexVerifiableConnection, IndexVerifiableModel, IndexVerifyReport, MissingIndex, VerifyIndexesOptions, formatIndexReport, verifyIndexes };
@@ -0,0 +1,76 @@
1
+ //#region src/indexes/verify-indexes.ts
2
+ /**
3
+ * Order-sensitive: a compound index is not the same index with keys swapped.
4
+ *
5
+ * A SINGLE-field numeric index is direction-agnostic though — mongo traverses
6
+ * an index either way, so `{t:1}` and `{t:-1}` are the same index and a schema
7
+ * declaring one against a database holding the other is not drift. Only numeric
8
+ * directions collapse; `2dsphere` / `hashed` are index TYPES, not directions.
9
+ */
10
+ function normalizeKey(key) {
11
+ const entries = Object.entries(key);
12
+ if (entries.length === 1 && typeof entries[0]?.[1] === "number") return JSON.stringify([[entries[0][0]]]);
13
+ return JSON.stringify(entries);
14
+ }
15
+ /**
16
+ * A TEXT index is stored under a key mongo invents (`{_fts, _ftsx}`) with the
17
+ * real fields moved to `weights`, so it never matches its own declaration by
18
+ * key. Compare the field SET instead, or every text index reads as permanently
19
+ * missing and the whole check becomes noise nobody reads.
20
+ */
21
+ function textFields(key) {
22
+ const fields = Object.entries(key).filter(([, dir]) => dir === "text").map(([field]) => field);
23
+ return fields.length > 0 ? fields.sort() : null;
24
+ }
25
+ async function verifyIndexes(connection, options = {}) {
26
+ const include = options.includeModel ?? (() => true);
27
+ const report = {
28
+ modelsChecked: 0,
29
+ missingUnique: [],
30
+ missingRegular: [],
31
+ unreadable: []
32
+ };
33
+ for (const name of connection.modelNames().sort()) {
34
+ if (!include(name)) continue;
35
+ const model = connection.model(name);
36
+ const declared = model.schema.indexes();
37
+ if (declared.length === 0) continue;
38
+ let existing;
39
+ let existingText;
40
+ try {
41
+ const live = await model.collection.listIndexes().toArray();
42
+ existing = new Set(live.map((i) => normalizeKey(i.key)));
43
+ existingText = new Set(live.filter((i) => i.weights).map((i) => JSON.stringify(Object.keys(i.weights).sort())));
44
+ } catch {
45
+ report.unreadable.push(name);
46
+ continue;
47
+ }
48
+ report.modelsChecked += 1;
49
+ for (const [key, indexOptions] of declared) {
50
+ const asText = textFields(key);
51
+ if (asText) {
52
+ if (existingText.has(JSON.stringify(asText))) continue;
53
+ } else if (existing.has(normalizeKey(key))) continue;
54
+ const entry = {
55
+ model: name,
56
+ collection: model.collection.name,
57
+ key: JSON.stringify(key)
58
+ };
59
+ if (indexOptions?.unique) report.missingUnique.push(entry);
60
+ else report.missingRegular.push(entry);
61
+ }
62
+ }
63
+ return report;
64
+ }
65
+ /** One-line summary for a boot log. */
66
+ function formatIndexReport(report) {
67
+ const parts = [
68
+ `${report.modelsChecked} models checked`,
69
+ `missing unique: ${report.missingUnique.length}`,
70
+ `missing regular: ${report.missingRegular.length}`
71
+ ];
72
+ if (report.unreadable.length > 0) parts.push(`unreadable: ${report.unreadable.length}`);
73
+ return `[index-check] ${parts.join(" — ")}`;
74
+ }
75
+ //#endregion
76
+ export { formatIndexReport, verifyIndexes };
@@ -16,6 +16,58 @@ import { t as createError } from "./error-BuByZwKU.mjs";
16
16
  * // bulkWrite is what this plugin contributes.
17
17
  * await repo.bulkWrite([...ops]);
18
18
  */
19
+ /**
20
+ * Driver options `bulkWrite` forwards, and the only ones it forwards.
21
+ *
22
+ * The signature used to be `{ session?, ordered?, [key: string]: unknown }` and
23
+ * the call forwarded exactly two of them. So `writeConcern: { w: 'majority' }`
24
+ * on a money import was accepted by the type, dropped on the floor, and the
25
+ * write went out under the default concern with nothing raised.
26
+ */
27
+ const BULK_WRITE_OPTIONS = [
28
+ "ordered",
29
+ "session",
30
+ "writeConcern",
31
+ "bypassDocumentValidation",
32
+ "comment",
33
+ "let",
34
+ "ignoreUndefined"
35
+ ];
36
+ /**
37
+ * Driver options that EXIST but this method does not forward.
38
+ *
39
+ * The options bag is dual-purpose by design: it is spread into
40
+ * `_buildContext`, so it also carries plugin context — `organizationId` for
41
+ * multi-tenant scoping, and whatever else a host's plugins read off it.
42
+ * Refusing every unrecognised key therefore breaks legitimate callers, which
43
+ * is exactly how the first version of this fence failed: it rejected the
44
+ * tenant id and took two passing tests with it.
45
+ *
46
+ * So the line sits where the caller's expectation is. If the DRIVER would
47
+ * understand the option and we would silently not pass it on, refuse — that is
48
+ * the case where someone believes they configured the write. A key the driver
49
+ * does not know either is plugin context, and passes through untouched.
50
+ */
51
+ const UNSUPPORTED_DRIVER_OPTIONS = /* @__PURE__ */ new Set([
52
+ "forceServerObjectId",
53
+ "timeoutMS",
54
+ "maxTimeMS",
55
+ "retryWrites",
56
+ "checkKeys",
57
+ "serializeFunctions",
58
+ "enableUtf8Validation",
59
+ "raw",
60
+ "explain"
61
+ ]);
62
+ /** Narrow the bag to driver options, refusing the ones we would silently drop. */
63
+ function pickBulkWriteOptions(options) {
64
+ const dropped = Object.keys(options).filter((k) => UNSUPPORTED_DRIVER_OPTIONS.has(k));
65
+ if (dropped.length > 0) throw createError(400, `bulkWrite: option(s) ${dropped.map((k) => `'${k}'`).join(", ")} are MongoDB driver options this method does not forward. Refused rather than ignored, because silently dropping one changes what the write does. Supported: ${BULK_WRITE_OPTIONS.join(", ")}.`);
66
+ const out = {};
67
+ for (const key of BULK_WRITE_OPTIONS) if (options[key] !== void 0) out[key] = options[key];
68
+ out.ordered = options.ordered ?? true;
69
+ return out;
70
+ }
19
71
  function batchOperationsPlugin() {
20
72
  return {
21
73
  name: "batch-operations",
@@ -36,6 +88,7 @@ function batchOperationsPlugin() {
36
88
  * ]);
37
89
  */
38
90
  repo.registerMethod("bulkWrite", async function(operations, options = {}) {
91
+ const driverOptions = pickBulkWriteOptions(options);
39
92
  const context = await this._buildContext("bulkWrite", {
40
93
  operations,
41
94
  ...options
@@ -43,10 +96,7 @@ function batchOperationsPlugin() {
43
96
  try {
44
97
  const finalOps = context.operations || operations;
45
98
  if (!finalOps || finalOps.length === 0) throw createError(400, "bulkWrite requires at least one operation");
46
- const result = await this.Model.bulkWrite(finalOps, {
47
- ordered: options.ordered ?? true,
48
- session: options.session
49
- });
99
+ const result = await this.Model.bulkWrite(finalOps, driverOptions);
50
100
  const bulkResult = {
51
101
  ok: result.ok,
52
102
  insertedCount: result.insertedCount,
@@ -1,5 +1,5 @@
1
- import { i as ObjectId } from "./core-62oo3N1C.mjs";
2
- import { l as Plugin, p as RepositoryContext } from "./repository-H_ffGgpE.mjs";
1
+ import { g as ObjectId } from "./pagination-BpIv2bdS.mjs";
2
+ import { l as Plugin, p as RepositoryContext } from "./repository-ZbLtL7Nk.mjs";
3
3
  import mongoose, { Connection } from "mongoose";
4
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";
@@ -312,7 +312,10 @@ interface SequentialIdOptions {
312
312
  prefix: string;
313
313
  /** Mongoose model — used to derive the counter key from model name */
314
314
  model: mongoose.Model<any>;
315
- /** Number of digits to pad to (default: 4 → "0001") */
315
+ /**
316
+ * MINIMUM number of digits (default: 4 → "0001"). Never truncates —
317
+ * sequence 10000 under `padding: 4` renders as `INV-10000`.
318
+ */
316
319
  padding?: number;
317
320
  /** Separator between prefix and number (default: '-') */
318
321
  separator?: string;
@@ -346,10 +349,54 @@ interface DateSequentialIdOptions {
346
349
  * - 'daily' → BILL-2026-02-20-0001, resets every day
347
350
  */
348
351
  partition?: 'yearly' | 'monthly' | 'daily';
349
- /** Number of digits to pad to (default: 4) */
352
+ /**
353
+ * MINIMUM number of digits for the sequence (default: 4). `padStart` never
354
+ * truncates, so the 10,000th id of a period under `padding: 4` is
355
+ * `BILL-2026-10000` — it grows a digit rather than wrapping to `0000`.
356
+ * Lexical sort order therefore only holds while a period stays below
357
+ * 10^padding; size the padding for your peak period.
358
+ */
350
359
  padding?: number;
351
360
  /** Separator (default: '-') */
352
361
  separator?: string;
362
+ /**
363
+ * Counter scope.
364
+ * - `'global'` (default) — one counter per model + period, shared by
365
+ * every tenant in the database. Ids are unique across the deployment.
366
+ * - `'tenant'` — one counter per tenant + model + period, keyed by
367
+ * `context[tenantKey]`. Every tenant's numbering starts at 1 (Shopify-
368
+ * style per-shop order numbers) and the write hot spot on the single
369
+ * shared counter document splits per tenant. Ids are unique WITHIN a
370
+ * tenant only — index the field compound with the tenant field.
371
+ * Fails closed: a create with no tenant in context throws rather than
372
+ * falling back to the global key, which would silently merge two
373
+ * tenants' sequences.
374
+ */
375
+ scope?: 'global' | 'tenant';
376
+ /**
377
+ * Context key carrying the tenant id under `scope: 'tenant'` (default:
378
+ * `'organizationId'` — the same default as `multiTenantPlugin`'s
379
+ * `contextKey`, so `repo.create(data, { organizationId })` feeds both).
380
+ */
381
+ tenantKey?: string;
382
+ /**
383
+ * Which clock decides the period boundary.
384
+ *
385
+ * - `'local'` (default) — the server's `TZ`. Kept as the default because it
386
+ * is what every existing deployment already generated: flipping it would
387
+ * retroactively change which period an id near a month boundary belongs
388
+ * to, and those ids are invoice and order numbers.
389
+ * - `'utc'` — **the correct choice for more than one replica.** Two replicas
390
+ * with different `TZ` settings derive different counter keys for the same
391
+ * logical month and each increments its own, so the same number is issued
392
+ * twice inside one tenant-period. UTC is the one boundary every replica
393
+ * agrees on. It also matches the ecosystem's other period key,
394
+ * `usagePeriod()` in `@classytic/repo-core/usage`.
395
+ *
396
+ * Containers usually run UTC anyway, which is exactly why the difference
397
+ * stays invisible until one host is configured otherwise.
398
+ */
399
+ timezone?: 'local' | 'utc';
353
400
  }
354
401
  /**
355
402
  * Generator: Date-partitioned sequential counter.
@@ -371,6 +418,15 @@ interface DateSequentialIdOptions {
371
418
  * }),
372
419
  * })
373
420
  * ```
421
+ *
422
+ * @example Per-tenant numbering in a shared database
423
+ * ```typescript
424
+ * customIdPlugin({
425
+ * field: 'orderNumber',
426
+ * generator: dateSequentialId({ prefix: 'ORD', model: OrderModel, scope: 'tenant' }),
427
+ * })
428
+ * // org-a: ORD-2026-02-0001, ORD-2026-02-0002 … org-b: ORD-2026-02-0001 …
429
+ * ```
374
430
  */
375
431
  declare function dateSequentialId(options: DateSequentialIdOptions): IdGenerator;
376
432
  interface PrefixedIdOptions {