@classytic/mongokit 3.44.0 → 3.46.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 (48) hide show
  1. package/README.md +23 -3
  2. package/dist/{PaginationEngine-C9iS0U5l.mjs → PaginationEngine-BRlLTQQz.mjs} +1 -1
  3. package/dist/{PaginationEngine-D17AthKw.d.mts → PaginationEngine-BWb-c47a.d.mts} +1 -1
  4. package/dist/{Repository-B6oL6OMv.d.mts → Repository-DJuRmBNx.d.mts} +16 -7
  5. package/dist/{Repository-eMRYZ_2Y.mjs → Repository-DheA7oW-.mjs} +135 -29
  6. package/dist/actions/index.d.mts +1 -1
  7. package/dist/actions/index.mjs +2 -2
  8. package/dist/{actions-DExyXMTy.mjs → actions-DKQx9ToJ.mjs} +1 -1
  9. package/dist/ai/index.d.mts +1 -1
  10. package/dist/better-auth/index.d.mts +5 -2
  11. package/dist/better-auth/index.mjs +10 -20
  12. package/dist/{create-repository-D7MQpIZo.mjs → create-repository-C9DHHcl7.mjs} +3 -3
  13. package/dist/{create-repository-BOFUYj1l.d.mts → create-repository-DG-gwOf0.d.mts} +5 -5
  14. package/dist/{index-8QUEsOFI.d.mts → index-5mhQ63_B.d.mts} +2 -2
  15. package/dist/index.d.mts +19 -18
  16. package/dist/index.mjs +10 -10
  17. package/dist/kernel-conformance/index.d.mts +44 -3
  18. package/dist/kernel-conformance/index.mjs +43 -4
  19. package/dist/{method-registry.plugin-CGAepsBU.mjs → method-registry.plugin-BeJfQBJV.mjs} +2 -2
  20. package/dist/{multi-tenant.plugin-DfeDMiI8.d.mts → multi-tenant.plugin-BTPtCLNK.d.mts} +2 -2
  21. package/dist/{operations-Dw9OFV0V.d.mts → operations-D6XsHx4e.d.mts} +6 -7
  22. package/dist/outbox/index.d.mts +2 -2
  23. package/dist/outbox/index.mjs +2 -2
  24. package/dist/pagination/PaginationEngine.d.mts +1 -1
  25. package/dist/pagination/PaginationEngine.mjs +1 -1
  26. package/dist/pagination/utils/limits.d.mts +1 -1
  27. package/dist/{pagination-DAUg5zOY.d.mts → pagination-BAM9cA4N.d.mts} +5 -7
  28. package/dist/{plugin-options-Bpcuz9kS.d.mts → plugin-options-DrNGWV5b.d.mts} +2 -2
  29. package/dist/plugins/index.d.mts +4 -4
  30. package/dist/plugins/index.mjs +4 -4
  31. package/dist/{query-defaults-aUoKlEbh.mjs → query-defaults-DwvWUjZU.mjs} +16 -51
  32. package/dist/read-model/index.d.mts +1 -1
  33. package/dist/{repository-DzVIwhU2.d.mts → repository-B0aFVgP2.d.mts} +1 -1
  34. package/dist/{retention-purge-DqcYvRnG.d.mts → retention-purge-CCd_VXUf.d.mts} +4 -1
  35. package/dist/sync/index.d.mts +25 -9
  36. package/dist/sync/index.mjs +122 -12
  37. package/dist/testkit/index.d.mts +2 -2
  38. package/dist/testkit/index.mjs +1 -1
  39. package/dist/{timestamp.plugin-Bz_zL9PX.mjs → timestamp.plugin-B-IJrlYb.mjs} +1 -1
  40. package/dist/{to-plain-PO5r1Qoy.d.mts → to-plain-Do-8jTKC.d.mts} +2 -2
  41. package/dist/{transaction-C2PFxfIv.mjs → transaction-C6Oj941P.mjs} +14 -12
  42. package/dist/{update-DcfmNbJe.mjs → update-CYaJMu6j.mjs} +2 -4
  43. package/dist/utils/index.d.mts +1 -1
  44. package/dist/{validation-chain.plugin-B3gZCZj4.d.mts → validation-chain.plugin-COYrUdd8.d.mts} +19 -4
  45. package/dist/{validation-chain.plugin-DNY6MIF6.mjs → validation-chain.plugin-DJplVJ12.mjs} +10 -7
  46. package/package.json +11 -11
  47. package/skills/mongokit/references/plugins-tenancy.md +1 -0
  48. package/skills/mongokit/references/transactions-outbox.md +3 -1
package/dist/index.mjs CHANGED
@@ -1,21 +1,21 @@
1
1
  import { n as idVariants } from "./id-resolution-Cqh49d9J.mjs";
2
- import { a as ALL_OPERATIONS, c as READ_OPERATIONS, d as RETENTION_PURGE_ERROR_CODES, i as searchKeysPlugin, l as operationsByPolicyKey, n as Repository, o as MUTATING_OPERATIONS, r as AggregationBuilder, s as OP_REGISTRY, t as HOOK_PRIORITY, u as createMongoPurgePort } from "./Repository-eMRYZ_2Y.mjs";
3
- import { C as isCreateManyPartialError, g as LookupBuilder, v as compileFilterToMongo } from "./update-DcfmNbJe.mjs";
4
- import { t as actions_exports } from "./actions-DExyXMTy.mjs";
5
- import { a as supportsTransactions, c as MONGOKIT_CAPABILITIES, d as probeMongoCapabilities, f as resetCapabilityProbeCache, h as transactionResolutionOf, i as runTransaction, l as capabilitiesForSupport, m as resolveTransactionSupport, n as batchTransaction, o as withTransaction, p as resolveMongoCapabilities, r as isTransactionUnsupported, t as TRANSACTION_ERROR_CODES, u as classifyHelloReply } from "./transaction-C2PFxfIv.mjs";
6
- import { t as createRepository } from "./create-repository-D7MQpIZo.mjs";
2
+ import { a as ALL_OPERATIONS, c as READ_OPERATIONS, d as RETENTION_PURGE_ERROR_CODES, i as searchKeysPlugin, l as operationsByPolicyKey, n as Repository, o as MUTATING_OPERATIONS, r as AggregationBuilder, s as OP_REGISTRY, t as HOOK_PRIORITY, u as createMongoPurgePort } from "./Repository-DheA7oW-.mjs";
3
+ import { C as isCreateManyPartialError, g as LookupBuilder, v as compileFilterToMongo } from "./update-CYaJMu6j.mjs";
4
+ import { t as actions_exports } from "./actions-DKQx9ToJ.mjs";
5
+ import { a as supportsTransactions, c as MONGOKIT_CAPABILITIES, d as probeMongoCapabilities, f as resetCapabilityProbeCache, h as transactionResolutionOf, i as runTransaction, l as capabilitiesForSupport, m as resolveTransactionSupport, n as batchTransaction, o as withTransaction, p as resolveMongoCapabilities, r as isTransactionUnsupported, t as TRANSACTION_ERROR_CODES, u as classifyHelloReply } from "./transaction-C6Oj941P.mjs";
6
+ import { t as createRepository } from "./create-repository-C9DHHcl7.mjs";
7
7
  import { n as defineModel, r as isModelRegistered, t as ModelCollisionError } from "./model-registry-DrVfJ3Nt.mjs";
