@classytic/mongokit 3.43.1 → 3.44.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 (92) hide show
  1. package/README.md +14 -0
  2. package/dist/{PaginationEngine-DZnkbSrr.mjs → PaginationEngine-C9iS0U5l.mjs} +13 -27
  3. package/dist/{PaginationEngine-CRHyuUtw.d.mts → PaginationEngine-D17AthKw.d.mts} +1 -3
  4. package/dist/{Repository-DnL4xk5w.d.mts → Repository-B6oL6OMv.d.mts} +33 -11
  5. package/dist/{Repository-Dj6xqPxJ.mjs → Repository-eMRYZ_2Y.mjs} +204 -92
  6. package/dist/actions/index.d.mts +1 -1
  7. package/dist/actions/index.mjs +2 -2
  8. package/dist/{actions-ChYfkPYP.mjs → actions-DExyXMTy.mjs} +3 -4
  9. package/dist/adapter/index.d.mts +6 -10
  10. package/dist/adapter/index.mjs +1 -1
  11. package/dist/{adapter-CkMoMF83.mjs → adapter-Qzgc3ZD_.mjs} +4 -6
  12. package/dist/ai/index.d.mts +21 -8
  13. package/dist/ai/index.mjs +15 -9
  14. package/dist/better-auth/index.d.mts +17 -19
  15. package/dist/better-auth/index.mjs +3 -4
  16. package/dist/{coercion-BjdPX3YF.d.mts → coercion-COp9DRNw.d.mts} +0 -2
  17. package/dist/{create-repository-CJtVdwig.d.mts → create-repository-BOFUYj1l.d.mts} +5 -7
  18. package/dist/{create-repository-UjwPWR1z.mjs → create-repository-D7MQpIZo.mjs} +4 -5
  19. package/dist/{derive-DRBiegRH.mjs → derive-Dxsc31fV.mjs} +3 -6
  20. package/dist/{error-BuByZwKU.mjs → error-DjdmPLWO.mjs} +1 -2
  21. package/dist/{field-selection-FSQFLpsU.mjs → field-selection-DPFaFw9Y.mjs} +1 -2
  22. package/dist/{id-chunks-BmAwsqNk.mjs → id-chunks-DH-RAZWG.mjs} +13 -8
  23. package/dist/{id-resolution-BqDrk0S2.mjs → id-resolution-Cqh49d9J.mjs} +1 -2
  24. package/dist/idempotency/index.d.mts +4 -7
  25. package/dist/idempotency/index.mjs +1 -2
  26. package/dist/{index-SYF8N28D.d.mts → index-8QUEsOFI.d.mts} +3 -4
  27. package/dist/{index-C0AjVqX3.d.mts → index-ehGZLbrd.d.mts} +2 -8
  28. package/dist/index.d.mts +63 -96
  29. package/dist/index.mjs +36 -52
  30. package/dist/indexes/verify-indexes.d.mts +8 -11
  31. package/dist/indexes/verify-indexes.mjs +1 -2
  32. package/dist/{indexes-DIjMpYES.d.mts → indexes-FfyOuhDK.d.mts} +0 -2
  33. package/dist/{join-scope-DSAznE8E.mjs → join-scope-C1gOtZ59.mjs} +2 -3
  34. package/dist/kernel-conformance/index.d.mts +39 -13
  35. package/dist/kernel-conformance/index.mjs +4 -7
  36. package/dist/lock/index.d.mts +134 -7
  37. package/dist/lock/index.mjs +349 -14
  38. package/dist/{logger-K7dPmu2U.mjs → logger-hbWOlj_X.mjs} +1 -2
  39. package/dist/{method-registry.plugin-C63pGn1d.mjs → method-registry.plugin-CGAepsBU.mjs} +3 -5
  40. package/dist/{model-registry-ClKtWiHa.mjs → model-registry-DrVfJ3Nt.mjs} +1 -2
  41. package/dist/{mongooseToJsonSchema-C3k4ide0.mjs → mongooseToJsonSchema-DMDotz4_.mjs} +2 -3
  42. package/dist/{multi-tenant.plugin-B_q9NUHc.d.mts → multi-tenant.plugin-DfeDMiI8.d.mts} +143 -11
  43. package/dist/{operations-XiFgG5US.d.mts → operations-Dw9OFV0V.d.mts} +14 -5
  44. package/dist/outbox/index.d.mts +24 -9
  45. package/dist/outbox/index.mjs +17 -6
  46. package/dist/pagination/PaginationEngine.d.mts +1 -1
  47. package/dist/pagination/PaginationEngine.mjs +1 -1
  48. package/dist/pagination/utils/limits.d.mts +6 -9
  49. package/dist/pagination/utils/limits.mjs +2 -3
  50. package/dist/{pagination-D6psses0.d.mts → pagination-DAUg5zOY.d.mts} +2 -10
  51. package/dist/{plugin-options-Cirmb4Kp.d.mts → plugin-options-Bpcuz9kS.d.mts} +2 -4
  52. package/dist/plugins/index.d.mts +6 -7
  53. package/dist/plugins/index.mjs +6 -7
  54. package/dist/query/primitives/coercion.d.mts +1 -1
  55. package/dist/query/primitives/coercion.mjs +2 -3
  56. package/dist/query/primitives/geo.d.mts +11 -14
  57. package/dist/query/primitives/geo.mjs +1 -2
  58. package/dist/query/primitives/indexes.d.mts +1 -1
  59. package/dist/query/primitives/indexes.mjs +1 -2
  60. package/dist/{query-defaults-BxUcSOoF.mjs → query-defaults-aUoKlEbh.mjs} +2 -3
  61. package/dist/read-model/index.d.mts +18 -21
  62. package/dist/read-model/index.mjs +3 -4
  63. package/dist/{repository-MqAP409e.d.mts → repository-DzVIwhU2.d.mts} +25 -29
  64. package/dist/retention-purge-DqcYvRnG.d.mts +35 -0
  65. package/dist/{rolldown-runtime-D7D4PA-g.mjs → rolldown-runtime-D8jQpDG7.mjs} +1 -2
  66. package/dist/search/index.d.mts +1 -1
  67. package/dist/search/index.mjs +1 -1
  68. package/dist/sync/index.d.mts +95 -15
  69. package/dist/sync/index.mjs +308 -62
  70. package/dist/tenant/index.d.mts +8 -11
  71. package/dist/tenant/index.mjs +1 -2
  72. package/dist/testkit/index.d.mts +179 -23
  73. package/dist/testkit/index.mjs +421 -23
  74. package/dist/{timestamp.plugin-DpOWavVI.mjs → timestamp.plugin-Bz_zL9PX.mjs} +192 -49
  75. package/dist/{to-plain-vksRrLtc.mjs → to-plain-CEKv_ylg.mjs} +2 -4
  76. package/dist/{to-plain-o5tUY2nV.d.mts → to-plain-PO5r1Qoy.d.mts} +2 -16
  77. package/dist/{transaction-DmiB4k2P.mjs → transaction-C2PFxfIv.mjs} +5 -8
  78. package/dist/{type-utils-CLzRFtIA.d.mts → type-utils-CKvuewAL.d.mts} +0 -2
  79. package/dist/{update-WgeAUJ_I.mjs → update-DcfmNbJe.mjs} +12 -20
  80. package/dist/usage/index.d.mts +2 -5
  81. package/dist/usage/index.mjs +8 -6
  82. package/dist/utils/index.d.mts +8 -10
  83. package/dist/utils/index.mjs +7 -7
  84. package/dist/{validation-chain.plugin-BAeC5eVT.d.mts → validation-chain.plugin-B3gZCZj4.d.mts} +135 -48
  85. package/dist/{validation-chain.plugin-DRApTmkN.mjs → validation-chain.plugin-DNY6MIF6.mjs} +207 -55
  86. package/llms.txt +33 -0
  87. package/package.json +29 -25
  88. package/skills/mongokit/SKILL.md +112 -0
  89. package/skills/mongokit/references/pagination-search.md +87 -0
  90. package/skills/mongokit/references/plugins-tenancy.md +102 -0
  91. package/skills/mongokit/references/repository-api.md +83 -0
  92. package/skills/mongokit/references/transactions-outbox.md +101 -0
