@classytic/mongokit 3.25.0 → 3.28.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.
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/@classytic%2Fmongokit.svg)](https://www.npmjs.com/package/@classytic/mongokit)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![Sponsor](https://img.shields.io/github/sponsors/classytic?style=flat-square&label=Sponsor&logo=GitHub&color=EA4AAA)](https://github.com/sponsors/classytic)
5
6
 
6
7
  Production-ready repository layer for Mongoose apps: tenant-safe CRUD, soft delete, pagination, hooks, retries, transactions, change streams, and plugins.
7
8
 
@@ -16,20 +16,56 @@ import { decodeAggCursor, encodeAggCursor, isKeysetMode, normalizeGroupBy, valid
16
16
  * predicate the purge targets.
17
17
  */
18
18
  function createMongoPurgePort(repo, field, value, session) {
19
- const filter = { [field]: value };
19
+ return createKeysetPurgePort(repo, { [field]: value }, session);
20
+ }
21
+ /**
22
+ * Build a `PurgePort` bound to a repository + a pre-compiled Mongo filter
23
+ * (the repository compiles Filter IR via `compileFilterToMongo` before
24
+ * calling this) — the range/filter-scoped sibling of
25
+ * {@link createMongoPurgePort}.
26
+ *
27
+ * The per-strategy chunk logic is IDENTICAL to the equality-bound port; the
28
+ * only difference is the base predicate — an arbitrary compiled filter (a
29
+ * `civilDate` window, a retention cutoff, a compound cohort) instead of
30
+ * `{ [field]: value }`. The narrowed write is re-asserted on
31
+ * `{ _id: { $in: ids }, ...filter }`, the same defense the equality port
32
+ * uses, so a row that leaves the matching set between the id select and the
33
+ * write is never touched.
34
+ *
35
+ * Mirrors `createMongoArchivePort`, which likewise binds a pre-compiled
36
+ * filter rather than a `field/value` pair.
37
+ */
38
+ function createMongoPurgePortFromFilter(repo, filter, session) {
39
+ return createKeysetPurgePort(repo, filter, session);
40
+ }
41
+ /**
42
+ * Shared keyset-progressed port both public factories delegate to. The
43
+ * closure's `cursor` is the port's single-run progression state.
44
+ */
45
+ function createKeysetPurgePort(repo, baseFilter, session) {
20
46
  const baseOpts = {
21
47
  session,
22
48
  bypassTenant: true
23
49
  };
50
+ let cursor = null;
51
+ const selectFilter = () => cursor == null ? baseFilter : {
52
+ ...baseFilter,
53
+ _id: { $gt: cursor }
54
+ };
24
55
  return { async purgeChunk(strategy, limit) {
25
56
  if (strategy.type === "anonymize") {
26
- if (Object.values(strategy.fields).some((v) => typeof v === "function")) return purgeAnonymizeFunctional(repo, filter, strategy.fields, limit, session);
57
+ if (Object.values(strategy.fields).some((v) => typeof v === "function")) {
58
+ const { processed, lastId } = await purgeAnonymizeFunctional(repo, selectFilter(), strategy.fields, limit, session);
59
+ if (lastId != null) cursor = lastId;
60
+ return processed;
61
+ }
27
62
  }
28
- const idDocs = await repo.Model.find(filter, { _id: 1 }).limit(limit).session(session ?? null).lean().exec();
63
+ const idDocs = await repo.Model.find(selectFilter(), { _id: 1 }).sort({ _id: 1 }).limit(limit).session(session ?? null).lean().exec();
29
64
  if (idDocs.length === 0) return 0;
65
+ const ids = idDocs.map((d) => d._id);
30
66
  const chunkFilter = {
31
- _id: { $in: idDocs.map((d) => d._id) },
32
- [field]: value
67
+ ...baseFilter,
68
+ _id: { $in: ids }
33
69
  };
34
70
  switch (strategy.type) {
35
71
  case "hard":
@@ -37,17 +73,19 @@ function createMongoPurgePort(repo, field, value, session) {
37
73
  ...baseOpts,
38
74
  mode: "hard"
39
75
  });
40
- return idDocs.length;
76
+ break;
41
77
  case "soft":
42
78
  await repo.updateMany(chunkFilter, { $set: {
43
79
  [strategy.deletedField ?? "deleted"]: true,
44
80
  [strategy.deletedAtField ?? "deletedAt"]: /* @__PURE__ */ new Date()
45
81
  } }, baseOpts);
46
- return idDocs.length;
82
+ break;
47
83
  case "anonymize":
48
84
  await repo.updateMany(chunkFilter, { $set: strategy.fields }, baseOpts);
49
- return idDocs.length;
85
+ break;
50
86
  }
87
+ cursor = ids[ids.length - 1];
88
+ return idDocs.length;
51
89
  } };
52
90
  }
53
91
  /**
@@ -56,13 +94,20 @@ function createMongoPurgePort(repo, field, value, session) {
56
94
  * heterogeneous `updateOne` ops. Two round-trips per chunk regardless
57
95
  * of N — vs the N+1 of per-doc `update()` fan-out.
58
96
  *
97
+ * Selection is `_id`-ascending against the caller's keyset-narrowed
98
+ * filter; returns the chunk's last `_id` so the port can advance its
99
+ * cursor after the write commits.
100
+ *
59
101
  * Mongo's `bulkWrite` is ordered: false → independent failures don't
60
102
  * abort the batch. For idempotent anonymize (same row anonymized twice
61
103
  * is the same write), this matches the at-least-once contract.
62
104
  */
63
- async function purgeAnonymizeFunctional(repo, filter, fields, limit, session) {
64
- const docs = await repo.Model.find(filter).limit(limit).session(session ?? null).lean().exec();
65
- if (docs.length === 0) return 0;
105
+ async function purgeAnonymizeFunctional(repo, selectFilter, fields, limit, session) {
106
+ const docs = await repo.Model.find(selectFilter).sort({ _id: 1 }).limit(limit).session(session ?? null).lean().exec();
107
+ if (docs.length === 0) return {
108
+ processed: 0,
109
+ lastId: null
110
+ };
66
111
  const operations = docs.map((doc) => {
67
112
  const set = {};
68
113
  for (const [k, v] of Object.entries(fields)) set[k] = typeof v === "function" ? v(doc) : v;
@@ -75,7 +120,10 @@ async function purgeAnonymizeFunctional(repo, filter, fields, limit, session) {
75
120
  ordered: false,
76
121
  session: session ?? void 0
77
122
  });
78
- return docs.length;
123
+ return {
124
+ processed: docs.length,
125
+ lastId: docs[docs.length - 1]?._id ?? null
126
+ };
79
127
  }
80
128
  //#endregion
81
129
  //#region src/capabilities.ts
@@ -97,6 +145,7 @@ const MONGOKIT_CAPABILITIES = {
97
145
  getOrCreate: true,
98
146
  countAndExists: true,
99
147
  purgeByField: true,
148
+ purgeByFilter: true,
100
149
  archiveByFilter: true,
101
150
  arrayOperators: true,
102
151
  regexFilter: true,
@@ -1731,6 +1780,33 @@ function isTransactionUnsupported(error) {
1731
1780
  const message = (error.message || "").toLowerCase();
1732
1781
  return message.includes("transaction numbers are only allowed on a replica set member") || message.includes("transaction is not supported") || message.includes("does not support retryable writes");
1733
1782
  }
1783
+ /**
1784
+ * PROACTIVELY report whether a connection's deployment supports multi-document
1785
+ * transactions — the read-side companion to {@link isTransactionUnsupported}
1786
+ * (which classifies a FAILED attempt). Single source of truth for the topology
1787
+ * check so callers don't hand-roll `client.topology.description.type` probing.
1788
+ *
1789
+ * Returns `false` ONLY when the topology is positively a standalone (`'Single'`)
1790
+ * server; an unknown/undefined topology (not yet connected, non-standard
1791
+ * connection object) returns `true` — optimistic, because a genuine failure is
1792
+ * still caught reactively by `isTransactionUnsupported`. Lets a caller skip a
1793
+ * doomed transaction attempt on standalone dev Mongo rather than starting one
1794
+ * just to catch the error.
1795
+ *
1796
+ * Accepts a Mongoose `Connection` (or anything exposing `getClient()`/`client`
1797
+ * with a driver `topology`), so it stays decoupled from mongoose types.
1798
+ */
1799
+ function supportsTransactions(connection) {
1800
+ const probe = connection;
1801
+ if (!probe) return true;
1802
+ let client;
1803
+ try {
1804
+ client = probe.getClient?.() ?? probe.client ?? void 0;
1805
+ } catch {
1806
+ return true;
1807
+ }
1808
+ return client?.topology?.description?.type !== "Single";
1809
+ }
1734
1810
  //#endregion
1735
1811
  //#region src/Repository.ts
1736
1812
  /**
@@ -3441,6 +3517,24 @@ var Repository = class extends RepositoryBase {
3441
3517
  return runChunkedPurge(strategy, options, createMongoPurgePort(this, field, value, options.session));
3442
3518
  }
3443
3519
  /**
3520
+ * Range/filter-scoped purge — see `StandardRepo.purgeByFilter` for the
3521
+ * cross-kit contract. The compliance primitive for "purge/anonymize a
3522
+ * slice across a RANGE while retaining measures" (redact a PII dimension
3523
+ * across a `civilDate` window, hard-delete past a retention cutoff): the
3524
+ * base predicate is an arbitrary compiled filter, not `field = value`.
3525
+ *
3526
+ * Composes the kit-agnostic `runChunkedPurge` orchestrator (loop, signal,
3527
+ * progress, result envelope) with `createMongoPurgePortFromFilter` (the
3528
+ * filter-bound sibling of `createMongoPurgePort`). Accepts Filter IR or a
3529
+ * Mongo-shaped filter record — compiled once via `compileFilterToMongo`,
3530
+ * the same dual-dialect rule as `archiveByFilter` and every other verb.
3531
+ * See [actions/purge.ts](./actions/purge.ts) for the port.
3532
+ */
3533
+ async purgeByFilter(filter, strategy, options = {}) {
3534
+ const compiled = compileFilterToMongo(filter);
3535
+ return runChunkedPurge(strategy, options, createMongoPurgePortFromFilter(this, compiled, options.session));
3536
+ }
3537
+ /**
3444
3538
  * Chunked cold-storage extraction — see `StandardRepo.archiveByFilter`
3445
3539
  * for the cross-kit contract (write-before-delete, at-least-once,
3446
3540
  * duplicate-tolerant sinks). Mongokit composes the kit-agnostic
@@ -4209,4 +4303,4 @@ var Repository = class extends RepositoryBase {
4209
4303
  }
4210
4304
  };
4211
4305
  //#endregion
4212
- export { withTransaction as a, MUTATING_OPERATIONS as c, operationsByPolicyKey as d, MONGOKIT_CAPABILITIES as f, isTransactionUnsupported as i, OP_REGISTRY as l, Repository as n, AggregationBuilder as o, createMongoPurgePort as p, batchTransaction as r, ALL_OPERATIONS as s, HOOK_PRIORITY$1 as t, READ_OPERATIONS as u };
4306
+ export { supportsTransactions as a, ALL_OPERATIONS as c, READ_OPERATIONS as d, operationsByPolicyKey as f, isTransactionUnsupported as i, MUTATING_OPERATIONS as l, createMongoPurgePort as m, Repository as n, withTransaction as o, MONGOKIT_CAPABILITIES as p, batchTransaction as r, AggregationBuilder as s, HOOK_PRIORITY$1 as t, OP_REGISTRY as u };
@@ -1,4 +1,4 @@
1
- import { n as Repository } from "../Repository-KKR8jxfD.mjs";
1
+ import { n as Repository } from "../Repository-BFBTAMlA.mjs";
2
2
  import { n as createMongooseAdapter } from "../adapter-CLvUHiTW.mjs";
3
3
  import { pluralizeBetterAuthCollection, resolveBetterAuthCollections } from "@classytic/repo-core/better-auth";
4
4
  //#region src/better-auth/index.ts
@@ -1,5 +1,5 @@
1
- import { n as Repository } from "./Repository-KKR8jxfD.mjs";
2
- import { a as methodRegistryPlugin, d as cachePlugin, f as batchOperationsPlugin, g as auditLogPlugin, i as multiTenantPlugin, m as auditTrailPlugin, n as softDeletePlugin, o as customIdPlugin, t as timestampPlugin } from "./timestamp.plugin-B8252GuQ.mjs";
1
+ import { n as Repository } from "./Repository-BFBTAMlA.mjs";
2
+ import { a as methodRegistryPlugin, d as cachePlugin, f as batchOperationsPlugin, g as auditLogPlugin, i as multiTenantPlugin, m as auditTrailPlugin, n as softDeletePlugin, o as customIdPlugin, t as timestampPlugin } from "./timestamp.plugin-ennbatTW.mjs";
3
3
  //#region src/create-repository.ts
4
4
  function isLoggerAudit(value) {
5
5
  const v = value;
@@ -1462,6 +1462,21 @@ declare class Repository<TDoc = unknown> extends RepositoryBase {
1462
1462
  * the port implementation.
1463
1463
  */
1464
1464
  purgeByField(field: string, value: unknown, strategy: TenantPurgeStrategy, options?: TenantPurgeOptions): Promise<TenantPurgeResult>;
1465
+ /**
1466
+ * Range/filter-scoped purge — see `StandardRepo.purgeByFilter` for the
1467
+ * cross-kit contract. The compliance primitive for "purge/anonymize a
1468
+ * slice across a RANGE while retaining measures" (redact a PII dimension
1469
+ * across a `civilDate` window, hard-delete past a retention cutoff): the
1470
+ * base predicate is an arbitrary compiled filter, not `field = value`.
1471
+ *
1472
+ * Composes the kit-agnostic `runChunkedPurge` orchestrator (loop, signal,
1473
+ * progress, result envelope) with `createMongoPurgePortFromFilter` (the
1474
+ * filter-bound sibling of `createMongoPurgePort`). Accepts Filter IR or a
1475
+ * Mongo-shaped filter record — compiled once via `compileFilterToMongo`,
1476
+ * the same dual-dialect rule as `archiveByFilter` and every other verb.
1477
+ * See [actions/purge.ts](./actions/purge.ts) for the port.
1478
+ */
1479
+ purgeByFilter(filter: Record<string, unknown> | Filter, strategy: TenantPurgeStrategy, options?: TenantPurgeOptions): Promise<TenantPurgeResult>;
1465
1480
  /**
1466
1481
  * Chunked cold-storage extraction — see `StandardRepo.archiveByFilter`
1467
1482
  * for the cross-kit contract (write-before-delete, at-least-once,
package/dist/index.d.mts CHANGED
@@ -6,9 +6,9 @@ import { a as InferRawDoc, c as NonNullableFields, d as Strict, f as UpdatePatch
6
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-DCHRWRXw.mjs";
7
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-DVyVGREb.mjs";
8
8
  import { t as PaginationEngine } from "./PaginationEngine-CBS1XGzp.mjs";
9
- import { a as Repository, i as HOOK_PRIORITY, n as CreateRepositoryConfig, o as TransitionMachine, r as createRepository, s as AggregationBuilder, t as AuditConfig } from "./create-repository-HjMD9h9_.mjs";
9
+ import { a as Repository, i as HOOK_PRIORITY, n as CreateRepositoryConfig, o as TransitionMachine, r as createRepository, s as AggregationBuilder, t as AuditConfig } from "./create-repository-D4ZU94b3.mjs";
10
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-vFufQZMZ.mjs";
11
- import { A as elasticSearchPlugin, C as LeasePluginOptions, D as immutableStatesPlugin, E as ImmutableStatesPluginOptions, F as auditLogPlugin, I as AppendOnlyPluginOptions, L as appendOnlyPlugin, M as BatchOperationsMethods, N as BulkWriteResult, O as fieldFilterPlugin, P as batchOperationsPlugin, R as AggregateHelpersMethods, S as LeaseMethods, T as ImmutableClaimView, _ as observabilityPlugin, a as uniqueField, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as cascadePlugin, k as ElasticSearchOptions, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MongoOperationsMethods, w as leasePlugin, x as methodRegistryPlugin, y as mongoOperationsPlugin, z as aggregateHelpersPlugin } from "./validation-chain.plugin-DxD15h-2.mjs";
11
+ import { A as ImmutableStatesPluginOptions, B as AppendOnlyPluginOptions, C as LeasePluginOptions, D as IdempotencyStorePluginOptions, E as IdempotencyStoreMethods, F as cascadePlugin, H as AggregateHelpersMethods, I as BatchOperationsMethods, L as BulkWriteResult, M as fieldFilterPlugin, N as ElasticSearchOptions, O as idempotencyStorePlugin, P as elasticSearchPlugin, R as batchOperationsPlugin, S as LeaseMethods, T as IdempotencyClaim, U as aggregateHelpersPlugin, V as appendOnlyPlugin, _ as observabilityPlugin, a as uniqueField, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as immutableStatesPlugin, k as ImmutableClaimView, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MongoOperationsMethods, w as leasePlugin, x as methodRegistryPlugin, y as mongoOperationsPlugin, z as auditLogPlugin } from "./validation-chain.plugin-Cm0Ttnd8.mjs";
12
12
  import { n as SchemaPathsLike, t as FieldType$1 } from "./coercion-BjdPX3YF.mjs";
13
13
  import { n as SchemaIndexes } from "./indexes-DIjMpYES.mjs";
14
14
  import { _ as isDuplicateKeyError, a as configureLogger, c as idVariants, f as createFieldPreset, g as createError, h as getMongooseProjection, i as createMemoryCache, m as getFieldsForUser, n as buildCrudSchemasFromModel, p as filterResponseData, r as buildCrudSchemasFromMongooseSchema, t as toPlain, v as parseDuplicateKeyError } from "./to-plain-DkF5px0R.mjs";
@@ -492,6 +492,23 @@ declare function batchTransaction<TRepos extends Record<string, object>, TResult
492
492
  * problem is the same, and the fallback semantics are correct either way.
493
493
  */
494
494
  declare function isTransactionUnsupported(error: Error): boolean;
495
+ /**
496
+ * PROACTIVELY report whether a connection's deployment supports multi-document
497
+ * transactions — the read-side companion to {@link isTransactionUnsupported}
498
+ * (which classifies a FAILED attempt). Single source of truth for the topology
499
+ * check so callers don't hand-roll `client.topology.description.type` probing.
500
+ *
501
+ * Returns `false` ONLY when the topology is positively a standalone (`'Single'`)
502
+ * server; an unknown/undefined topology (not yet connected, non-standard
503
+ * connection object) returns `true` — optimistic, because a genuine failure is
504
+ * still caught reactively by `isTransactionUnsupported`. Lets a caller skip a
505
+ * doomed transaction attempt on standalone dev Mongo rather than starting one
506
+ * just to catch the error.
507
+ *
508
+ * Accepts a Mongoose `Connection` (or anything exposing `getClient()`/`client`
509
+ * with a driver `topology`), so it stays decoupled from mongoose types.
510
+ */
511
+ declare function supportsTransactions(connection: unknown): boolean;
495
512
  //#endregion
496
513
  //#region src/utils/repo-options.d.ts
497
514
  /**
@@ -692,4 +709,4 @@ declare function createOptionsExtractor<TCtx extends Record<string, unknown>>(fi
692
709
  */
693
710
  declare function systemContext(extra?: SessionOptions): SessionOptions;
694
711
  //#endregion
695
- export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, AggregationBuilder, 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 CascadeOptions, type CascadePurgeReferencesOptions, type CascadeRelation, type CollationOptions, type CollectionRWLike, type CreateInput, type CreateOptions, type CreateRepositoryConfig, type CursorPayload, type CustomIdOptions, type DateSequentialIdOptions, type DecodedCursor, type DeepPartial, type DeleteResult, type DocField, type ElasticSearchOptions, type EnsureAuditTrailReadyOptions, type EventHandlers, type EventPayload, type EventPhase, type FieldPreset, type FieldType, type FilterQuery, type FindOneAndUpdateOptions, type GroupResult, HOOK_PRIORITY, type HookMode, type IdGenerator, type ImmutableClaimView, type ImmutableStatesPluginOptions, type InferDocument, type InferRawDoc, type KeysOfType, type KeysetPaginationOptions, type LeaseMethods, type LeasePluginOptions, type Logger, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, type Middleware, type MiddlewareContext, type MinMaxResult, type MinimalRepoView, type MongoKitPluginName, type MongoOperationsMethods, type MongoOperatorUpdate, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OperationDescriptor, type OperationMetric, type OperationOptions, type PaginationConfig, PaginationEngine, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, type PrioritizedHook, type PurgeMode, type PurgeableRepoLike, QueryParser, type QueryParserOptions, 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 SoftDeleteFilterMode, type SoftDeleteMethods, type SoftDeleteOptions, type SoftDeleteRepository, type SortDirection, type SortSpec, type Strict, type SubdocumentMethods, type TenantContext, type TenantStore, type TransitionMachine, type UpdateManyResult, type UpdateOptions, type UpdatePatch, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidatorDefinition, type ValueType, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, cascadePurgeReferences, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, idVariants, immutableField, immutableStatesPlugin, isDuplicateKeyError, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, repoOptionsFromCtx, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, systemContext, timestampPlugin, toPlain, uniqueField, validationChainPlugin, withTransaction };
712
+ export { ALL_OPERATIONS, type AggregateHelpersMethods, type AggregateOptions, type AggregatePaginationOptions, AggregationBuilder, 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 CascadeOptions, type CascadePurgeReferencesOptions, type CascadeRelation, type CollationOptions, type CollectionRWLike, type CreateInput, type CreateOptions, type CreateRepositoryConfig, type CursorPayload, type CustomIdOptions, type DateSequentialIdOptions, type DecodedCursor, type DeepPartial, type DeleteResult, type DocField, type ElasticSearchOptions, type EnsureAuditTrailReadyOptions, type EventHandlers, type EventPayload, type EventPhase, type FieldPreset, type FieldType, type FilterQuery, type FindOneAndUpdateOptions, type GroupResult, 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, LookupBuilder, type LookupOptions, type LookupPopulateOptions, type LookupPopulateResult, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, type Middleware, type MiddlewareContext, type MinMaxResult, type MinimalRepoView, type MongoKitPluginName, type MongoOperationsMethods, type MongoOperatorUpdate, type MultiTenantOptions, type NonNullableFields, OP_REGISTRY, type ObjectId, type ObservabilityOptions, type OffsetPaginationOptions, type OperationDescriptor, type OperationMetric, type OperationOptions, type PaginationConfig, PaginationEngine, type ParsedQuery, type PartialBy, type Plugin, type PluginFunction, type PluginType, type PolicyKey, type PopulateOption, type PopulateSpec, type PrefixedIdOptions, type PrioritizedHook, type PurgeMode, type PurgeableRepoLike, QueryParser, type QueryParserOptions, 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 SoftDeleteFilterMode, type SoftDeleteMethods, type SoftDeleteOptions, type SoftDeleteRepository, type SortDirection, type SortSpec, type Strict, type SubdocumentMethods, type TenantContext, type TenantStore, type TransitionMachine, type UpdateManyResult, type UpdateOptions, type UpdatePatch, type UpdateWithValidationResult, type UserContext, type ValidationChainOptions, type ValidatorDefinition, type ValueType, type WithPlugins, type WithTransactionOptions, index_d_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, cascadePurgeReferences, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isDuplicateKeyError, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, repoOptionsFromCtx, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, supportsTransactions, systemContext, timestampPlugin, toPlain, uniqueField, validationChainPlugin, withTransaction };
package/dist/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  import { n as idVariants } from "./id-resolution-BqDrk0S2.mjs";
2
- import { a as withTransaction, c as MUTATING_OPERATIONS, d as operationsByPolicyKey, f as MONGOKIT_CAPABILITIES, i as isTransactionUnsupported, l as OP_REGISTRY, n as Repository, o as AggregationBuilder, p as createMongoPurgePort, r as batchTransaction, s as ALL_OPERATIONS, t as HOOK_PRIORITY, u as READ_OPERATIONS } from "./Repository-KKR8jxfD.mjs";
2
+ import { a as supportsTransactions, c as ALL_OPERATIONS, d as READ_OPERATIONS, f as operationsByPolicyKey, i as isTransactionUnsupported, l as MUTATING_OPERATIONS, m as createMongoPurgePort, n as Repository, o as withTransaction, p as MONGOKIT_CAPABILITIES, r as batchTransaction, s as AggregationBuilder, t as HOOK_PRIORITY, u as OP_REGISTRY } from "./Repository-BFBTAMlA.mjs";
3
3
  import { b as LookupBuilder, x as compileFilterToMongo } from "./update-1Lq9OMD2.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 methodRegistryPlugin, c as getNextSequence, d as cachePlugin, f as batchOperationsPlugin, g as auditLogPlugin, h as ensureAuditTrailReady, i as multiTenantPlugin, l as prefixedId, m as auditTrailPlugin, n as softDeletePlugin, o as customIdPlugin, p as AuditTrailQuery, r as adminBypass, s as dateSequentialId, t as timestampPlugin, u as sequentialId } from "./timestamp.plugin-B8252GuQ.mjs";
7
+ import { a as methodRegistryPlugin, c as getNextSequence, d as cachePlugin, f as batchOperationsPlugin, g as auditLogPlugin, h as ensureAuditTrailReady, i as multiTenantPlugin, l as prefixedId, m as auditTrailPlugin, n as softDeletePlugin, o as customIdPlugin, p as AuditTrailQuery, r as adminBypass, s as dateSequentialId, t as timestampPlugin, u as sequentialId } from "./timestamp.plugin-ennbatTW.mjs";
8
8
  import { t as PaginationEngine } from "./PaginationEngine-CczbvCAq.mjs";
9
9
  import { isGeoOperator, parseGeoFilter } from "./query/primitives/geo.mjs";
10
- import { t as createRepository } from "./create-repository-DA5VJh-3.mjs";
11
- import { _ as appendOnlyPlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as cascadePlugin, h as elasticSearchPlugin, i as requireField, l as observabilityPlugin, m as fieldFilterPlugin, 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 aggregateHelpersPlugin } from "./validation-chain.plugin-C3Vwp-Rk.mjs";
10
+ import { t as createRepository } from "./create-repository-2DMwkYVz.mjs";
11
+ 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 immutableStatesPlugin, n as blockIf, o as validationChainPlugin, p as idempotencyStorePlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "./validation-chain.plugin-k9joH2qw.mjs";
12
12
  import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-FSQFLpsU.mjs";
13
13
  import { buildFieldTypeMap, coerceFieldValue, coerceHeuristic } from "./query/primitives/coercion.mjs";
14
14
  import { extractSchemaIndexes } from "./query/primitives/indexes.mjs";
@@ -1611,4 +1611,4 @@ function systemContext(extra) {
1611
1611
  */
1612
1612
  var src_default = Repository;
1613
1613
  //#endregion
1614
- export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, OP_REGISTRY, PaginationEngine, QueryParser, READ_OPERATIONS, Repository, actions_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, cascadePurgeReferences, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, src_default as default, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, idVariants, immutableField, immutableStatesPlugin, isDuplicateKeyError, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, repoOptionsFromCtx, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, systemContext, timestampPlugin, toPlain, uniqueField, validationChainPlugin, withTransaction };
1614
+ export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, OP_REGISTRY, PaginationEngine, QueryParser, READ_OPERATIONS, Repository, actions_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, cascadePlugin, cascadePurgeReferences, compileFilterToMongo, configureLogger, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, src_default as default, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, getNextSequence, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isDuplicateKeyError, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, repoOptionsFromCtx, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, supportsTransactions, systemContext, timestampPlugin, toPlain, uniqueField, validationChainPlugin, withTransaction };
@@ -1,6 +1,6 @@
1
1
  import { S as RepositoryPluginName, b as MONGOKIT_PLUGIN_NAMES, l as Plugin, x as MongoKitPluginName } from "../repository-DCHRWRXw.mjs";
2
2
  import { A as ensureAuditTrailReady, C as AuditTrailMethods, D as AuditTrailQueryOptions, E as AuditTrailQuery, O as EnsureAuditTrailReadyOptions, S as AuditQueryResult, T as AuditTrailOptions, _ as RepositoryCachePluginOptions, a as DateSequentialIdOptions, b as AuditOperation, c as SequentialIdOptions, d as getNextSequence, f as prefixedId, g as RepositoryCacheHandle, h as CacheOptions, i as CustomIdOptions, k as auditTrailPlugin, l as customIdPlugin, m as CacheAdapter, o as IdGenerator, p as sequentialId, r as multiTenantPlugin, s as PrefixedIdOptions, t as MultiTenantOptions, u as dateSequentialId, v as cachePlugin, w as AuditTrailMode, x as AuditQueryOptions, y as AuditEntry } from "../multi-tenant.plugin-DVyVGREb.mjs";
3
- import { A as elasticSearchPlugin, C as LeasePluginOptions, D as immutableStatesPlugin, E as ImmutableStatesPluginOptions, F as auditLogPlugin, I as AppendOnlyPluginOptions, L as appendOnlyPlugin, M as BatchOperationsMethods, O as fieldFilterPlugin, P as batchOperationsPlugin, R as AggregateHelpersMethods, S as LeaseMethods, T as ImmutableClaimView, _ as observabilityPlugin, a as uniqueField, b as MethodRegistryRepository, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as cascadePlugin, k as ElasticSearchOptions, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MongoOperationsMethods, w as leasePlugin, x as methodRegistryPlugin, y as mongoOperationsPlugin, z as aggregateHelpersPlugin } from "../validation-chain.plugin-DxD15h-2.mjs";
3
+ import { A as ImmutableStatesPluginOptions, B as AppendOnlyPluginOptions, C as LeasePluginOptions, D as IdempotencyStorePluginOptions, E as IdempotencyStoreMethods, F as cascadePlugin, H as AggregateHelpersMethods, I as BatchOperationsMethods, M as fieldFilterPlugin, N as ElasticSearchOptions, O as idempotencyStorePlugin, P as elasticSearchPlugin, R as batchOperationsPlugin, S as LeaseMethods, T as IdempotencyClaim, U as aggregateHelpersPlugin, V as appendOnlyPlugin, _ as observabilityPlugin, a as uniqueField, b as MethodRegistryRepository, c as TenantContext, d as SubdocumentMethods, f as subdocumentPlugin, g as OperationMetric, h as ObservabilityOptions, i as requireField, j as immutableStatesPlugin, k as ImmutableClaimView, l as TenantStore, m as softDeletePlugin, n as blockIf, o as validationChainPlugin, p as SoftDeleteMethods, r as immutableField, s as timestampPlugin, t as autoInject, u as createTenantContext, v as MongoOperationsMethods, w as leasePlugin, x as methodRegistryPlugin, y as mongoOperationsPlugin, z as auditLogPlugin } from "../validation-chain.plugin-Cm0Ttnd8.mjs";
4
4
  import { ChangeLogStore } from "@classytic/repo-core/sync";
5
5
  //#region src/plugins/change-log.plugin.d.ts
6
6
  interface ChangeLogPluginOptions {
@@ -15,4 +15,4 @@ interface ChangeLogPluginOptions {
15
15
  }
16
16
  declare function changeLogPlugin(options: ChangeLogPluginOptions): Plugin;
17
17
  //#endregion
18
- export { type AggregateHelpersMethods, type AppendOnlyPluginOptions, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailMode, type AuditTrailOptions, AuditTrailQuery, type AuditTrailQueryOptions, type BatchOperationsMethods, type CacheAdapter, type CacheOptions, type ChangeLogPluginOptions, type CustomIdOptions, type DateSequentialIdOptions, type ElasticSearchOptions, type EnsureAuditTrailReadyOptions, type IdGenerator, type ImmutableClaimView, type ImmutableStatesPluginOptions, type LeaseMethods, type LeasePluginOptions, MONGOKIT_PLUGIN_NAMES, type MethodRegistryRepository, type MongoKitPluginName, type MongoOperationsMethods, type MultiTenantOptions, type ObservabilityOptions, type OperationMetric, type PrefixedIdOptions, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type RepositoryPluginName, type SequentialIdOptions, type SoftDeleteMethods, type SubdocumentMethods, type TenantContext, type TenantStore, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, changeLogPlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, getNextSequence, immutableField, immutableStatesPlugin, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
18
+ export { type AggregateHelpersMethods, type AppendOnlyPluginOptions, type AuditEntry, type AuditOperation, type AuditQueryOptions, type AuditQueryResult, type AuditTrailMethods, type AuditTrailMode, type AuditTrailOptions, AuditTrailQuery, type AuditTrailQueryOptions, type BatchOperationsMethods, type CacheAdapter, type CacheOptions, type ChangeLogPluginOptions, type CustomIdOptions, type DateSequentialIdOptions, type ElasticSearchOptions, type EnsureAuditTrailReadyOptions, type IdGenerator, type IdempotencyClaim, type IdempotencyStoreMethods, type IdempotencyStorePluginOptions, type ImmutableClaimView, type ImmutableStatesPluginOptions, type LeaseMethods, type LeasePluginOptions, MONGOKIT_PLUGIN_NAMES, type MethodRegistryRepository, type MongoKitPluginName, type MongoOperationsMethods, type MultiTenantOptions, type ObservabilityOptions, type OperationMetric, type PrefixedIdOptions, type RepositoryCacheHandle, type RepositoryCachePluginOptions, type RepositoryPluginName, type SequentialIdOptions, type SoftDeleteMethods, type SubdocumentMethods, type TenantContext, type TenantStore, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, changeLogPlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, getNextSequence, idempotencyStorePlugin, immutableField, immutableStatesPlugin, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
@@ -1,5 +1,5 @@
1
- import { a as methodRegistryPlugin, c as getNextSequence, d as cachePlugin, f as batchOperationsPlugin, g as auditLogPlugin, h as ensureAuditTrailReady, i as multiTenantPlugin, l as prefixedId, m as auditTrailPlugin, n as softDeletePlugin, o as customIdPlugin, p as AuditTrailQuery, s as dateSequentialId, t as timestampPlugin, u as sequentialId } from "../timestamp.plugin-B8252GuQ.mjs";
2
- import { _ as appendOnlyPlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as cascadePlugin, h as elasticSearchPlugin, i as requireField, l as observabilityPlugin, m as fieldFilterPlugin, 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 aggregateHelpersPlugin } from "../validation-chain.plugin-C3Vwp-Rk.mjs";
1
+ import { a as methodRegistryPlugin, c as getNextSequence, d as cachePlugin, f as batchOperationsPlugin, g as auditLogPlugin, h as ensureAuditTrailReady, i as multiTenantPlugin, l as prefixedId, m as auditTrailPlugin, n as softDeletePlugin, o as customIdPlugin, p as AuditTrailQuery, s as dateSequentialId, t as timestampPlugin, u as sequentialId } from "../timestamp.plugin-ennbatTW.mjs";
2
+ 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 immutableStatesPlugin, n as blockIf, o as validationChainPlugin, p as idempotencyStorePlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "../validation-chain.plugin-k9joH2qw.mjs";
3
3
  //#region src/plugins/change-log.plugin.ts
4
4
  const PLUGIN_NAME = "changeLog";
5
5
  function isSkipped(context) {
@@ -70,4 +70,4 @@ function changeLogPlugin(options) {
70
70
  };
71
71
  }
72
72
  //#endregion
73
- export { AuditTrailQuery, MONGOKIT_PLUGIN_NAMES, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, changeLogPlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, getNextSequence, immutableField, immutableStatesPlugin, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
73
+ export { AuditTrailQuery, MONGOKIT_PLUGIN_NAMES, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, changeLogPlugin, createTenantContext, customIdPlugin, dateSequentialId, elasticSearchPlugin, ensureAuditTrailReady, fieldFilterPlugin, getNextSequence, idempotencyStorePlugin, immutableField, immutableStatesPlugin, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, prefixedId, requireField, sequentialId, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
@@ -1,4 +1,4 @@
1
- import { a as Repository, n as CreateRepositoryConfig } from "../create-repository-HjMD9h9_.mjs";
1
+ import { a as Repository, n as CreateRepositoryConfig } from "../create-repository-D4ZU94b3.mjs";
2
2
  import { Connection, Model, Schema } from "mongoose";
3
3
  import { MongoMemoryReplSet, MongoMemoryServer } from "mongodb-memory-server";
4
4
  //#region src/testkit/index.d.ts
@@ -1,4 +1,4 @@
1
- import { t as createRepository } from "../create-repository-DA5VJh-3.mjs";
1
+ import { t as createRepository } from "../create-repository-2DMwkYVz.mjs";
2
2
  import { createConnection } from "mongoose";
3
3
  //#region src/testkit/index.ts
4
4
  function idempotentStop(server) {
@@ -1,4 +1,4 @@
1
- import { l as OP_REGISTRY, s as ALL_OPERATIONS, t as HOOK_PRIORITY } from "./Repository-KKR8jxfD.mjs";
1
+ import { c as ALL_OPERATIONS, t as HOOK_PRIORITY, u as OP_REGISTRY } from "./Repository-BFBTAMlA.mjs";
2
2
  import { n as warn } from "./logger-K7dPmu2U.mjs";
3
3
  import { t as createError } from "./error-BuByZwKU.mjs";
4
4
  import { throwIfAborted, withRetry } from "@classytic/repo-core/repository";
@@ -226,6 +226,142 @@ interface ImmutableStatesPluginOptions {
226
226
  }
227
227
  declare function immutableStatesPlugin(options: ImmutableStatesPluginOptions): Plugin;
228
228
  //#endregion
229
+ //#region src/plugins/idempotency-store.plugin.d.ts
230
+ interface IdempotencyStorePluginOptions {
231
+ /** Field carrying the deterministic operation key. @default `'key'` */
232
+ keyField?: string;
233
+ /** Field carrying the row's claim status. @default `'status'` */
234
+ statusField?: string;
235
+ /** Field carrying the current holder's lease token. @default `'leaseToken'` */
236
+ leaseTokenField?: string;
237
+ /** Field carrying the lease expiry timestamp. @default `'leaseExpiresAt'` */
238
+ leaseExpiresAtField?: string;
239
+ /** Field counting claim acquisitions (1 = first). @default `'attempts'` */
240
+ attemptsField?: string;
241
+ /** Field carrying saga resume data (Mixed). @default `'progress'` */
242
+ progressField?: string;
243
+ /** Field carrying the replayable success outcome (Mixed). @default `'result'` */
244
+ resultField?: string;
245
+ /** Field carrying the terminal failure code. @default `'failureCode'` */
246
+ failureCodeField?: string;
247
+ /** Field carrying the last error message (diagnostics). @default `'lastError'` */
248
+ lastErrorField?: string;
249
+ /** Status value of an in-flight claim. @default `'in_flight'` */
250
+ inFlightStatus?: string;
251
+ /** Terminal success status. @default `'succeeded'` */
252
+ succeededStatus?: string;
253
+ /** Terminal failure status. @default `'failed'` */
254
+ failedStatus?: string;
255
+ /** Claim lease (ms) — must outlive the longest guarded execution. @default 60_000 */
256
+ defaultLeaseMs?: number;
257
+ /**
258
+ * Re-acquisition ceiling. When > 0, an expired in-flight claim whose
259
+ * `attempts` already reached this value returns `exhausted` instead of
260
+ * re-acquiring — the bounded-retry escalation valve for operations whose
261
+ * external side effect is idempotent but may be wedged. 0 = unbounded
262
+ * (flow/order semantics: recovery always re-claims). @default 0
263
+ */
264
+ maxAttempts?: number;
265
+ }
266
+ /** Outcome of `claimKey()` — the claim decision matrix. */
267
+ type IdempotencyClaim<TProgress = Record<string, unknown>, TResult = unknown> =
268
+ /**
269
+ * This caller owns the lease — execute. `progress` present ⇒ a stale-lease
270
+ * re-acquire after a previous attempt persisted resume data (crash
271
+ * recovery): resume from it instead of re-running completed pivots.
272
+ * `attempt` is 1 on a fresh claim.
273
+ */
274
+ {
275
+ kind: 'acquired';
276
+ leaseToken: string;
277
+ attempt: number;
278
+ progress?: TProgress;
279
+ } |
280
+ /** A previous execution succeeded — replay its stored outcome. */
281
+ {
282
+ kind: 'completed';
283
+ result?: TResult;
284
+ } |
285
+ /** A previous execution failed terminally — replay the recorded code. */
286
+ {
287
+ kind: 'failed';
288
+ failureCode?: string;
289
+ error?: string;
290
+ } |
291
+ /** Another caller holds a live lease. `progress` (when persisted) lets a
292
+ * domain layer act on completed pivots without owning the lease — e.g.
293
+ * finish an idempotent post-pivot sync while the holder is wedged. */
294
+ {
295
+ kind: 'busy';
296
+ progress?: TProgress;
297
+ leaseToken?: string;
298
+ } |
299
+ /** `maxAttempts` re-acquisitions exhausted — escalate to a human. */
300
+ {
301
+ kind: 'exhausted';
302
+ attempts: number;
303
+ progress?: TProgress;
304
+ };
305
+ /**
306
+ * Methods contributed by `idempotencyStorePlugin()`. Use as a type assertion
307
+ * when constructing the repo (same convention as `LeaseMethods`).
308
+ */
309
+ interface IdempotencyStoreMethods<TProgress = Record<string, unknown>, TResult = unknown> {
310
+ /**
311
+ * Atomically claim `key`. Exactly one concurrent caller acquires; the rest
312
+ * branch on the row's durable state — see {@link IdempotencyClaim}.
313
+ *
314
+ * `seed` merges extra fields into the row on FIRST insert only (domain
315
+ * columns, tenant stamp). It never mutates an existing row.
316
+ */
317
+ claimKey(key: string, opts?: {
318
+ leaseMs?: number;
319
+ seed?: Record<string, unknown>;
320
+ }): Promise<IdempotencyClaim<TProgress, TResult>>;
321
+ /**
322
+ * Persist saga resume data (post-pivot, pre-complete). Lease-token-guarded —
323
+ * a stale crashed holder can't clobber a re-acquirer's record. Optional
324
+ * `where` adds domain guards to the CAS filter.
325
+ */
326
+ saveClaimProgress(key: string, leaseToken: string, progress: TProgress, opts?: {
327
+ where?: Record<string, unknown>;
328
+ }): Promise<boolean>;
329
+ /**
330
+ * CAS the claim to the terminal SUCCESS status, storing `result` for
331
+ * replay. Loses cleanly (returns false) when the lease was re-acquired.
332
+ */
333
+ completeClaim(key: string, leaseToken: string, result?: TResult, opts?: {
334
+ where?: Record<string, unknown>;
335
+ }): Promise<boolean>;
336
+ /**
337
+ * CAS the claim to the terminal FAILURE status. Later claims replay
338
+ * `failureCode` instead of re-executing. Use ONLY for definitive failures
339
+ * where the external side effect is known NOT to have happened.
340
+ */
341
+ failClaim(key: string, leaseToken: string, failure: {
342
+ code: string;
343
+ error?: string;
344
+ }, opts?: {
345
+ where?: Record<string, unknown>;
346
+ }): Promise<boolean>;
347
+ /**
348
+ * Delete the caller's own in-flight row on definitive PRE-side-effect
349
+ * failure so a retry re-executes cleanly. Never deletes a terminal row or
350
+ * a re-acquired lease.
351
+ */
352
+ releaseClaim(key: string, leaseToken: string): Promise<boolean>;
353
+ /**
354
+ * Record an AMBIGUOUS error and lapse the lease immediately (keeps the row
355
+ * in-flight) so a recovery sweep re-claims without waiting out the lease.
356
+ * The bridge between "this attempt is giving up" and "the operation is not
357
+ * over" — e.g. a network error mid-external-call.
358
+ */
359
+ expireClaim(key: string, leaseToken: string, opts?: {
360
+ error?: string;
361
+ }): Promise<boolean>;
362
+ }
363
+ declare function idempotencyStorePlugin(options?: IdempotencyStorePluginOptions): Plugin;
364
+ //#endregion
229
365
  //#region src/plugins/lease.plugin.d.ts
230
366
  interface LeasePluginOptions {
231
367
  /** Field carrying the row's status. @default `'status'` */
@@ -822,4 +958,4 @@ declare function immutableField(field: string): ValidatorDefinition;
822
958
  */
823
959
  declare function uniqueField(field: string, errorMessage?: string): ValidatorDefinition;
824
960
  //#endregion
825
- export { elasticSearchPlugin as A, LeasePluginOptions as C, immutableStatesPlugin as D, ImmutableStatesPluginOptions as E, auditLogPlugin as F, AppendOnlyPluginOptions as I, appendOnlyPlugin as L, BatchOperationsMethods as M, BulkWriteResult as N, fieldFilterPlugin as O, batchOperationsPlugin as P, AggregateHelpersMethods as R, LeaseMethods as S, ImmutableClaimView as T, observabilityPlugin as _, uniqueField as a, MethodRegistryRepository as b, TenantContext as c, SubdocumentMethods as d, subdocumentPlugin as f, OperationMetric as g, ObservabilityOptions as h, requireField as i, cascadePlugin as j, ElasticSearchOptions as k, TenantStore as l, softDeletePlugin as m, blockIf as n, validationChainPlugin as o, SoftDeleteMethods as p, immutableField as r, timestampPlugin as s, autoInject as t, createTenantContext as u, MongoOperationsMethods as v, leasePlugin as w, methodRegistryPlugin as x, mongoOperationsPlugin as y, aggregateHelpersPlugin as z };
961
+ export { ImmutableStatesPluginOptions as A, AppendOnlyPluginOptions as B, LeasePluginOptions as C, IdempotencyStorePluginOptions as D, IdempotencyStoreMethods as E, cascadePlugin as F, AggregateHelpersMethods as H, BatchOperationsMethods as I, BulkWriteResult as L, fieldFilterPlugin as M, ElasticSearchOptions as N, idempotencyStorePlugin as O, elasticSearchPlugin as P, batchOperationsPlugin as R, LeaseMethods as S, IdempotencyClaim as T, aggregateHelpersPlugin as U, appendOnlyPlugin as V, observabilityPlugin as _, uniqueField as a, MethodRegistryRepository as b, TenantContext as c, SubdocumentMethods as d, subdocumentPlugin as f, OperationMetric as g, ObservabilityOptions as h, requireField as i, immutableStatesPlugin as j, ImmutableClaimView as k, TenantStore as l, softDeletePlugin as m, blockIf as n, validationChainPlugin as o, SoftDeleteMethods as p, immutableField as r, timestampPlugin as s, autoInject as t, createTenantContext as u, MongoOperationsMethods as v, leasePlugin as w, methodRegistryPlugin as x, mongoOperationsPlugin as y, auditLogPlugin as z };
@@ -1,4 +1,4 @@
1
- import { l as OP_REGISTRY, s as ALL_OPERATIONS } from "./Repository-KKR8jxfD.mjs";
1
+ import { c as ALL_OPERATIONS, u as OP_REGISTRY } from "./Repository-BFBTAMlA.mjs";
2
2
  import { _ as upsert } from "./update-1Lq9OMD2.mjs";
3
3
  import { n as warn } from "./logger-K7dPmu2U.mjs";
4
4
  import { t as createError } from "./error-BuByZwKU.mjs";
@@ -610,6 +610,153 @@ function immutableStatesPlugin(options) {
610
610
  };
611
611
  }
612
612
  //#endregion
613
+ //#region src/plugins/idempotency-store.plugin.ts
614
+ function randomToken() {
615
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
616
+ globalThis.crypto.getRandomValues(bytes);
617
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
618
+ }
619
+ function idempotencyStorePlugin(options = {}) {
620
+ const keyField = options.keyField ?? "key";
621
+ const statusField = options.statusField ?? "status";
622
+ const leaseTokenField = options.leaseTokenField ?? "leaseToken";
623
+ const leaseExpiresAtField = options.leaseExpiresAtField ?? "leaseExpiresAt";
624
+ const attemptsField = options.attemptsField ?? "attempts";
625
+ const progressField = options.progressField ?? "progress";
626
+ const resultField = options.resultField ?? "result";
627
+ const failureCodeField = options.failureCodeField ?? "failureCode";
628
+ const lastErrorField = options.lastErrorField ?? "lastError";
629
+ const inFlightStatus = options.inFlightStatus ?? "in_flight";
630
+ const succeededStatus = options.succeededStatus ?? "succeeded";
631
+ const failedStatus = options.failedStatus ?? "failed";
632
+ const defaultLeaseMs = options.defaultLeaseMs ?? 6e4;
633
+ const maxAttempts = options.maxAttempts ?? 0;
634
+ return {
635
+ name: "idempotencyStore",
636
+ apply(repo) {
637
+ if (!repo.registerMethod) throw new Error("idempotencyStorePlugin requires methodRegistryPlugin. Add methodRegistryPlugin() before idempotencyStorePlugin().");
638
+ const asDoc = (d) => d;
639
+ repo.registerMethod("claimKey", async function(key, opts = {}) {
640
+ if (typeof key !== "string" || key.length === 0) throw createError(400, "idempotencyStorePlugin.claimKey: key must be a non-empty string");
641
+ const leaseMs = opts.leaseMs ?? defaultLeaseMs;
642
+ const now = /* @__PURE__ */ new Date();
643
+ const leaseToken = randomToken();
644
+ const { doc, created } = await this.getOrCreate({ [keyField]: key }, {
645
+ ...opts.seed ?? {},
646
+ [keyField]: key,
647
+ [statusField]: inFlightStatus,
648
+ [leaseTokenField]: leaseToken,
649
+ [leaseExpiresAtField]: new Date(now.getTime() + leaseMs),
650
+ [attemptsField]: 1
651
+ });
652
+ if (created) return {
653
+ kind: "acquired",
654
+ leaseToken,
655
+ attempt: 1
656
+ };
657
+ const row = asDoc(doc);
658
+ if (row[statusField] === succeededStatus) return {
659
+ kind: "completed",
660
+ result: row[resultField]
661
+ };
662
+ if (row[statusField] === failedStatus) return {
663
+ kind: "failed",
664
+ failureCode: row[failureCodeField],
665
+ error: row[lastErrorField]
666
+ };
667
+ const leaseExpiresAt = row[leaseExpiresAtField];
668
+ const progress = row[progressField];
669
+ if (leaseExpiresAt && leaseExpiresAt > now) return {
670
+ kind: "busy",
671
+ ...progress !== void 0 ? { progress } : {},
672
+ ...row[leaseTokenField] !== void 0 ? { leaseToken: row[leaseTokenField] } : {}
673
+ };
674
+ const attempts = row[attemptsField] ?? 1;
675
+ if (maxAttempts > 0 && attempts >= maxAttempts) return {
676
+ kind: "exhausted",
677
+ attempts,
678
+ ...progress !== void 0 ? { progress } : {}
679
+ };
680
+ const reclaimed = await this.findOneAndUpdate({
681
+ [keyField]: key,
682
+ [statusField]: inFlightStatus,
683
+ [leaseExpiresAtField]: { $lte: now }
684
+ }, {
685
+ $set: {
686
+ [leaseTokenField]: leaseToken,
687
+ [leaseExpiresAtField]: new Date(now.getTime() + leaseMs)
688
+ },
689
+ $inc: { [attemptsField]: 1 }
690
+ }, { returnDocument: "after" });
691
+ if (reclaimed) {
692
+ const rec = asDoc(reclaimed);
693
+ const recProgress = rec[progressField];
694
+ return {
695
+ kind: "acquired",
696
+ leaseToken,
697
+ attempt: rec[attemptsField] ?? attempts + 1,
698
+ ...recProgress !== void 0 ? { progress: recProgress } : {}
699
+ };
700
+ }
701
+ return {
702
+ kind: "busy",
703
+ ...progress !== void 0 ? { progress } : {}
704
+ };
705
+ });
706
+ repo.registerMethod("saveClaimProgress", async function(key, leaseToken, progress, opts = {}) {
707
+ return await this.findOneAndUpdate({
708
+ ...opts.where ?? {},
709
+ [keyField]: key,
710
+ [leaseTokenField]: leaseToken,
711
+ [statusField]: inFlightStatus
712
+ }, { $set: { [progressField]: progress } }, { returnDocument: "after" }) !== null;
713
+ });
714
+ repo.registerMethod("completeClaim", async function(key, leaseToken, result, opts = {}) {
715
+ return await this.findOneAndUpdate({
716
+ ...opts.where ?? {},
717
+ [keyField]: key,
718
+ [leaseTokenField]: leaseToken,
719
+ [statusField]: inFlightStatus
720
+ }, { $set: {
721
+ [statusField]: succeededStatus,
722
+ ...result !== void 0 ? { [resultField]: result } : {}
723
+ } }, { returnDocument: "after" }) !== null;
724
+ });
725
+ repo.registerMethod("failClaim", async function(key, leaseToken, failure, opts = {}) {
726
+ if (!failure || typeof failure.code !== "string" || failure.code.length === 0) throw createError(400, "idempotencyStorePlugin.failClaim: failure.code must be a non-empty string");
727
+ return await this.findOneAndUpdate({
728
+ ...opts.where ?? {},
729
+ [keyField]: key,
730
+ [leaseTokenField]: leaseToken,
731
+ [statusField]: inFlightStatus
732
+ }, { $set: {
733
+ [statusField]: failedStatus,
734
+ [failureCodeField]: failure.code,
735
+ ...failure.error !== void 0 ? { [lastErrorField]: failure.error } : {}
736
+ } }, { returnDocument: "after" }) !== null;
737
+ });
738
+ repo.registerMethod("releaseClaim", async function(key, leaseToken) {
739
+ const res = await this.deleteMany({
740
+ [keyField]: key,
741
+ [leaseTokenField]: leaseToken,
742
+ [statusField]: inFlightStatus
743
+ });
744
+ return (typeof res === "number" ? res : res?.deletedCount ?? 0) > 0;
745
+ });
746
+ repo.registerMethod("expireClaim", async function(key, leaseToken, opts = {}) {
747
+ return await this.findOneAndUpdate({
748
+ [keyField]: key,
749
+ [leaseTokenField]: leaseToken,
750
+ [statusField]: inFlightStatus
751
+ }, { $set: {
752
+ [leaseExpiresAtField]: /* @__PURE__ */ new Date(),
753
+ ...opts.error !== void 0 ? { [lastErrorField]: opts.error } : {}
754
+ } }, { returnDocument: "after" }) !== null;
755
+ });
756
+ }
757
+ };
758
+ }
759
+ //#endregion
613
760
  //#region src/plugins/lease.plugin.ts
614
761
  function leasePlugin(options = {}) {
615
762
  const statusField = options.statusField ?? "status";
@@ -1304,4 +1451,4 @@ function uniqueField(field, errorMessage) {
1304
1451
  };
1305
1452
  }
1306
1453
  //#endregion
1307
- export { appendOnlyPlugin as _, uniqueField as a, subdocumentPlugin as c, mongoOperationsPlugin as d, leasePlugin as f, cascadePlugin as g, elasticSearchPlugin as h, requireField as i, observabilityPlugin as l, fieldFilterPlugin as m, blockIf as n, validationChainPlugin as o, immutableStatesPlugin as p, immutableField as r, createTenantContext as s, autoInject as t, MONGOKIT_PLUGIN_NAMES as u, aggregateHelpersPlugin as v };
1454
+ export { cascadePlugin as _, uniqueField as a, subdocumentPlugin as c, mongoOperationsPlugin as d, leasePlugin as f, elasticSearchPlugin as g, fieldFilterPlugin as h, requireField as i, observabilityPlugin as l, immutableStatesPlugin as m, blockIf as n, validationChainPlugin as o, idempotencyStorePlugin as p, immutableField as r, createTenantContext as s, autoInject as t, MONGOKIT_PLUGIN_NAMES as u, appendOnlyPlugin as v, aggregateHelpersPlugin as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classytic/mongokit",
3
- "version": "3.25.0",
3
+ "version": "3.28.0",
4
4
  "description": "Production-grade MongoDB repositories - smart pagination, events, and plugins. Built on @classytic/repo-core.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -113,7 +113,7 @@
113
113
  },
114
114
  "homepage": "https://github.com/classytic/mongokit#readme",
115
115
  "peerDependencies": {
116
- "@classytic/repo-core": ">=0.14.0",
116
+ "@classytic/repo-core": ">=0.17.0",
117
117
  "mongoose": ">=9.4.1"
118
118
  },
119
119
  "engines": {
@@ -157,7 +157,7 @@
157
157
  "@better-auth/mongo-adapter": "^1.6.22",
158
158
  "@biomejs/biome": "^2.4.10",
159
159
  "@classytic/dev-tools": "^0.2.0",
160
- "@classytic/repo-core": "^0.14.0",
160
+ "@classytic/repo-core": "^0.17.0",
161
161
  "@types/node": "^22.0.0",
162
162
  "@vitest/coverage-v8": "^4.1.4",
163
163
  "ajv": "^8.18.0",