8
- import { c as PAGINATION_ERROR_CODES, h as resetPaginationDefaults, m as getPaginationDefaults, p as configurePaginationDefaults, t as PaginationEngine } from "./PaginationEngine-C9iS0U5l.mjs";
9
- import { C as ensureAuditTrailReady, S as auditTrailPlugin, _ as prefixedId, a as declareTenantContextKey, b as cachePlugin, c as tenantContextKeysOf, d as customIdPlugin, f as dateSequentialId, g as periodCounterKey, h as getNextSequence, i as multiTenantPlugin, l as civilPeriodKey, m as getNextPeriodSequence, n as softDeletePlugin, o as forwardScope, p as ensureSequenceAtLeast, r as adminBypass, s as isScopeKey, t as timestampPlugin, u as createSequenceCounter, v as readSequence, w as auditLogPlugin, x as AuditTrailQuery, y as sequentialId } from "./timestamp.plugin-Bz_zL9PX.mjs";
10
- import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "./validation-chain.plugin-DNY6MIF6.mjs";
11
- import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "./method-registry.plugin-CGAepsBU.mjs";
8
+ import { c as PAGINATION_ERROR_CODES, h as resetPaginationDefaults, m as getPaginationDefaults, p as configurePaginationDefaults, t as PaginationEngine } from "./PaginationEngine-BRlLTQQz.mjs";
9
+ import { C as ensureAuditTrailReady, S as auditTrailPlugin, _ as prefixedId, a as declareTenantContextKey, b as cachePlugin, c as tenantContextKeysOf, d as customIdPlugin, f as dateSequentialId, g as periodCounterKey, h as getNextSequence, i as multiTenantPlugin, l as civilPeriodKey, m as getNextPeriodSequence, n as softDeletePlugin, o as forwardScope, p as ensureSequenceAtLeast, r as adminBypass, s as isScopeKey, t as timestampPlugin, u as createSequenceCounter, v as readSequence, w as auditLogPlugin, x as AuditTrailQuery, y as sequentialId } from "./timestamp.plugin-B-IJrlYb.mjs";
10
+ import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "./validation-chain.plugin-DJplVJ12.mjs";
11
+ import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "./method-registry.plugin-BeJfQBJV.mjs";
12
12
  import { n as warn, t as configureLogger } from "./logger-hbWOlj_X.mjs";
13
13
  import { GEO_OPERATORS, parseGeoFilter } from "./query/primitives/geo.mjs";
14
14
  import { buildFieldTypeMap, coerceHeuristic } from "./query/primitives/coercion.mjs";
15
15
  import { n as isDuplicateKeyError, r as parseDuplicateKeyError, t as createError } from "./error-DjdmPLWO.mjs";
16
16
  import { extractSchemaIndexes } from "./query/primitives/indexes.mjs";
17
17
  import { n as declareCollectionScope, t as JOIN_ERROR_CODES } from "./join-scope-C1gOtZ59.mjs";
18
- import { a as assertQueryDefaultsConfigured, c as getQueryDefaults, l as resetQueryDefaults, o as configureAggregateDefaults, s as configureQueryDefaults, t as QUERY_DEFAULTS_ERROR_CODES, u as resolveQueryOptions } from "./query-defaults-aUoKlEbh.mjs";
18
+ import { a as configureAggregateDefaults, c as resetQueryDefaults, i as assertQueryDefaultsConfigured, l as resolveQueryOptions, o as configureQueryDefaults, s as getQueryDefaults, t as QUERY_DEFAULTS_ERROR_CODES } from "./query-defaults-DwvWUjZU.mjs";
19
19
  import { _ as tokenizeSearchText, d as buildSearchKeys, f as prefixSearchFilter, g as normalizeSearchText, h as findSearchMatches, l as DEFAULT_SEARCH_KEYS_FIELD, m as searchKeysPath, n as SearchKeysWriteError, p as searchKeysIndex, t as SEARCH_KEYS_ERROR_CODES, u as MAX_PREFIX_SEARCH_TERMS } from "./derive-Dxsc31fV.mjs";
20
20
  import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-DPFaFw9Y.mjs";
21
21
  import "./search/index.mjs";
@@ -37,14 +37,16 @@ export declare const KERNEL_CONFORMANCE_CHECKS: readonly [
37
37
  /** §11.5 — an injected outbox that does not enlist `ctx.session` is REFUSED. (conditional) */
38
38
  'outbox-requires-transactional-save',
39
39
  /** §11.10 — a blueprint with optional modules off registers no extra models. (conditional) */
40
- 'optional-modules-register-no-models'];
40
+ 'optional-modules-register-no-models',
41
+ /** scale D7 — every unique index leads with the tenant field, or is a registered global. (conditional) */
42
+ 'unique-indexes-tenant-prefixed'];
41
43
  type ConformanceCheck = (typeof KERNEL_CONFORMANCE_CHECKS)[number];
42
44
  /**
43
45
  * Checks that only run when the kernel supplies the input they need. When the input is
44
46
  * absent they are reported as NOT EXERCISED — a visible marker test plus a report entry,
45
47
  * never a silent pass.
46
48
  */