@@ -1,19 +1,18 @@
1
- import { _ as getMongooseProjection, a as createMemoryCache, b as createError, c as IdType, d as isObjectIdInstance, f as isValidIdForType, g as getFieldsForUser, h as filterResponseData, i as buildCrudSchemasFromMongooseSchema, l as getSchemaIdType, m as createFieldPreset, n as CrudSchemasFramework, o as LoggerConfig, p as toObjectId, r as buildCrudSchemasFromModel, s as configureLogger, t as toPlain, v as CreateErrorOptions, y as ParseDuplicateKeyOptions } from "../to-plain-o5tUY2nV.mjs";
1
+ import { _ as getMongooseProjection, a as createMemoryCache, b as createError, c as IdType, d as isObjectIdInstance, f as isValidIdForType, g as getFieldsForUser, h as filterResponseData, i as buildCrudSchemasFromMongooseSchema, l as getSchemaIdType, m as createFieldPreset, n as CrudSchemasFramework, o as LoggerConfig, p as toObjectId, r as buildCrudSchemasFromModel, s as configureLogger, t as toPlain, v as CreateErrorOptions, y as ParseDuplicateKeyOptions } from "../to-plain-PO5r1Qoy.mjs";
2
2
  import { ClientSession, Model } from "mongoose";
3
- //#region src/utils/id-chunks.d.ts
4
3
  /**
5
4
  * Default slice for `$in` fan-out. Larger than `runChunkedPurge`'s 1000 on purpose:
6
5
  * a purge chunk bounds the WRITE LOCK SCOPE of each progressive strategy step, while a
7
6
  * snapshot slice bounds only the query-document size and the per-call working set —
8
7
  * 10k ObjectIds is ~120 KB of `$in`, far under every limit, at 10× fewer round trips.
9
8
  */
10
- declare const DEFAULT_ID_CHUNK = 10000;
9
+ export declare const DEFAULT_ID_CHUNK = 10000;
11
10
  /**
12
11
  * Collect the bare id values matching `filter`, streaming via a driver cursor so the
13
12
  * only O(n) allocation is the returned array itself (a `find().lean()` materializes an
14
13
  * intermediate array of `{_id}` documents first — roughly double the peak for large n).
15
14
  */
16
- declare function collectIds(model: Model<unknown>, filter: Record<string, unknown>, options?: {
15
+ export declare function collectIds(model: Model<unknown>, filter: Record<string, unknown>, options?: {
17
16
  session?: ClientSession | undefined;
18
17
  idField?: string;
19
18
  }): Promise<unknown[]>;
@@ -22,7 +21,7 @@ declare function collectIds(model: Model<unknown>, filter: Record<string, unknow
22
21
  * array. `size` must be a positive integer — sliced `$in` correctness depends on it,
23
22
  * so a nonsense size throws instead of silently degrading to one giant slice.
24
23
  */
25
- declare function idChunks<T>(ids: readonly T[], size: number): Generator<T[], void, undefined>;
24
+ export declare function idChunks<T>(ids: readonly T[], size: number): Generator<T[], void, undefined>;
26
25
  /**
27
26
  * Compose the keyset-progressed select filter: the base predicate, narrowed to rows past
28
27
  * the last committed `_id`. `cursor == null` means "from the start". Selection paired
@@ -30,7 +29,7 @@ declare function idChunks<T>(ids: readonly T[], size: number): Generator<T[], vo
30
29
  * never revisits a processed row and never skips an unprocessed one. Never mutates
31
30
  * `baseFilter`.
32
31
  */
33
- declare function keysetFilter(baseFilter: Record<string, unknown>, cursor: unknown): Record<string, unknown>;
32
+ export declare function keysetFilter(baseFilter: Record<string, unknown>, cursor: unknown): Record<string, unknown>;
34
33
  /**
35
34
  * The `_id`-ascending bounded chunk select both action ports share:
36
35
  * `find(filter).sort({_id: 1}).limit(limit).lean()`. Lean on purpose — chunk consumers
@@ -38,7 +37,7 @@ declare function keysetFilter(baseFilter: Record<string, unknown>, cursor: unkno
38
37
  * are needed (the purge port's common path); omit it for full docs (archive's readChunk,
39
38
  * anonymize-functional).
40
39
  */
41
- declare function selectKeysetChunk<T>(model: Model<T>, filter: Record<string, unknown>, limit: number, options?: {
40
+ export declare function selectKeysetChunk<T>(model: Model<T>, filter: Record<string, unknown>, limit: number, options?: {
42
41
  session?: ClientSession | undefined;
43
42
  projection?: Record<string, 1 | 0> | undefined;
44
43
  }): Promise<T[]>;
@@ -49,6 +48,5 @@ declare function selectKeysetChunk<T>(model: Model<T>, filter: Record<string, un
49
48
  * write — such a row is silently skipped instead of written outside its cohort. Both
50
49
  * action ports use this shape; use it for any select-then-write over a chunk.
51
50
  */
52
- declare function narrowToIds(baseFilter: Record<string, unknown>, ids: readonly unknown[]): Record<string, unknown>;
53
- //#endregion
54
- export { type CreateErrorOptions, type CrudSchemasFramework, DEFAULT_ID_CHUNK, type IdType, type LoggerConfig, type ParseDuplicateKeyOptions, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, collectIds, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getMongooseProjection, getSchemaIdType, idChunks, isObjectIdInstance, isValidIdForType, keysetFilter, narrowToIds, selectKeysetChunk, toObjectId, toPlain };
51
+ export declare function narrowToIds(baseFilter: Record<string, unknown>, ids: readonly unknown[]): Record<string, unknown>;
52
+ export { type CreateErrorOptions, type CrudSchemasFramework, type IdType, type LoggerConfig, type ParseDuplicateKeyOptions, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getMongooseProjection, getSchemaIdType, isObjectIdInstance, isValidIdForType, toObjectId, toPlain };
@@ -1,8 +1,8 @@
1
- import { a as toObjectId, i as isValidIdForType, r as isObjectIdInstance, t as getSchemaIdType } from "../id-resolution-BqDrk0S2.mjs";
2
- import { a as narrowToIds, i as keysetFilter, n as collectIds, o as selectKeysetChunk, r as idChunks, t as DEFAULT_ID_CHUNK } from "../id-chunks-BmAwsqNk.mjs";
3
- import { t as configureLogger } from "../logger-K7dPmu2U.mjs";
4
- import { t as createError } from "../error-BuByZwKU.mjs";
5
- import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "../field-selection-FSQFLpsU.mjs";
6
- import { n as createMemoryCache, t as toPlain } from "../to-plain-vksRrLtc.mjs";
7
- import { n as buildCrudSchemasFromMongooseSchema, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-C3k4ide0.mjs";
1
+ import { t as createError } from "../error-DjdmPLWO.mjs";
2
+ import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "../field-selection-DPFaFw9Y.mjs";
3
+ import { a as narrowToIds, i as keysetFilter, n as collectIds, o as selectKeysetChunk, r as idChunks, t as DEFAULT_ID_CHUNK } from "../id-chunks-DH-RAZWG.mjs";
4
+ import { a as toObjectId, i as isValidIdForType, r as isObjectIdInstance, t as getSchemaIdType } from "../id-resolution-Cqh49d9J.mjs";
5
+ import { t as configureLogger } from "../logger-hbWOlj_X.mjs";
6
+ import { n as createMemoryCache, t as toPlain } from "../to-plain-CEKv_ylg.mjs";
7
+ import { n as buildCrudSchemasFromMongooseSchema, t as buildCrudSchemasFromModel } from "../mongooseToJsonSchema-DMDotz4_.mjs";
8
8
  export { DEFAULT_ID_CHUNK, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, collectIds, configureLogger, createError, createFieldPreset, createMemoryCache, filterResponseData, getFieldsForUser, getMongooseProjection, getSchemaIdType, idChunks, isObjectIdInstance, isValidIdForType, keysetFilter, narrowToIds, selectKeysetChunk, toObjectId, toPlain };
@@ -1,10 +1,10 @@
1
- import { A as ObjectId, F as SortSpec, N as SelectSpec, j as PopulateSpec } from "./pagination-D6psses0.mjs";
2
- import { h as RepositoryInstance, l as Plugin, p as RepositoryContext } from "./repository-MqAP409e.mjs";
3
- import { c as ValidationChainOptions, i as Logger, l as ValidatorDefinition, o as SoftDeleteOptions, r as FieldPreset, t as CascadeOptions } from "./plugin-options-Cirmb4Kp.mjs";
1
+ import { A as ObjectId, F as SortSpec, N as SelectSpec, j as PopulateSpec } from "./pagination-DAUg5zOY.mjs";
2
+ import { h as RepositoryInstance, l as Plugin, p as RepositoryContext } from "./repository-DzVIwhU2.mjs";
3
+ import { n as RetentionPurgeEvent, r as RetentionPurgeRequest } from "./retention-purge-DqcYvRnG.mjs";
4
+ import { c as ValidationChainOptions, i as Logger, l as ValidatorDefinition, o as SoftDeleteOptions, r as FieldPreset, t as CascadeOptions } from "./plugin-options-Bpcuz9kS.mjs";
4
5
  import { BulkWriteResult } from "@classytic/repo-core/repository";
5
6
  import { OffsetPaginationResult } from "@classytic/repo-core/pagination";
6
7
  import { AsyncLocalStorage } from "node:async_hooks";
7
- //#region src/plugins/aggregate-helpers.plugin.d.ts
8
8
  /**
9
9
  * Aggregate helpers plugin
10
10
  *
@@ -88,8 +88,6 @@ interface AggregateHelpersMethods {
88
88
  */
89
89
  max(field: string, query?: Record<string, unknown>, options?: Record<string, unknown>): Promise<number>;
90
90
  }
91
- //#endregion
92
- //#region src/plugins/append-only.plugin.d.ts
93
91
  interface AppendOnlyPluginOptions {
94
92
  /**
95
93
  * Mutating ops to permit besides `create` / `createMany`. Use for a
@@ -106,13 +104,19 @@ interface AppendOnlyPluginOptions {
106
104
  * "append-only", never "missing organizationId".
107
105
  */
108
106
  priority?: number | undefined;
107
+ /**
108
+ * Allow the audited retention purge: `delete` / `deleteMany` with
109
+ * `{ mode: 'hard', retentionPurge: { reason, actor? } }` (and
110
+ * `purgeByFilter` / `purgeByField` with `retentionPurge`) pass the fence,
111
+ * emitting `after:append-only-purge`. Default `false`: the option is
112
+ * refused (403 `RETENTION_PURGE_DISABLED`).
113
+ */
114
+ allowPurge?: boolean | undefined;
109
115
  }
116
+ /** Payload of `after:append-only-purge`: one per successful purge call. */
117
+ type AppendOnlyPurgeEvent = RetentionPurgeEvent;
110
118
  declare function appendOnlyPlugin(options?: AppendOnlyPluginOptions): Plugin;
111
- //#endregion
112
- //#region src/plugins/audit-log.plugin.d.ts
113
119
  declare function auditLogPlugin(logger: Logger): Plugin;
114
- //#endregion
115
- //#region src/plugins/batch-operations.plugin.d.ts
116
120
  /** Options accepted by `repo.bulkWrite`. Plugin context may ride alongside. */
117
121
  interface BulkWriteOptions {
118
122
  /** Stop at the first failure. Default `true`, matching the driver. */
@@ -144,19 +148,15 @@ interface BatchOperationsMethods {
144
148
  * ]);
145
149
  * console.log(result.insertedCount, result.modifiedCount, result.deletedCount);
146
150
  */
147
- bulkWrite(operations: Record<string, unknown>[], options?: {
151
+ bulkWrite(operations: readonly Record<string, unknown>[], options?: {
148
152
  session?: unknown;
149
153
  ordered?: boolean;
150
154
  }): Promise<BulkWriteResult>;
151
155
  }
152
- //#endregion
153
- //#region src/plugins/cascade.plugin.d.ts
154
156
  /**
155
157
  * Cascade delete plugin.
156
158
  */
157
159
  declare function cascadePlugin(options: CascadeOptions): Plugin;
158
- //#endregion
159
- //#region src/plugins/elastic.plugin.d.ts
160
160
  interface ElasticSearchOptions {
161
161
  /** Elasticsearch or OpenSearch client instance */
162
162
  client: any;
@@ -166,8 +166,6 @@ interface ElasticSearchOptions {
166
166
  idField?: string;
167
167
  }
168
168
  declare function elasticSearchPlugin(options: ElasticSearchOptions): Plugin;
169
- //#endregion
170
- //#region src/plugins/field-filter.plugin.d.ts
171
169
  /**
172
170
  * Field filter plugin that restricts fields based on user context
173
171
  *
@@ -181,8 +179,6 @@ declare function elasticSearchPlugin(options: ElasticSearchOptions): Plugin;
181
179
  * const repo = new Repository(Model, [fieldFilterPlugin(fieldPreset)]);
182
180
  */
183
181
  declare function fieldFilterPlugin(fieldPreset: FieldPreset): Plugin;
184
- //#endregion
185
- //#region src/plugins/idempotency-store.plugin.d.ts
186
182
  interface IdempotencyStorePluginOptions {
187
183
  /** Field carrying the deterministic operation key. @default `'key'` */
188
184
  keyField?: string;
@@ -323,8 +319,6 @@ interface IdempotencyStoreMethods<TProgress = Record<string, unknown>, TResult =
323
319
  * failures, lapsed-claim sweeps). Removed in mongokit 4.
324
320
  */
325
321
  declare function idempotencyStorePlugin(options?: IdempotencyStorePluginOptions): Plugin;
326
- //#endregion
327
- //#region src/plugins/immutable-states.plugin.d.ts
328
322
  /** The slice of a `before:claim` context `allowClaim` predicates see. */
329
323
  interface ImmutableClaimView {
330
324
  id: unknown;
@@ -356,6 +350,14 @@ interface ImmutableStatesPluginOptions {
356
350
  * state — keep the predicate a strict fingerprint.
357
351
  */
358
352
  allowClaim?: ((view: ImmutableClaimView) => boolean) | undefined;
353
+ /**
354
+ * Allow the audited retention purge: `delete` / `deleteMany` with
355
+ * `{ mode: 'hard', retentionPurge: { reason, actor? } }` (and
356
+ * `purgeByFilter` / `purgeByField` with `retentionPurge`) may remove frozen
357
+ * rows, emitting `after:immutable-states-purge`. Default `false`: the
358
+ * option is refused (403 `RETENTION_PURGE_DISABLED`).
359
+ */
360
+ allowPurge?: boolean | undefined;
359
361
  /** Domain error to throw. Default: 403 `IMMUTABLE_STATE_VIOLATION`. */
360
362
  errorFactory?: ((ctx: {
361
363
  id: unknown;
@@ -363,9 +365,14 @@ interface ImmutableStatesPluginOptions {
363
365
  state?: string | undefined;
364
366
  }) => Error) | undefined;
365
367
  }
368
+ /** Per-call purge request (`options.retentionPurge`). */
369
+ type ImmutablePurgeRequest = RetentionPurgeRequest;
370
+ /** Payload of `after:immutable-states-purge`: one per successful purge call. */
371
+ interface ImmutablePurgeEvent extends RetentionPurgeEvent {
372
+ /** The frozen states configured on the plugin. */
373
+ states: readonly string[];
374
+ }
366
375
  declare function immutableStatesPlugin(options: ImmutableStatesPluginOptions): Plugin;
367
- //#endregion
368
- //#region src/plugins/lease.plugin.d.ts
369
376
  interface LeasePluginOptions {
370
377
  /** Field carrying the row's status. @default `'status'` */
371
378
  statusField?: string;
@@ -381,7 +388,57 @@ interface LeasePluginOptions {
381
388
  doneStatus?: string;
382
389
  /** Sort fields used to pick the FIFO winner from the lease pool. @default `{ createdAt: 1 }` */
383
390
  sort?: Record<string, 1 | -1>;
391
+ /**
392
+ * Clock used by `lease` / `extend` / `release` for both the CAS
393
+ * comparison and the new expiry. A per-call `now` wins over it.
394
+ * Inject one to drive lease expiry from a test or a host `Clock`
395
+ * port. @default `() => new Date()`
396
+ */
397
+ nowFn?: () => Date;
398
+ /**
399
+ * Statuses whose expired lease `lease()` may recover. Only rows in
400
+ * one of these statuses AND with `leaseExpiresAt < now` are dead
401
+ * leases; a row in any other status is never re-leased, whatever its
402
+ * lease field says (a cancelled row with a stale expiry stays
403
+ * cancelled). @default `[processingStatus]`
404
+ */
405
+ deadLeaseStatuses?: readonly string[];
406
+ /**
407
+ * Optional attempts counter. When set, every successful `lease()`
408
+ * (first claim and dead-lease recovery alike) applies
409
+ * `$inc: { [attemptsField]: 1 }`. Off by default.
410
+ */
411
+ attemptsField?: string;
412
+ /**
413
+ * Cap on `attemptsField`. A row whose counter is already
414
+ * `>= maxAttempts` is not leasable; a row missing the field is.
415
+ * Exhausted rows stay where they are until `reapExhausted()` moves
416
+ * them to `exhaustedStatus` (or the host sweeps them itself).
417
+ * Requires `attemptsField` (throws at construction).
418
+ */
419
+ maxAttempts?: number;
420
+ /**
421
+ * Terminal status `reapExhausted()` moves an exhausted row to (e.g.
422
+ * `'failed'`). Requires `maxAttempts`; must differ from
423
+ * `pendingStatus`, `processingStatus` and every `deadLeaseStatuses`
424
+ * entry (throws at construction). Without it `reapExhausted()` needs
425
+ * a per-call `status`.
426
+ */
427
+ exhaustedStatus?: string;
428
+ /** Field `reapExhausted()` stamps with the reap time (e.g. `'failedAt'`). Off by default. */
429
+ exhaustedAtField?: string;
430
+ /**
431
+ * Extra predicate ANDed into the PENDING arm only, so not-yet-due rows
432
+ * wait while a dead lease is still recovered. The object form is
433
+ * STATIC (built once); for a time-relative predicate pass the function
434
+ * form, called per `lease()` with the resolved `now` (per-call `now`,
435
+ * else `nowFn()`): `pendingFilter: (now) => ({ dueAt: { $lte: now } })`.
436
+ * A per-call `pendingFilter` replaces this one.
437
+ */
438
+ pendingFilter?: LeasePendingFilter;
384
439
  }
440
+ /** Static pending-arm predicate, or a builder called with the claim's `now`. */
441
+ type LeasePendingFilter = Record<string, unknown> | ((now: Date) => Record<string, unknown>);
385
442
  /**
386
443
  * Methods contributed by `leasePlugin()`. Use as a type assertion when
387
444
  * constructing the repo so call sites get autocomplete:
@@ -397,9 +454,18 @@ interface LeasePluginOptions {
397
454
  interface LeaseMethods<TDoc> {
398
455
  /**
399
456
  * Atomically claim the next available row. Matches rows that are
400
- * either in `pendingStatus` OR have an expired `leaseExpiresAt` (dead
401
- * lease recovery). Sets status to `processingStatus`, stamps
402
- * `leasedBy`, and pushes `leaseExpiresAt = now + leaseFor`.
457
+ * either in `pendingStatus` (AND `pendingFilter`, when given) OR in a
458
+ * `deadLeaseStatuses` status with an expired `leaseExpiresAt` (dead
459
+ * lease recovery). The caller's `filter` is kept as given (its own
460
+ * `$or` included) and the claim condition is ANDed beside it. Sets
461
+ * status to `processingStatus`, stamps `leasedBy`, pushes
462
+ * `leaseExpiresAt = now + leaseFor`, and bumps `attemptsField` when
463
+ * configured.
464
+ *
465
+ * `leasedBy` is the fencing token for `extend` / `release`: make it
466
+ * unique per claim (e.g. `${instanceId}:${randomUUID()}`). A worker
467
+ * that reuses one id could otherwise finalise a stale run after its
468
+ * own lapsed lease was re-leased under the same id.
403
469
  *
404
470
  * @returns The leased doc, or `null` when no row matches.
405
471
  */
@@ -407,17 +473,24 @@ interface LeaseMethods<TDoc> {
407
473
  filter?: Record<string, unknown>;
408
474
  leaseFor: number;
409
475
  leasedBy: string;
476
+ /** Clock reading for this call. @default plugin `nowFn()` */
477
+ now?: Date;
478
+ /** Replaces the plugin-level `pendingFilter` for this call. */
479
+ pendingFilter?: LeasePendingFilter;
410
480
  options?: Record<string, unknown>;
411
481
  }): Promise<TDoc | null>;
412
482
  /**
413
483
  * Push a held lease's expiry further. Only succeeds when the lease
414
- * is still ours (`leasedBy === leasedBy && leaseExpiresAt > now`).
484
+ * is still ours (`leasedBy === leasedBy && leaseExpiresAt > now`) and
485
+ * the row is still in `processingStatus` (a row the host moved to a
486
+ * terminal status, e.g. cancelled, is not kept alive).
415
487
  * Returns `null` if the lease was lost — caller should drop the
416
488
  * work, since someone else has already started replaying it.
417
489
  */
418
490
  extend(id: string | ObjectId, opts: {
419
491
  leasedBy: string;
420
492
  leaseFor: number;
493
+ now?: Date;
421
494
  options?: Record<string, unknown>;
422
495
  }): Promise<TDoc | null>;
423
496
  /**
@@ -434,17 +507,48 @@ interface LeaseMethods<TDoc> {
434
507
  * `lease()` predicate) skip this row.
435
508
  *
436
509
  * Returns the released doc, or `null` when the CAS lost (lease
437
- * expired / held by another worker / row gone).
510
+ * expired / held by another worker / row gone / row no longer in
511
+ * `processingStatus`, e.g. the host cancelled it mid-run).
438
512
  */
439
513
  release(id: string | ObjectId, opts: {
440
514
  leasedBy: string;
441
515
  finalStatus?: string;
516
+ now?: Date;
442
517
  options?: Record<string, unknown>;
443
518
  }): Promise<TDoc | null>;
519
+ /**
520
+ * Move every EXHAUSTED row to `exhaustedStatus` and return the moved
521
+ * rows (after the move), so the host only audits and alerts. A row is
522
+ * exhausted when `lease()` would claim it but for the attempts cap:
523
+ * `attemptsField >= maxAttempts` AND either pending (and passing
524
+ * `pendingFilter`) or in a `deadLeaseStatuses` status whose lease
525
+ * lapsed (`leaseExpiresAt < now`). A row whose lease is still live is
526
+ * left alone: its holder may still release it.
527
+ *
528
+ * Each move is a compare-and-set on that same condition, so two
529
+ * reapers never both return a row, and a row is returned once: once
530
+ * moved it no longer matches. Clears `leasedBy` / `leaseExpiresAt`
531
+ * and stamps `exhaustedAtField` with `now` when configured. The
532
+ * caller's `filter` is ANDed in as with `lease()`. Requires
533
+ * `maxAttempts`; reads at most `limit` rows (default 100) in `sort`
534
+ * order, so call it again until it returns `[]`. A batch shorter than
535
+ * `limit` does not mean none are left: a concurrent reaper may have
536
+ * moved some of the rows read (and keeps draining them).
537
+ */
538
+ reapExhausted(opts?: {
539
+ filter?: Record<string, unknown>;
540
+ /** Clock reading for this call. @default plugin `nowFn()` */
541
+ now?: Date;
542
+ /** Rows read per call. @default 100 */
543
+ limit?: number;
544
+ /** Replaces the plugin-level `exhaustedStatus` for this call. */
545
+ status?: string;
546
+ /** Replaces the plugin-level `pendingFilter` for this call. */
547
+ pendingFilter?: LeasePendingFilter;
548
+ options?: Record<string, unknown>;
549
+ }): Promise<TDoc[]>;
444
550
  }
445
551
  declare function leasePlugin(options?: LeasePluginOptions): Plugin;
446
- //#endregion
447
- //#region src/plugins/method-registry.plugin.d.ts
448
552
  /**
449
553
  * Extended repository interface with method registry
450
554
  */
@@ -457,8 +561,6 @@ interface MethodRegistryRepository extends RepositoryInstance {
457
561
  * Method registry plugin that enables dynamic method registration
458
562
  */
459
563
  declare function methodRegistryPlugin(): Plugin;
460
- //#endregion
461
- //#region src/plugins/mongo-operations.plugin.d.ts
462
564
  /**
463
565
  * MongoDB operations plugin
464
566
  *
@@ -689,8 +791,6 @@ interface MongoOperationsMethods<TDoc> {
689
791
  */
690
792
  incrementIfBelow(id: string | ObjectId, field: string, ceiling: number, by?: number, options?: Record<string, unknown>): Promise<TDoc | null>;
691
793
  }
692
- //#endregion
693
- //#region src/plugins/observability.plugin.d.ts
694
794
  interface OperationMetric {
695
795
  /** Operation name (e.g., 'create', 'getAll', 'update') */
696
796
  operation: string;
@@ -718,8 +818,6 @@ interface ObservabilityOptions {
718
818
  slowThresholdMs?: number;
719
819
  }
720
820
  declare function observabilityPlugin(options: ObservabilityOptions): Plugin;
721
- //#endregion
722
- //#region src/plugins/search-keys.plugin.d.ts
723
821
  interface SearchKeysPluginOptions {
724
822
  /** Source fields (dotted paths allowed; string, number, or arrays of them). */
725
823
  fields: readonly string[];
@@ -727,8 +825,6 @@ interface SearchKeysPluginOptions {
727
825
  keysField?: string;
728
826
  }
729
827
  declare function searchKeysPlugin(options: SearchKeysPluginOptions): Plugin;
730
- //#endregion
731
- //#region src/plugins/soft-delete.plugin.d.ts
732
828
  /**
733
829
  * Soft delete plugin
734
830
  *
@@ -820,8 +916,6 @@ interface SoftDeleteMethods<TDoc> {
820
916
  session?: unknown;
821
917
  }): Promise<OffsetPaginationResult<TDoc>>;
822
918
  }
823
- //#endregion
824
- //#region src/plugins/subdocument.plugin.d.ts
825
919
  /**
826
920
  * Subdocument plugin for managing nested arrays
827
921
  *
@@ -902,8 +996,6 @@ interface SubdocumentMethods<TDoc> {
902
996
  */
903
997
  deleteSubdocument(parentId: string | ObjectId, arrayPath: string, subId: string | ObjectId, options?: Record<string, unknown>): Promise<TDoc>;
904
998
  }
905
- //#endregion
906
- //#region src/plugins/tenant-context.d.ts
907
999
  interface TenantStore {
908
1000
  tenantId: string | undefined;
909
1001
  [key: string]: unknown;
@@ -924,8 +1016,6 @@ interface TenantContext {
924
1016
  readonly storage: AsyncLocalStorage<TenantStore>;
925
1017
  }
926
1018
  declare function createTenantContext(): TenantContext;
927
- //#endregion
928
- //#region src/plugins/timestamp.plugin.d.ts
929
1019
  /**
930
1020
  * Timestamp plugin that auto-injects timestamps
931
1021
  *
@@ -933,8 +1023,6 @@ declare function createTenantContext(): TenantContext;
933
1023
  * const repo = new Repository(Model, [timestampPlugin()]);
934
1024
  */
935
1025
  declare function timestampPlugin(): Plugin;
936
- //#endregion
937
- //#region src/plugins/validation-chain.plugin.d.ts
938
1026
  type OperationType = 'create' | 'createMany' | 'update' | 'findOneAndUpdate' | 'delete';
939
1027
  /**
940
1028
  * Validation chain plugin
@@ -972,5 +1060,4 @@ declare function immutableField(field: string): ValidatorDefinition;
972
1060
  * Ensure field value is unique
973
1061
  */
974
1062
  declare function uniqueField(field: string, errorMessage?: string): ValidatorDefinition;
975
- //#endregion
976
- export { immutableStatesPlugin as A, BulkWriteOptions as B, MethodRegistryRepository as C, leasePlugin as D, LeasePluginOptions as E, fieldFilterPlugin as F, appendOnlyPlugin as G, batchOperationsPlugin as H, ElasticSearchOptions as I, AggregateHelpersMethods as K, elasticSearchPlugin as L, IdempotencyStoreMethods as M, IdempotencyStorePluginOptions as N, ImmutableClaimView as O, idempotencyStorePlugin as P, cascadePlugin as R, mongoOperationsPlugin as S, LeaseMethods as T, auditLogPlugin as U, BulkWriteResult as V, AppendOnlyPluginOptions as W, searchKeysPlugin as _, requireField as a, observabilityPlugin as b, timestampPlugin as c, createTenantContext as d, SubdocumentMethods as f, SearchKeysPluginOptions as g, softDeletePlugin as h, immutableField as i, IdempotencyClaim as j, ImmutableStatesPluginOptions as k, TenantContext as l, SoftDeleteMethods as m, autoInject as n, uniqueField as o, subdocumentPlugin as p, aggregateHelpersPlugin as q, blockIf as r, validationChainPlugin as s, OperationType as t, TenantStore as u, ObservabilityOptions as v, methodRegistryPlugin as w, MongoOperationsMethods as x, OperationMetric as y, BatchOperationsMethods as z };
1063
+ export { ImmutablePurgeEvent as A, elasticSearchPlugin as B, MethodRegistryRepository as C, LeasePluginOptions as D, LeasePendingFilter as E, IdempotencyStoreMethods as F, batchOperationsPlugin as G, BatchOperationsMethods as H, IdempotencyStorePluginOptions as I, AppendOnlyPurgeEvent as J, auditLogPlugin as K, idempotencyStorePlugin as L, ImmutableStatesPluginOptions as M, immutableStatesPlugin as N, leasePlugin as O, IdempotencyClaim as P, fieldFilterPlugin as R, mongoOperationsPlugin as S, LeaseMethods as T, BulkWriteOptions as U, cascadePlugin as V, BulkWriteResult as W, AggregateHelpersMethods as X, appendOnlyPlugin as Y, aggregateHelpersPlugin as Z, searchKeysPlugin as _, requireField as a, observabilityPlugin as b, timestampPlugin as c, createTenantContext as d, SubdocumentMethods as f, SearchKeysPluginOptions as g, softDeletePlugin as h, immutableField as i, ImmutablePurgeRequest as j, ImmutableClaimView as k, TenantContext as l, SoftDeleteMethods as m, autoInject as n, uniqueField as o, subdocumentPlugin as p, AppendOnlyPluginOptions as q, blockIf as r, validationChainPlugin as s, OperationType as t, TenantStore as u, ObservabilityOptions as v, methodRegistryPlugin as w, MongoOperationsMethods as x, OperationMetric as y, ElasticSearchOptions as z };