47
- export declare const CONDITIONAL_CHECKS: readonly ["bind-verifies-requirements", "outbox-requires-transactional-save", "optional-modules-register-no-models"];
49
+ export declare const CONDITIONAL_CHECKS: readonly ["bind-verifies-requirements", "outbox-requires-transactional-save", "optional-modules-register-no-models", "unique-indexes-tenant-prefixed"];
48
50
  /**
49
51
  * The two runner functions the suite needs, INJECTED by the consumer.
50
52
  *
@@ -118,6 +120,23 @@ interface KernelConformanceReport {
118
120
  readonly skipped: readonly ConformanceCheckStatus[];
119
121
  readonly notExercised: readonly ConformanceCheckStatus[];
120
122
  }
123
+ /** What becomes of a deliberately global unique index once a deployment is sharded or podded (D7). */
124
+ type GlobalUniqueDisposition = 'shard-key-prefix' | 'identity-registry' | 'pod-local' | 'drop';
125
+ /** A unique index that is global ON PURPOSE. A bare name is not enough: the reason is the contract. */
126
+ interface AllowedGlobalUnique {
127
+ /** The index NAME as declared (`options.name`), or its keys joined by `,` when unnamed. */
128
+ readonly index: string;
129
+ readonly reason: string;
130
+ readonly t2: GlobalUniqueDisposition;
131
+ }
132
+ interface TenantUniquesOptions {
133
+ /** The tenant field the kernel prefixes with, e.g. `'organizationId'`. */
134
+ readonly tenantField: string;
135
+ /** Bind the kernel with tenancy ENABLED (field strategy) on the connection the suite supplies. */
136
+ readonly bindScoped: (connection: Connection) => unknown | Promise<unknown>;
137
+ /** Deliberate globals. Empty (the default) means every unique index must be tenant-led. */
138
+ readonly allowGlobal?: readonly AllowedGlobalUnique[];
139
+ }
121
140
  interface KernelConformanceOptions<TBlueprint extends KernelBlueprintLike, TEngine> {
122
141
  /** Kernel name, e.g. `'party'`. Used in test titles and to derive legacy export names. */
123
142
  readonly name: string;
@@ -215,6 +234,28 @@ interface KernelConformanceOptions<TBlueprint extends KernelBlueprintLike, TEngi
215
234
  * `optional-modules-register-no-models` (§11.10).
216
235
  */
217
236
  readonly minimalBlueprint?: () => TBlueprint;
237
+ /**
238
+ * Enables `unique-indexes-tenant-prefixed` (scale decision D7).
239
+ *
240
+ * MongoDB refuses `shardCollection` on a tenant key when ANY unique index does not start with
241
+ * that key, so one global unique makes the whole collection unshardable by tenant and turns a
242
+ * later move to pods or shards into a data migration. The check binds the kernel with tenancy
243
+ * ON, reads every registered schema's indexes, and requires each unique one to lead with the
244
+ * tenant field, or to be registered in `allowGlobal` with a reason and a T2 disposition.
245
+ *
246
+ * It also fails when it inspected no unique index at all (a scan that finds nothing proves
247
+ * nothing), and when an `allowGlobal` entry names an index that no longer exists or is now
248
+ * tenant-led (a stale entry would hide a real one).
249
+ *
250
+ * ```ts
251
+ * tenantUniques: {
252
+ * tenantField: 'organizationId',
253
+ * bindScoped: (connection) => defineX({ tenant: { enabled: true } }).bind(connection, runtime),
254
+ * allowGlobal: [{ index: 'directory_site_slug', reason: 'public slug resolved without a tenant', t2: 'identity-registry' }],
255
+ * }
256
+ * ```
257
+ */
258
+ readonly tenantUniques?: TenantUniquesOptions;
218
259
  /**
219
260
  * Explicit, reasoned opt-outs. Unknown or duplicate ids throw at call time. Every skip is
220
261
  * printed and registered as a visible marker test.
@@ -240,4 +281,4 @@ interface InstrumentedTransport {
240
281
  * "did I create this?", never by inspecting the object.
241
282
  */
242
283
  export declare function instrumentTransport(inner: EventTransportLike): InstrumentedTransport;
243
- export type { ConformanceCheck, ConformanceCheckOutcome, ConformanceCheckStatus, ConformanceConnect, ConformanceConnectionHandle, ConformanceEvent, ConformanceRunner, ConformanceSkip, EventTransportLike, InstrumentedTransport, KernelBindContext, KernelBlueprintLike, KernelConformanceOptions, KernelConformanceReport };
284
+ export type { AllowedGlobalUnique, ConformanceCheck, ConformanceCheckOutcome, ConformanceCheckStatus, ConformanceConnect, ConformanceConnectionHandle, ConformanceEvent, ConformanceRunner, ConformanceSkip, EventTransportLike, GlobalUniqueDisposition, InstrumentedTransport, KernelBindContext, KernelBlueprintLike, KernelConformanceOptions, KernelConformanceReport, TenantUniquesOptions };
@@ -104,7 +104,8 @@ const KERNEL_CONFORMANCE_CHECKS = [
104
104
  "maintenance-fails-loud",
105
105
  "bind-verifies-requirements",
106
106
  "outbox-requires-transactional-save",
107
- "optional-modules-register-no-models"
107
+ "optional-modules-register-no-models",
108
+ "unique-indexes-tenant-prefixed"
108
109
  ];
109
110
  /**
110
111
  * Checks that only run when the kernel supplies the input they need. When the input is
@@ -114,7 +115,8 @@ const KERNEL_CONFORMANCE_CHECKS = [
114
115
  const CONDITIONAL_CHECKS = [
115
116
  "bind-verifies-requirements",
116
117
  "outbox-requires-transactional-save",
117
- "optional-modules-register-no-models"
118
+ "optional-modules-register-no-models",
119
+ "unique-indexes-tenant-prefixed"
118
120
  ];
119
121
  // src/kernel-conformance/suite.ts
120
122
  /**
@@ -217,12 +219,14 @@ function describeKernelConformance(opts) {
217
219
  const conditionalInputs = {
218
220
  "bind-verifies-requirements": "bindWithUnmetRequirement",
219
221
  "outbox-requires-transactional-save": "bindWithNonTransactionalOutbox",
220
- "optional-modules-register-no-models": "minimalBlueprint"
222
+ "optional-modules-register-no-models": "minimalBlueprint",
223
+ "unique-indexes-tenant-prefixed": "tenantUniques"
221
224
  };
222
225
  const conditionalSupplied = {
223
226
  "bind-verifies-requirements": opts.bindWithUnmetRequirement !== void 0,
224
227
  "outbox-requires-transactional-save": opts.bindWithNonTransactionalOutbox !== void 0,
225
- "optional-modules-register-no-models": opts.minimalBlueprint !== void 0
228
+ "optional-modules-register-no-models": opts.minimalBlueprint !== void 0,
229
+ "unique-indexes-tenant-prefixed": opts.tenantUniques !== void 0
226
230
  };
227
231
  const statuses = KERNEL_CONFORMANCE_CHECKS.map((check) => {
228
232
  const reason = skipReasons.get(check);
@@ -589,6 +593,41 @@ function describeKernelConformance(opts) {
589
593
  assert(check, drift.extra.length === 0 && drift.missing.length === 0, `the minimal bind registered [${registered.join(", ")}] but declared [${minimal.modelNames.join(", ")}] — a disabled module still compiled a model`);
590
594
  });
591
595
  });
596
+ register("unique-indexes-tenant-prefixed", "every unique index leads with the tenant field, or is a registered global", async () => {
597
+ const check = "unique-indexes-tenant-prefixed";
598
+ const cfg = opts.tenantUniques;
599
+ const allowed = new Map((cfg.allowGlobal ?? []).map((g) => [g.index, g]));
600
+ for (const g of allowed.values()) assert(check, g.reason.trim().length > 0, `allowGlobal entry '${g.index}' has no reason — a global unique index without one is the defect this check exists to catch`);
601
+ await withConnection(async (connection) => {
602
+ const before = new Set(Object.keys(connection.models));
603
+ await opts.tenantUniques?.bindScoped(connection);
604
+ const offenders = [];
605
+ const seenGlobals = /* @__PURE__ */ new Set();
606
+ let inspected = 0;
607
+ for (const modelName of Object.keys(connection.models).filter((n) => !before.has(n))) {
608
+ const schema = connection.models[modelName]?.schema;
609
+ for (const [fields, options] of schema?.indexes() ?? []) {
610
+ if (!options?.unique) continue;
611
+ inspected++;
612
+ const keys = Object.keys(fields);
613
+ const name = String(options.name ?? keys.join(","));
614
+ if (keys[0] === cfg.tenantField) {
615
+ assert(check, !allowed.has(name), `allowGlobal lists '${name}' but it already leads with '${cfg.tenantField}' — remove the stale entry (it would hide a future global)`);
616
+ continue;
617
+ }
618
+ if (allowed.has(name)) {
619
+ seenGlobals.add(name);
620
+ continue;
621
+ }
622
+ offenders.push(`${modelName}.${name} {${keys.join(", ")}}`);
623
+ }
624
+ }
625
+ assert(check, offenders.length === 0, `unique index(es) that do not lead with '${cfg.tenantField}': [${offenders.join("; ")}]. MongoDB refuses shardCollection on a tenant key while any unique index lacks it. Prefix the index with the tenant field, or register it in tenantUniques.allowGlobal with a reason and a T2 disposition.`);
626
+ assert(check, inspected > 0, "no unique index was inspected — the scoped bind registered none, so this check proved nothing");
627
+ const stale = [...allowed.keys()].filter((n) => !seenGlobals.has(n));
628
+ assert(check, stale.length === 0, `allowGlobal names index(es) that no scoped model declares: [${stale.join(", ")}] — remove them`);
629
+ });
630
+ });
592
631
  });
593
632
  }
594
633
  function defaultReport(report) {
@@ -31,7 +31,8 @@ const BULK_WRITE_OPTIONS = [
31
31
  "bypassDocumentValidation",
32
32
  "comment",
33
33
  "let",
34
- "ignoreUndefined"
34
+ "ignoreUndefined",
35
+ "maxTimeMS"
35
36
  ];
36
37
  /**
37
38
  * Driver options that EXIST but this method does not forward.
@@ -51,7 +52,6 @@ const BULK_WRITE_OPTIONS = [
51
52
  const UNSUPPORTED_DRIVER_OPTIONS = /* @__PURE__ */ new Set([
52
53
  "forceServerObjectId",
53
54
  "timeoutMS",
54
- "maxTimeMS",
55
55
  "retryWrites",
56
56
  "checkKeys",
57
57
  "serializeFunctions",
@@ -1,5 +1,5 @@
1
- import { A as ObjectId } from "./pagination-DAUg5zOY.mjs";
2
- import { l as Plugin, p as RepositoryContext } from "./repository-DzVIwhU2.mjs";
1
+ import { A as ObjectId } from "./pagination-BAM9cA4N.mjs";
2
+ import { l as Plugin, p as RepositoryContext } from "./repository-B0aFVgP2.mjs";
3
3
  import mongoose, { Connection } from "mongoose";
4
4
  import { CacheAdapter as CacheAdapter$1, CacheOptions, RepositoryCacheHandle as RepositoryCacheHandle$1, RepositoryCachePluginOptions, cachePlugin } from "@classytic/repo-core/cache";
5
5
  import { TenantConfig } from "@classytic/repo-core/tenant";
@@ -1,4 +1,4 @@
1
- import { A as ObjectId, F as SortSpec, M as ReadPreferenceType, N as SelectSpec, b as WriteConcernSpec, i as CountStrategy, j as PopulateSpec, r as CollationOptions, v as ReadConcernLevel } from "./pagination-DAUg5zOY.mjs";
1
+ import { A as ObjectId, F as SortSpec, M as ReadPreferenceType, N as SelectSpec, b as WriteConcernSpec, i as CountStrategy, j as PopulateSpec, r as CollationOptions, v as ReadConcernLevel } from "./pagination-BAM9cA4N.mjs";
2
2
  import { DeleteManyResult, DeleteResult, LookupPopulateResult, UpdateManyResult } from "@classytic/repo-core/repository";
3
3
  import { PipelineStage, PopulateOptions } from "mongoose";
4
4
  import { Filter } from "@classytic/repo-core/filter";
@@ -378,12 +378,11 @@ interface CacheOperationOptions {
378
378
  cache?: import('@classytic/repo-core/cache').CacheOptions;
379
379
  }
380
380
  /**
381
- * `session.withTransaction()`'s own options param — `TransactionOptions` (readConcern,
382
- * writeConcern, readPreference, maxCommitTimeMS) PLUS the driver's `timeoutMS`, which is
383
- * NOT a field on `TransactionOptions` itself (verified against the installed mongodb
384
- * driver's `ClientSession.withTransaction` overload, mongodb.d.ts). `timeoutMS` bounds the
385
- * WHOLE `withTransaction` call, including its built-in retry loop (default 120s) — distinct
386
- * from `maxCommitTimeMS`, which only bounds a single `commitTransaction` server call.
381
+ * `TransactionOptions` (readConcern, writeConcern, readPreference, maxCommitTimeMS) PLUS
382
+ * `timeoutMS`: the budget for the WHOLE transaction, including its retry loop (default 120 s).
383
+ * It is enforced by mongokit, NOT the driver (the driver's `timeoutMS` is a no-op since 7.8):
384
+ * every mongokit operation inside is sent with `maxTimeMS` = what remains, and the commit with
385
+ * `maxCommitTimeMS` = what remained when the attempt started.
387
386
  */
388
387
  type ConvenientTransactionOptions = import('mongoose').mongo.TransactionOptions & {
389
388
  timeoutMS?: number;
@@ -1,5 +1,5 @@
1
- import { r as Repository } from "../Repository-B6oL6OMv.mjs";
2
- import { u as PaginationConfig } from "../pagination-DAUg5zOY.mjs";
1
+ import { r as Repository } from "../Repository-DJuRmBNx.mjs";
2
+ import { u as PaginationConfig } from "../pagination-BAM9cA4N.mjs";
3
3
  import { Connection, Model } from "mongoose";
4
4
  /** Delivery states. `dead_letter` is terminal and operator-visible. */
5
5
  export declare const OUTBOX_STATUSES: readonly ['pending', 'delivered', 'dead_letter'];
@@ -1,5 +1,5 @@
1
- import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-CGAepsBU.mjs";
2
- import { n as Repository } from "../Repository-eMRYZ_2Y.mjs";
1
+ import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-BeJfQBJV.mjs";
2
+ import { n as Repository } from "../Repository-DheA7oW-.mjs";
3
3
  import { Schema } from "mongoose";
4
4
  // src/outbox/index.ts
5
5
  /**
@@ -1,2 +1,2 @@
1
- import { n as PaginationEngine, t as CountMemoKey } from "../PaginationEngine-D17AthKw.mjs";
1
+ import { n as PaginationEngine, t as CountMemoKey } from "../PaginationEngine-BWb-c47a.mjs";
2
2
  export { CountMemoKey, PaginationEngine };
@@ -1 +1 @@
1
- export { t as PaginationEngine } from "../PaginationEngine-C9iS0U5l.mjs";
1
+ export { t as PaginationEngine } from "../PaginationEngine-BRlLTQQz.mjs";
@@ -1,4 +1,4 @@
1
- import { u as PaginationConfig } from "../../pagination-DAUg5zOY.mjs";
1
+ import { u as PaginationConfig } from "../../pagination-BAM9cA4N.mjs";
2
2
  /**
3
3
  * Validates and sanitizes limit value
4
4
  * Parses strings to numbers and prevents NaN bugs
@@ -1,4 +1,4 @@
1
- import { Connection, Document, Model, PipelineStage, PopulateOptions, Types } from "mongoose";
1
+ import { Document, Model, PipelineStage, PopulateOptions, Types } from "mongoose";
2
2
  /**
3
3
  * Core query/document primitives shared across every mongokit module —
4
4
  * sort/select/populate/filter specs and the mongoose-facing aliases.
@@ -99,17 +99,15 @@ declare function getQueryDefaults(): {
99
99
  /** Back to no deployment defaults. For tests. */
100
100
  declare function resetQueryDefaults(): void;
101
101
  /**
102
- * Boot check (M7): throws unless the deployment sets a query `maxTimeMS`, and — when a connection
103
- * is passed — unless its client carries a CSOT `timeoutMS`. Returns what it verified.
102
+ * Boot check (M7): throws unless the deployment sets a query `maxTimeMS`. Returns what it verified.
103
+ * A client `timeoutMS` is not checked: the driver (7.8+) drops it from the client options and only
104
+ * logs a process warning, so there is nothing to read; it bounds nothing either way.
104
105
  */
105
- declare function assertQueryDefaultsConfigured(connection?: Connection): {
106
+ declare function assertQueryDefaultsConfigured(): {
106
107
  maxTimeMS: number;
107
- timeoutMS?: number;
108
108
  };
109
109
  declare const QUERY_DEFAULTS_ERROR_CODES: {
110
110
  readonly NOT_CONFIGURED: 'mongokit.query_defaults.not_configured';
111
- /** A per-call bound inside a transaction that carries its own CSOT budget (the driver forbids both). */
112
- readonly TIMEOUT_IN_TRANSACTION: 'mongokit.query_defaults.timeout_in_transaction';
113
111
  };
114
112
  /** Resolve the options one command runs with. `repo` is the repository's own defaults. */
115
113
  declare function resolveQueryOptions(kind: CommandKind, perCall: PerCallQueryOptions, repo?: {
@@ -1,5 +1,5 @@
1
- import { A as ObjectId, F as SortSpec, N as SelectSpec, j as PopulateSpec } from "./pagination-DAUg5zOY.mjs";
2
- import { h as RepositoryInstance, p as RepositoryContext } from "./repository-DzVIwhU2.mjs";
1
+ import { A as ObjectId, F as SortSpec, N as SelectSpec, j as PopulateSpec } from "./pagination-BAM9cA4N.mjs";
2
+ import { h as RepositoryInstance, p as RepositoryContext } from "./repository-B0aFVgP2.mjs";
3
3
  import { OffsetPaginationResult } from "@classytic/repo-core/pagination";
4
4
  /** Field preset configuration (field-filter plugin) */
5
5
  interface FieldPreset {
@@ -1,7 +1,7 @@
1
- import { A as ImmutablePurgeEvent, B as elasticSearchPlugin, C as MethodRegistryRepository, D as LeasePluginOptions, E as LeasePendingFilter, F as IdempotencyStoreMethods, G as batchOperationsPlugin, H as BatchOperationsMethods, I as IdempotencyStorePluginOptions, J as AppendOnlyPurgeEvent, K as auditLogPlugin, L as idempotencyStorePlugin, M as ImmutableStatesPluginOptions, N as immutableStatesPlugin, O as leasePlugin, P as IdempotencyClaim, R as fieldFilterPlugin, S as mongoOperationsPlugin, T as LeaseMethods, V as cascadePlugin, X as AggregateHelpersMethods, Y as appendOnlyPlugin, Z as aggregateHelpersPlugin, _ as searchKeysPlugin, a as requireField, b as observabilityPlugin, c as timestampPlugin, d as createTenantContext, f as SubdocumentMethods, g as SearchKeysPluginOptions, h as softDeletePlugin, i as immutableField, j as ImmutablePurgeRequest, k as ImmutableClaimView, l as TenantContext, m as SoftDeleteMethods, n as autoInject, o as uniqueField, p as subdocumentPlugin, q as AppendOnlyPluginOptions, r as blockIf, s as validationChainPlugin, t as OperationType, u as TenantStore, v as ObservabilityOptions, w as methodRegistryPlugin, x as MongoOperationsMethods, y as OperationMetric, z as ElasticSearchOptions } from "../validation-chain.plugin-B3gZCZj4.mjs";
2
- import { A as cachePlugin, B as EnsureAuditTrailReadyOptions, C as prefixedId, D as CacheOptions, E as CacheAdapter, F as AuditTrailMethods, H as ensureAuditTrailReady, I as AuditTrailMode, L as AuditTrailOptions, M as AuditOperation, N as AuditQueryOptions, O as RepositoryCacheHandle, P as AuditQueryResult, R as AuditTrailQuery, T as sequentialId, V as auditTrailPlugin, _ as customIdPlugin, a as DateSequentialIdOptions, b as getNextPeriodSequence, i as CustomIdOptions, j as AuditEntry, k as RepositoryCachePluginOptions, m as SequentialIdOptions, o as IdGenerator, r as multiTenantPlugin, t as MultiTenantOptions, u as PrefixedIdOptions, v as dateSequentialId, x as getNextSequence, z as AuditTrailQueryOptions } from "../multi-tenant.plugin-DfeDMiI8.mjs";
3
- import { C as RepositoryPluginName, S as MongoKitPluginName, l as Plugin, x as MONGOKIT_PLUGIN_NAMES } from "../repository-DzVIwhU2.mjs";
4
- import { n as RetentionPurgeEvent, r as RetentionPurgeRequest, t as RETENTION_PURGE_ERROR_CODES } from "../retention-purge-DqcYvRnG.mjs";
1
+ import { A as ImmutablePurgeEvent, B as elasticSearchPlugin, C as MethodRegistryRepository, D as LeasePluginOptions, E as LeasePendingFilter, F as IdempotencyStoreMethods, G as batchOperationsPlugin, H as BatchOperationsMethods, I as IdempotencyStorePluginOptions, J as AppendOnlyPurgeEvent, K as auditLogPlugin, L as idempotencyStorePlugin, M as ImmutableStatesPluginOptions, N as immutableStatesPlugin, O as leasePlugin, P as IdempotencyClaim, R as fieldFilterPlugin, S as mongoOperationsPlugin, T as LeaseMethods, V as cascadePlugin, X as AggregateHelpersMethods, Y as appendOnlyPlugin, Z as aggregateHelpersPlugin, _ as searchKeysPlugin, a as requireField, b as observabilityPlugin, c as timestampPlugin, d as createTenantContext, f as SubdocumentMethods, g as SearchKeysPluginOptions, h as softDeletePlugin, i as immutableField, j as ImmutablePurgeRequest, k as ImmutableClaimView, l as TenantContext, m as SoftDeleteMethods, n as autoInject, o as uniqueField, p as subdocumentPlugin, q as AppendOnlyPluginOptions, r as blockIf, s as validationChainPlugin, t as OperationType, u as TenantStore, v as ObservabilityOptions, w as methodRegistryPlugin, x as MongoOperationsMethods, y as OperationMetric, z as ElasticSearchOptions } from "../validation-chain.plugin-COYrUdd8.mjs";
2
+ import { A as cachePlugin, B as EnsureAuditTrailReadyOptions, C as prefixedId, D as CacheOptions, E as CacheAdapter, F as AuditTrailMethods, H as ensureAuditTrailReady, I as AuditTrailMode, L as AuditTrailOptions, M as AuditOperation, N as AuditQueryOptions, O as RepositoryCacheHandle, P as AuditQueryResult, R as AuditTrailQuery, T as sequentialId, V as auditTrailPlugin, _ as customIdPlugin, a as DateSequentialIdOptions, b as getNextPeriodSequence, i as CustomIdOptions, j as AuditEntry, k as RepositoryCachePluginOptions, m as SequentialIdOptions, o as IdGenerator, r as multiTenantPlugin, t as MultiTenantOptions, u as PrefixedIdOptions, v as dateSequentialId, x as getNextSequence, z as AuditTrailQueryOptions } from "../multi-tenant.plugin-BTPtCLNK.mjs";
3
+ import { C as RepositoryPluginName, S as MongoKitPluginName, l as Plugin, x as MONGOKIT_PLUGIN_NAMES } from "../repository-B0aFVgP2.mjs";
4
+ import { n as RetentionPurgeEvent, r as RetentionPurgeRequest, t as RETENTION_PURGE_ERROR_CODES } from "../retention-purge-CCd_VXUf.mjs";
5
5
  import { ChangeLogStore } from "@classytic/repo-core/sync";
6
6
  interface ChangeLogPluginOptions {
7
7
  /** Durable feed implementing the repo-core/sync contract. */
@@ -1,7 +1,7 @@
1
- import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "../validation-chain.plugin-DNY6MIF6.mjs";
2
- import { C as ensureAuditTrailReady, S as auditTrailPlugin, _ as prefixedId, b as cachePlugin, d as customIdPlugin, f as dateSequentialId, h as getNextSequence, i as multiTenantPlugin, m as getNextPeriodSequence, n as softDeletePlugin, t as timestampPlugin, w as auditLogPlugin, x as AuditTrailQuery, y as sequentialId } from "../timestamp.plugin-Bz_zL9PX.mjs";
3
- import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-CGAepsBU.mjs";
4
- import { d as RETENTION_PURGE_ERROR_CODES, i as searchKeysPlugin } from "../Repository-eMRYZ_2Y.mjs";
1
+ import { _ as cascadePlugin, a as uniqueField, c as subdocumentPlugin, d as mongoOperationsPlugin, f as leasePlugin, g as elasticSearchPlugin, h as fieldFilterPlugin, i as requireField, l as observabilityPlugin, m as idempotencyStorePlugin, n as blockIf, o as validationChainPlugin, p as immutableStatesPlugin, r as immutableField, s as createTenantContext, t as autoInject, u as MONGOKIT_PLUGIN_NAMES, v as appendOnlyPlugin, y as aggregateHelpersPlugin } from "../validation-chain.plugin-DJplVJ12.mjs";
2
+ import { C as ensureAuditTrailReady, S as auditTrailPlugin, _ as prefixedId, b as cachePlugin, d as customIdPlugin, f as dateSequentialId, h as getNextSequence, i as multiTenantPlugin, m as getNextPeriodSequence, n as softDeletePlugin, t as timestampPlugin, w as auditLogPlugin, x as AuditTrailQuery, y as sequentialId } from "../timestamp.plugin-B-IJrlYb.mjs";
3
+ import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-BeJfQBJV.mjs";
4
+ import { d as RETENTION_PURGE_ERROR_CODES, i as searchKeysPlugin } from "../Repository-DheA7oW-.mjs";
5
5
  // src/plugins/change-log.plugin.ts
6
6
  const PLUGIN_NAME = "changeLog";
7
7
  /** Marks a repository already captured: the store and scope it writes to. */
@@ -9,13 +9,13 @@ import mongoose from "mongoose";
9
9
  * An explicit value always wins (FL1). For an aggregation the aggregate chain is consulted before
10
10
  * the generic query chain, because it is the more specific instruction.
11
11
  *
12
- * The bound travels as per-op CSOT `timeoutMS` (the driver derives the wire `maxTimeMS` from
13
- * it, and a client-level `timeoutMS` cannot override it). It and `comment` go to the commands Mongoose itself bounds with its global `maxTimeMS`
14
- * (find, findOne, countDocuments, distinct, findOneAndUpdate, aggregate:
15
- * lib/query.js:2465,2812,2905,3074,3610; lib/aggregate.js:1106). Writes get `writeConcern`. The
16
- * connection's CSOT `timeoutMS` bounds everything else; {@link assertQueryDefaultsConfigured}
17
- * checks both at boot. Inside a transaction a DEFAULT read/write concern or read preference is
18
- * dropped (the transaction owns them); a per-call one is passed and the driver judges it.
12
+ * The bound travels as the server-side `maxTimeMS` on the command (driver 7.8 made the client-side
13
+ * `timeoutMS` / CSOT a no-op, CVE-2026-119179, so there is no per-op client budget any more). It
14
+ * reaches find, findOne, countDocuments, distinct, findOneAndUpdate, aggregate, insertMany,
15
+ * update/delete and bulkWrite. A single-document `create()` goes through Mongoose `save()`, which
16
+ * forwards no `maxTimeMS`, so it is NOT server-bounded. Writes also get `writeConcern`.
17
+ * {@link assertQueryDefaultsConfigured} checks the deployment bound at boot. Inside a transaction a DEFAULT read/write concern or read
18
+ * preference is dropped (the transaction owns them); a per-call one is passed and the driver judges it.
19
19
  */
20
20
  /** On `globalThis`, for the reason `pagination/defaults.ts` gives: a duplicated install must share it. */
21
21
  const POLICY_SLOT = Symbol.for("classytic.mongokit.queryDefaults");
@@ -60,31 +60,19 @@ function resetQueryDefaults() {
60
60
  };
61
61
  }
62
62
  /**
63
- * Boot check (M7): throws unless the deployment sets a query `maxTimeMS`, and — when a connection
64
- * is passed — unless its client carries a CSOT `timeoutMS`. Returns what it verified.
63
+ * Boot check (M7): throws unless the deployment sets a query `maxTimeMS`. Returns what it verified.
64
+ * A client `timeoutMS` is not checked: the driver (7.8+) drops it from the client options and only
65
+ * logs a process warning, so there is nothing to read; it bounds nothing either way.
65
66
  */
66
- function assertQueryDefaultsConfigured(connection) {
67
+ function assertQueryDefaultsConfigured() {
67
68
  const maxTimeMS = policy().query.maxTimeMS;
68
69
  if (maxTimeMS === void 0) throw createError(500, "[mongokit] no deployment query time bound: call configureQueryDefaults({ maxTimeMS }) at boot", {
69
70
  code: QUERY_DEFAULTS_ERROR_CODES.NOT_CONFIGURED,
70
71
  meta: { missing: "maxTimeMS" }
71
72
  });
72
- if (!connection) return { maxTimeMS };
73
- const timeoutMS = connection.getClient().options.timeoutMS;
74
- if (!(typeof timeoutMS === "number" && timeoutMS > 0)) throw createError(500, "[mongokit] the connection has no CSOT timeoutMS: pass { timeoutMS } to mongoose.connect()", {
75
- code: QUERY_DEFAULTS_ERROR_CODES.NOT_CONFIGURED,
76
- meta: { missing: "timeoutMS" }
77
- });
78
- return {
79
- maxTimeMS,
80
- timeoutMS
81
- };
73
+ return { maxTimeMS };
82
74
  }
83
- const QUERY_DEFAULTS_ERROR_CODES = {
84
- NOT_CONFIGURED: "mongokit.query_defaults.not_configured",
85
- /** A per-call bound inside a transaction that carries its own CSOT budget (the driver forbids both). */
86
- TIMEOUT_IN_TRANSACTION: "mongokit.query_defaults.timeout_in_transaction"
87
- };
75
+ const QUERY_DEFAULTS_ERROR_CODES = { NOT_CONFIGURED: "mongokit.query_defaults.not_configured" };
88
76
  const TX_DEADLINE = Symbol.for("@classytic/mongokit/transaction-deadline");
89
77
  /**
90
78
  * Set (or clear) the transaction budget's deadline (`performance.now()` ms) on a session.
@@ -108,14 +96,6 @@ function inTransaction(session) {
108
96
  const s = session;
109
97
  return typeof s?.inTransaction === "function" && s.inTransaction();
110
98
  }
111
- /**
112
- * A `withTransaction` running under a CSOT budget: the driver refuses any per-op `timeoutMS`
113
- * there (mongodb lib/utils.js:451, `session.explicit && session.timeoutContext != null`).
114
- */
115
- function csotTransaction(session) {
116
- const s = session;
117
- return inTransaction(session) && s?.explicit === true && s.timeoutContext != null;
118
- }
119
99
  /** Resolve the options one command runs with. `repo` is the repository's own defaults. */
120
100
  function resolveQueryOptions(kind, perCall, repo = {}) {
121
101
  const deployment = policy();
@@ -132,7 +112,6 @@ function resolveQueryOptions(kind, perCall, repo = {}) {
132
112
  const budget = tx ? transactionRemaining(perCall.session) : void 0;
133
113
  if (budget !== void 0 && budget <= 0) throw new mongoose.mongo.MongoOperationTimeoutError("Transaction budget exhausted before this operation");
134
114
  if (kind !== "write") {
135
- if (csotTransaction(perCall.session) && perCall.maxTimeMS !== void 0) throw createError(400, "[mongokit] a per-call maxTimeMS cannot be honoured inside a transaction with its own timeoutMS budget; bound the transaction instead", { code: QUERY_DEFAULTS_ERROR_CODES.TIMEOUT_IN_TRANSACTION });
136
115
  const bound = tx ? perCall.maxTimeMS : perCall.maxTimeMS ?? (kind === "aggregate" ? repo.aggregate?.maxTimeMs ?? deployment.aggregate.maxTimeMs : void 0) ?? q.maxTimeMS;
137
116
  const capped = budget === void 0 ? bound : Math.min(bound ?? Number.POSITIVE_INFINITY, budget);
138
117
  if (capped !== void 0) out.maxTimeMS = capped;
@@ -174,34 +153,20 @@ function readPreferenceMode(value) {
174
153
  }
175
154
  /** Apply resolved options to a mongoose Query. */
176
155
  function applyToQuery(query, o) {
177
- if (o.maxTimeMS !== void 0) {
178
- const csot = { timeoutMS: o.maxTimeMS };
179
- query.setOptions(csot);
180
- }
156
+ if (o.maxTimeMS !== void 0) query.maxTimeMS(o.maxTimeMS);
181
157
  if (o.comment !== void 0) query.setOptions({ comment: o.comment });
182
158
  if (o.readPreference !== void 0) query.read(readPreferenceMode(o.readPreference));
183
159
  if (o.readConcern !== void 0) query.readConcern(o.readConcern);
184
160
  if (o.writeConcern !== void 0) query.setOptions({ writeConcern: o.writeConcern });
185
161
  if (o.hint !== void 0) query.hint(o.hint);
186
162
  }
187
- /**
188
- * Make a find cursor's bound apply PER BATCH (CSOT `timeoutMode: 'iteration'`). Only when some
189
- * `timeoutMS` exists (this op's, or the client's): the driver refuses a timeoutMode without one
190
- * (mongodb lib/cursor/abstract_cursor.js:114). In iteration mode the driver enforces the budget
191
- * client-side per batch and sends no wire maxTimeMS.
192
- */
193
- function applyCursorTimeoutMode(query, o, connection) {
194
- const clientTimeout = connection.getClient().options.timeoutMS;
195
- if (o.maxTimeMS === void 0 && clientTimeout === void 0) return;
196
- query.setOptions({ timeoutMode: "iteration" });
197
- }
198
163
  /** Apply resolved options to a mongoose Aggregate. */
199
164
  function applyToAggregate(aggregation, o) {
200
- if (o.maxTimeMS !== void 0) aggregation.option({ timeoutMS: o.maxTimeMS });
165
+ if (o.maxTimeMS !== void 0) aggregation.option({ maxTimeMS: o.maxTimeMS });
201
166
  if (o.comment !== void 0) aggregation.option({ comment: o.comment });
202
167
  if (o.readPreference !== void 0) aggregation.read(readPreferenceMode(o.readPreference));
203
168
  if (o.readConcern !== void 0) aggregation.readConcern(o.readConcern);
204
169
  if (o.allowDiskUse !== void 0) aggregation.allowDiskUse(o.allowDiskUse);
205
170
  if (o.hint !== void 0) aggregation.hint(o.hint);
206
171
  }
207
- export { assertQueryDefaultsConfigured as a, getQueryDefaults as c, setTransactionDeadline as d, applyToQuery as i, resetQueryDefaults as l, applyCursorTimeoutMode as n, configureAggregateDefaults as o, applyToAggregate as r, configureQueryDefaults as s, QUERY_DEFAULTS_ERROR_CODES as t, resolveQueryOptions as u };
172
+ export { configureAggregateDefaults as a, resetQueryDefaults as c, assertQueryDefaultsConfigured as i, resolveQueryOptions as l, applyToAggregate as n, configureQueryDefaults as o, applyToQuery as r, getQueryDefaults as s, QUERY_DEFAULTS_ERROR_CODES as t, setTransactionDeadline as u };
@@ -1,4 +1,4 @@
1
- import { r as Repository } from "../Repository-B6oL6OMv.mjs";
1
+ import { r as Repository } from "../Repository-DJuRmBNx.mjs";
2
2
  import { BulkUpsertResult } from "@classytic/repo-core/repository";
3
3
  export declare const READ_MODEL_ERROR_CODES: {
4
4
  readonly MERGE_IN_TRANSACTION: 'mongokit.read_model.merge_in_transaction';
@@ -1,4 +1,4 @@
1
- import { A as ObjectId, D as AnyDocument, F as SortSpec, M as ReadPreferenceType, N as SelectSpec, _ as QueryDefaults, b as WriteConcernSpec, i as CountStrategy, j as PopulateSpec, k as FilterQuery, p as AggregateDefaults, v as ReadConcernLevel } from "./pagination-DAUg5zOY.mjs";
1
+ import { A as ObjectId, D as AnyDocument, F as SortSpec, M as ReadPreferenceType, N as SelectSpec, _ as QueryDefaults, b as WriteConcernSpec, i as CountStrategy, j as PopulateSpec, k as FilterQuery, p as AggregateDefaults, v as ReadConcernLevel } from "./pagination-BAM9cA4N.mjs";
2
2
  import { r as DocField } from "./type-utils-CKvuewAL.mjs";
3
3
  import { DistributionConfig, DistributionMissHandler, RepoCapabilities, RepositoryBase } from "@classytic/repo-core/repository";
4
4
  import { Model, PipelineStage } from "mongoose";
@@ -1,4 +1,4 @@
1
- import { p as RepositoryContext } from "./repository-DzVIwhU2.mjs";
1
+ import { p as RepositoryContext } from "./repository-B0aFVgP2.mjs";
2
2
  /** Per-call retention purge request (`options.retentionPurge`). */
3
3
  interface RetentionPurgeRequest {
4
4
  /** Why the rows are removed (e.g. `'retention: 7 years'`). Required, non-blank. */
@@ -17,6 +17,8 @@ interface RetentionPurgeEvent {
17
17
  filter: Record<string, unknown> | undefined;
18
18
  /** Rows removed (`delete`: 1 or 0). */
19
19
  deletedCount: number;
20
+ /** True when the delete is an `archiveByFilter` chunk the sink acknowledged first. */
21
+ archived: boolean;
20
22
  /**
21
23
  * The delete's session, when the call ran in one. Write the audit row with it
22
24
  * (`{ session: event.session }`) so the delete and its audit commit or abort
@@ -31,5 +33,6 @@ declare const RETENTION_PURGE_ERROR_CODES: {
31
33
  readonly REASON_REQUIRED: 'RETENTION_PURGE_REASON_REQUIRED';
32
34
  readonly INVALID_ACTOR: 'RETENTION_PURGE_INVALID_ACTOR';
33
35
  readonly HARD_ONLY: 'RETENTION_PURGE_HARD_ONLY';
36
+ readonly ARCHIVE_UNATTESTED: 'RETENTION_PURGE_ARCHIVE_UNATTESTED';
34
37
  };
35
38
  export { RetentionPurgeEvent as n, RetentionPurgeRequest as r, RETENTION_PURGE_ERROR_CODES as t };
@@ -1,5 +1,5 @@
1
1
  import { ClientSession, Connection, Model } from "mongoose";
2
- import { ChangeEntry, ChangeLogAppendOptions, ChangeLogStore, ChangesSinceOptions, CommandStreamStore, CommandStreamTx } from "@classytic/repo-core/sync";
2
+ import { ChangeEntry, ChangeLogAppendOptions, ChangeLogStore, ChangesPage, ChangesSinceOptions, CommandStreamStore, CommandStreamTx } from "@classytic/repo-core/sync";
3
3
  export interface ChangeLogModelOptions {
4
4
  /** Entries collection. Default `sync_changes`; its counter lives in `<collection>_counter`. */
5
5
  collection?: string;
@@ -43,12 +43,21 @@ export interface ChangeLogModels {
43
43
  feed: string;
44
44
  }
45
45
  export declare function createChangeLogModels(connection: Connection, options?: ChangeLogModelOptions): ChangeLogModels;
46
+ /** {@link MongoChangeLogStore.snapshot}'s options: the reader's view, plus the bound after the first page. */
47
+ export interface ChangesSnapshotOptions extends ChangesSinceOptions {
48
+ /** The `upTo` the first page returned; omit it on the first page. */
49
+ readonly upTo?: string;
50
+ }
51
+ /** One snapshot page. `cursor` pages the snapshot; `upTo` is where `since()` resumes once `hasMore` is false. */
52
+ export interface ChangesSnapshotPage extends ChangesPage {
53
+ readonly upTo: string;
54
+ }
46
55
  /** The mongo change feed: the `ChangeLogStore` contract plus superseded-entry compaction. */
47
56
  export interface MongoChangeLogStore extends ChangeLogStore {
48
57
  /**
49
- * Delete every entry a later COMMITTED entry of the same `(tenantId, scope, docId)` replaced. Safe for any client
50
- * cursor: entries carry whole documents, so a reader only ever needs the latest one; tombstones and
51
- * each document's latest entry stay. Idempotent; run it on a schedule.
58
+ * Delete every entry of a `(tenantId, scope, docId)` but the one a reader ends on (the last
59
+ * tombstone, or the highest-version upsert after it). Safe for any client cursor: entries carry
60
+ * whole documents, and a reader would have skipped every deleted one. Idempotent; run it on a schedule.
52
61
  */
53
62
  compactSuperseded(options?: {
54
63
  batchSize?: number;
@@ -57,9 +66,8 @@ export interface MongoChangeLogStore extends ChangeLogStore {
57
66
  }>;
58
67
  /**
59
68
  * Retention with a horizon (the `ChangeLogStore.prune` contract). Deletes every entry older than
60
- * `before` (`at < before`) that a later entry of the same `(tenantId, scope, docId)` replaced:
61
- * each document's latest entry stays, tombstone or not, so a full reload still sees every
62
- * document. Records a HORIZON (the highest deleted cursor, never lowered) per `(tenantId, scope)`
69
+ * `before` (`at < before`) except, per `(tenantId, scope, docId)`, the one a reader ends on (as
70
+ * `compactSuperseded` keeps), so a snapshot still sees every document. Records a HORIZON (the highest deleted cursor, never lowered) per `(tenantId, scope)`
63
71
  * and feed-wide, BEFORE deleting, and returns the feed-wide one, `''` while nothing was ever
64
72
  * pruned. `since(cursor, options)` with a cursor below the horizon of what that reader can see
65
73
  * (its tenant, shared scopes and `scopes`; the feed-wide horizon when unfiltered; `''` included)
@@ -73,6 +81,14 @@ export interface MongoChangeLogStore extends ChangeLogStore {
73
81
  prune(before: Date, options?: {
74
82
  batchSize?: number;
75
83
  }): Promise<string>;
84
+ /**
85
+ * The resync bootstrap for a reader `since()` expired: each document's latest entry at or below
86
+ * `upTo` (the reader's horizon, fixed by the first page), tombstones left out, in feed order. Then
87
+ * `since(upTo)` carries on, delivering every change above it, deletes included. Complete because
88
+ * `prune` and `compactSuperseded` never delete a document's latest entry. If that `since(upTo)`
89
+ * expires again, a prune raised the horizon in between: bootstrap again.
90
+ */
91
+ snapshot(cursor: string, options?: ChangesSnapshotOptions): Promise<ChangesSnapshotPage>;
76
92
  /**
77
93
  * The compaction horizon cursor a reader with these options is held to (`''` while nothing it
78
94
  * can see was pruned); feed-wide without options.
@@ -94,8 +110,8 @@ export interface MongoChangeLogStore extends ChangeLogStore {
94
110
  * entry; `null` when the feed holds none. Reads only (it never runs the sequencer), so it is cheap on a hot
95
111
  * path, e.g. a writer that needs the last recorded state of a record before it appends the next change.
96
112
  * A document is `(tenantId, scope, docId)`: `tenantId` omitted or `null` reads the untenanted document.
97
- * Pass `session` to see what the caller staged or appended in its own transaction. A pruned or compacted
98
- * document still has its latest entry (both keep it).
113
+ * Pass `session` to see what the caller staged or appended in its own transaction. Pruning and
114
+ * compaction keep the entry a reader ends on, which may not be the last one recorded.
99
115
  */
100
116
  latestOf(scope: string, docId: string, options?: {
101
117
  tenantId?: string | null;