@classytic/mongokit 3.40.0 → 3.40.1

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 (46) hide show
  1. package/README.md +71 -0
  2. package/dist/{PaginationEngine-D-P19Y9m.d.mts → PaginationEngine-CRHyuUtw.d.mts} +17 -3
  3. package/dist/{PaginationEngine-Dvl8jcRY.mjs → PaginationEngine-CfT2Cxkj.mjs} +296 -137
  4. package/dist/{Repository-BMgPSkqZ.mjs → Repository-CYhSC9ut.mjs} +571 -175
  5. package/dist/{Repository-CR8Y9g2E.d.mts → Repository-pnLw1rG5.d.mts} +57 -16
  6. package/dist/actions/index.d.mts +1 -1
  7. package/dist/actions/index.mjs +1 -1
  8. package/dist/adapter/index.mjs +1 -1
  9. package/dist/{adapter-BrVfPFSW.mjs → adapter-CkMoMF83.mjs} +1 -1
  10. package/dist/ai/index.d.mts +1 -1
  11. package/dist/better-auth/index.mjs +2 -2
  12. package/dist/{create-repository-CAle1MSU.d.mts → create-repository-BUx9vfG9.d.mts} +5 -5
  13. package/dist/{create-repository-BzBUMvd1.mjs → create-repository-BYyWzLbt.mjs} +2 -2
  14. package/dist/{index-CmjNyulu.d.mts → index-SYF8N28D.d.mts} +37 -19
  15. package/dist/index.d.mts +55 -15
  16. package/dist/index.mjs +57 -11
  17. package/dist/join-scope-DSAznE8E.mjs +168 -0
  18. package/dist/{mongooseToJsonSchema-waUVQwK4.mjs → mongooseToJsonSchema-C3k4ide0.mjs} +5 -1
  19. package/dist/{multi-tenant.plugin-CBgCo9im.d.mts → multi-tenant.plugin-a3UhQARj.d.mts} +2 -2
  20. package/dist/{operations-CrRIoc8Z.d.mts → operations-XiFgG5US.d.mts} +36 -13
  21. package/dist/outbox/index.d.mts +1 -1
  22. package/dist/outbox/index.mjs +1 -1
  23. package/dist/pagination/PaginationEngine.d.mts +2 -2
  24. package/dist/pagination/PaginationEngine.mjs +1 -1
  25. package/dist/pagination/utils/limits.d.mts +1 -1
  26. package/dist/{pagination-BpIv2bdS.d.mts → pagination-D6psses0.d.mts} +116 -13
  27. package/dist/{plugin-options-Zk46lD6F.d.mts → plugin-options-CRhIvSzx.d.mts} +2 -2
  28. package/dist/plugins/index.d.mts +3 -3
  29. package/dist/plugins/index.mjs +2 -2
  30. package/dist/query-defaults-BxUcSOoF.mjs +208 -0
  31. package/dist/read-model/index.d.mts +66 -0
  32. package/dist/read-model/index.mjs +193 -0
  33. package/dist/{repository-odl03582.d.mts → repository-DNHfjAZX.d.mts} +16 -2
  34. package/dist/sync/index.d.mts +15 -2
  35. package/dist/sync/index.mjs +38 -1
  36. package/dist/testkit/index.d.mts +40 -3
  37. package/dist/testkit/index.mjs +113 -2
  38. package/dist/{timestamp.plugin-DF0gJB6p.mjs → timestamp.plugin-zfdLYZiO.mjs} +50 -31
  39. package/dist/{to-plain-k7SjNlSS.d.mts → to-plain-CjgEInWV.d.mts} +2 -2
  40. package/dist/{transaction-B5L9glO0.mjs → transaction-BrRYtH_b.mjs} +127 -5
  41. package/dist/{update-DG0fCwRJ.mjs → update-WgeAUJ_I.mjs} +44 -22
  42. package/dist/utils/index.d.mts +1 -1
  43. package/dist/utils/index.mjs +1 -1
  44. package/dist/{validation-chain.plugin-BLY2gLEk.mjs → validation-chain.plugin-CHUHWMtk.mjs} +15 -3
  45. package/dist/{validation-chain.plugin-DYp9E7K2.d.mts → validation-chain.plugin-CTcNJCmV.d.mts} +4 -4
  46. package/package.json +8 -3
package/dist/index.mjs CHANGED
@@ -1,23 +1,26 @@
1
1
  import { n as idVariants } from "./id-resolution-BqDrk0S2.mjs";
2
- import { a as MUTATING_OPERATIONS, c as operationsByPolicyKey, i as ALL_OPERATIONS, l as createMongoPurgePort, n as Repository, o as OP_REGISTRY, r as AggregationBuilder, s as READ_OPERATIONS, t as HOOK_PRIORITY } from "./Repository-BMgPSkqZ.mjs";
3
- import { C as isCreateManyPartialError, g as LookupBuilder, v as compileFilterToMongo } from "./update-DG0fCwRJ.mjs";
2
+ import { a as MUTATING_OPERATIONS, c as operationsByPolicyKey, i as ALL_OPERATIONS, l as createMongoPurgePort, n as Repository, o as OP_REGISTRY, r as AggregationBuilder, s as READ_OPERATIONS, t as HOOK_PRIORITY } from "./Repository-CYhSC9ut.mjs";
3
+ import { C as isCreateManyPartialError, g as LookupBuilder, v as compileFilterToMongo } from "./update-WgeAUJ_I.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
+ 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-BxUcSOoF.mjs";
6
7
  import { t as actions_exports } from "./actions/index.mjs";
7
- import { c as classifyHelloReply, d as resolveMongoCapabilities, f as resolveTransactionSupport, i as withTransaction, l as probeMongoCapabilities, n as isTransactionUnsupported, o as MONGOKIT_CAPABILITIES, p as transactionResolutionOf, r as supportsTransactions, s as capabilitiesForSupport, t as batchTransaction, u as resetCapabilityProbeCache } from "./transaction-B5L9glO0.mjs";
8
- import { _ as AuditTrailQuery, a as declareTenantContextKey, b as auditLogPlugin, c as tenantContextKeysOf, d as ensureSequenceAtLeast, f as getNextSequence, g as cachePlugin, h as sequentialId, i as multiTenantPlugin, l as customIdPlugin, m as readSequence, n as softDeletePlugin, o as forwardScope, p as prefixedId, r as adminBypass, s as isScopeKey, t as timestampPlugin, u as dateSequentialId, v as auditTrailPlugin, y as ensureAuditTrailReady } from "./timestamp.plugin-DF0gJB6p.mjs";
8
+ import { c as classifyHelloReply, d as resolveMongoCapabilities, f as resolveTransactionSupport, i as withTransaction, l as probeMongoCapabilities, n as isTransactionUnsupported, o as MONGOKIT_CAPABILITIES, p as transactionResolutionOf, r as supportsTransactions, s as capabilitiesForSupport, t as batchTransaction, u as resetCapabilityProbeCache } from "./transaction-BrRYtH_b.mjs";
9
+ import { _ as AuditTrailQuery, a as declareTenantContextKey, b as auditLogPlugin, c as tenantContextKeysOf, d as ensureSequenceAtLeast, f as getNextSequence, g as cachePlugin, h as sequentialId, i as multiTenantPlugin, l as customIdPlugin, m as readSequence, n as softDeletePlugin, o as forwardScope, p as prefixedId, r as adminBypass, s as isScopeKey, t as timestampPlugin, u as dateSequentialId, v as auditTrailPlugin, y as ensureAuditTrailReady } from "./timestamp.plugin-zfdLYZiO.mjs";
9
10
  import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "./method-registry.plugin-C63pGn1d.mjs";
10
- import { a as getPaginationDefaults, i as configurePaginationDefaults, o as resetPaginationDefaults, t as PaginationEngine } from "./PaginationEngine-Dvl8jcRY.mjs";
11
+ import { c as PAGINATION_ERROR_CODES, h as resetPaginationDefaults, m as getPaginationDefaults, p as configurePaginationDefaults, t as PaginationEngine } from "./PaginationEngine-CfT2Cxkj.mjs";
11
12
  import { GEO_OPERATORS, parseGeoFilter } from "./query/primitives/geo.mjs";
12
- import { t as createRepository } from "./create-repository-BzBUMvd1.mjs";
13
+ import { n as declareCollectionScope, t as JOIN_ERROR_CODES } from "./join-scope-DSAznE8E.mjs";
14
+ import { t as createRepository } from "./create-repository-BYyWzLbt.mjs";
13
15
  import { n as defineModel, r as isModelRegistered, t as ModelCollisionError } from "./model-registry-ClKtWiHa.mjs";
14
- 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-BLY2gLEk.mjs";
16
+ 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-CHUHWMtk.mjs";
15
17
  import { i as getMongooseProjection, n as filterResponseData, r as getFieldsForUser, t as createFieldPreset } from "./field-selection-FSQFLpsU.mjs";
16
18
  import { buildFieldTypeMap, coerceHeuristic } from "./query/primitives/coercion.mjs";
17
19
  import { extractSchemaIndexes } from "./query/primitives/indexes.mjs";
18
20
  import { n as createMemoryCache, t as toPlain } from "./to-plain-vksRrLtc.mjs";
19
- import { n as buildCrudSchemasFromMongooseSchema, t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-waUVQwK4.mjs";
21
+ import { n as buildCrudSchemasFromMongooseSchema, t as buildCrudSchemasFromModel } from "./mongooseToJsonSchema-C3k4ide0.mjs";
20
22
  import { runChunkedPurge } from "@classytic/repo-core/repository";
23
+ import { stableStringify } from "@classytic/repo-core/hash";
21
24
  import { OPERATOR_DESCRIPTIONS, QueryGrammarError, URL_OPERATORS, clausesToFilter, escapeRegex, isQueryGrammarError, readFilterClauses, readPageRequest, readSearch, readSelect, readSort } from "@classytic/repo-core/query-parser";
22
25
  //#region src/actions/cascade-references.ts
23
26
  /**
@@ -219,6 +222,45 @@ function defineModels(spec) {
219
222
  };
220
223
  }
221
224
  //#endregion
225
+ //#region src/pagination/keyset-source.ts
226
+ /**
227
+ * `keysetSource(repo, { name, sort, filters, ...scope })` — a mongokit repository as a repo-core
228
+ * `KeysetSource`, for `mergeKeysetPages`. Pages are `getAll({ mode: 'keyset' })` under the given
229
+ * scope; the per-row cursor is `repo.keysetCursor` (scope-bound like every mongokit cursor); the
230
+ * source's `scope` string binds the composite cursor to (collection, filters, tenant values).
231
+ */
232
+ function keysetSource(repo, options) {
233
+ const { name, sort, filters = {}, ...scope } = options;
234
+ const { session: _session, user: _user, ...tenant } = forwardScope(scope);
235
+ return {
236
+ name,
237
+ scope: `${repo.Model.collection.collectionName}:${stableStringify({
238
+ filters,
239
+ tenant
240
+ })}`,
241
+ async page({ after, limit }) {
242
+ const page = await repo.getAll({
243
+ ...scope,
244
+ filters,
245
+ sort,
246
+ limit,
247
+ mode: "keyset",
248
+ ...after ? { after } : {}
249
+ });
250
+ if (page.method !== "keyset") throw new Error("[mongokit] keysetSource: getAll did not return a keyset page");
251
+ return {
252
+ data: page.data,
253
+ hasMore: page.hasMore
254
+ };
255
+ },
256
+ cursorOf: (row) => repo.keysetCursor(row, {
257
+ ...scope,
258
+ sort,
259
+ filters
260
+ })
261
+ };
262
+ }
263
+ //#endregion
222
264
  //#region src/query/parser/pipeline-sanitizer.ts
223
265
  /**
224
266
  * Sanitize $match configuration to prevent dangerous operators.
@@ -1059,12 +1101,14 @@ var QueryParser = class {
1059
1101
  parse(query) {
1060
1102
  const rt = this.rt;
1061
1103
  const q = query ?? {};
1062
- const { page, limit, sort, populate, search, after, cursor, select, lookup, aggregate } = q;
1104
+ const { page, limit, sort, populate, search, after, cursor, before, mode, select, lookup, aggregate } = q;
1063
1105
  const pageRequest = guarded(rt, () => readPageRequest({
1064
1106
  page,
1065
1107
  limit,
1066
1108
  after,
1067
- cursor
1109
+ cursor,
1110
+ before,
1111
+ mode
1068
1112
  }, { maxLimit: rt.options.maxLimit }), readPageRequest({}, { maxLimit: rt.options.maxLimit }));
1069
1113
  const effectiveSort = sort === void 0 && isSortFieldAllowed(rt, "-createdAt") ? DEFAULT_PARSER_SORT : sort;
1070
1114
  const sanitizedSearch = sanitizeSearch(rt, search);
@@ -1092,6 +1136,8 @@ var QueryParser = class {
1092
1136
  if (rt.options.enableLookups && lookup) parsed.lookups = parseLookups(rt, lookup);
1093
1137
  if (rt.options.enableAggregations && aggregate) parsed.aggregation = parseAggregation(rt, aggregate);
1094
1138
  if (pageRequest.after !== void 0) parsed.after = pageRequest.after;
1139
+ if (pageRequest.before !== void 0) parsed.before = pageRequest.before;
1140
+ if (pageRequest.mode !== void 0) parsed.mode = pageRequest.mode;
1095
1141
  if (pageRequest.page !== void 0) parsed.page = pageRequest.page;
1096
1142
  return parsed;
1097
1143
  }
@@ -1391,4 +1437,4 @@ function systemContext(extra) {
1391
1437
  */
1392
1438
  var src_default = Repository;
1393
1439
  //#endregion
1394
- export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, LookupBuilder, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, ModelCollisionError, OP_REGISTRY, PaginationEngine, QueryParser, READ_OPERATIONS, Repository, actions_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, capabilitiesForSupport, cascadePlugin, cascadePurgeReferences, classifyHelloReply, compileFilterToMongo, configureLogger, configurePaginationDefaults, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, declareTenantContextKey, src_default as default, defineModel, defineModels, elasticSearchPlugin, ensureAuditTrailReady, ensureSequenceAtLeast, fieldFilterPlugin, filterResponseData, forwardScope, getFieldsForUser, getMongooseProjection, getNextSequence, getPaginationDefaults, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isCreateManyPartialError, isDuplicateKeyError, isModelRegistered, isTransactionUnsupported, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, probeMongoCapabilities, readSequence, repoOptionsFromCtx, requireField, resetCapabilityProbeCache, resetPaginationDefaults, resolveMongoCapabilities, resolveTransactionSupport, sequentialId, softDeletePlugin, subdocumentPlugin, supportsTransactions, systemContext, tenantContextKeysOf, timestampPlugin, toPlain, transactionResolutionOf, uniqueField, validationChainPlugin, withTransaction };
1440
+ export { ALL_OPERATIONS, AggregationBuilder, AuditTrailQuery, HOOK_PRIORITY, JOIN_ERROR_CODES, LookupBuilder, MONGOKIT_CAPABILITIES, MONGOKIT_PLUGIN_NAMES, MUTATING_OPERATIONS, ModelCollisionError, OP_REGISTRY, PAGINATION_ERROR_CODES, PaginationEngine, QUERY_DEFAULTS_ERROR_CODES, QueryParser, READ_OPERATIONS, Repository, actions_exports as actions, adminBypass, aggregateHelpersPlugin, appendOnlyPlugin, assertQueryDefaultsConfigured, auditLogPlugin, auditTrailPlugin, autoInject, batchOperationsPlugin, batchTransaction, blockIf, buildCrudSchemasFromModel, buildCrudSchemasFromMongooseSchema, cachePlugin, capabilitiesForSupport, cascadePlugin, cascadePurgeReferences, classifyHelloReply, compileFilterToMongo, configureAggregateDefaults, configureLogger, configurePaginationDefaults, configureQueryDefaults, createError, createFieldPreset, createMemoryCache, createOptionsExtractor, createRepository, createTenantContext, customIdPlugin, dateSequentialId, declareCollectionScope, declareTenantContextKey, src_default as default, defineModel, defineModels, elasticSearchPlugin, ensureAuditTrailReady, ensureSequenceAtLeast, fieldFilterPlugin, filterResponseData, forwardScope, getFieldsForUser, getMongooseProjection, getNextSequence, getPaginationDefaults, getQueryDefaults, idVariants, idempotencyStorePlugin, immutableField, immutableStatesPlugin, isCreateManyPartialError, isDuplicateKeyError, isModelRegistered, isTransactionUnsupported, keysetSource, leasePlugin, methodRegistryPlugin, mongoOperationsPlugin, multiTenantPlugin, observabilityPlugin, operationsByPolicyKey, parseDuplicateKeyError, prefixedId, probeMongoCapabilities, readSequence, repoOptionsFromCtx, requireField, resetCapabilityProbeCache, resetPaginationDefaults, resetQueryDefaults, resolveMongoCapabilities, resolveQueryOptions, resolveTransactionSupport, sequentialId, softDeletePlugin, subdocumentPlugin, supportsTransactions, systemContext, tenantContextKeysOf, timestampPlugin, toPlain, transactionResolutionOf, uniqueField, validationChainPlugin, withTransaction };
@@ -0,0 +1,168 @@
1
+ import { t as createError } from "./error-BuByZwKU.mjs";
2
+ import mongoose from "mongoose";
3
+ //#region src/repository/join-scope.ts
4
+ /**
5
+ * Join scope — a joined collection is read under ITS OWN policy, or not at all.
6
+ *
7
+ * Policy plugins (`multiTenantPlugin`, `softDeletePlugin`) declare a scope rule on the MODEL they
8
+ * are bound to, and every repository marks its model governed. {@link scopeJoins} walks a
9
+ * pipeline (nested sub-pipelines and `$facet` branches included) and, for each `$lookup`,
10
+ * `$unionWith` and `$graphLookup`, resolves `from` to the registered models that own that
11
+ * collection, then prepends the union of their rules (fail closed). It REFUSES, with a closed
12
+ * code, a `from` no model owns (the silent-empty-join: a wrong literal matches nothing and raises
13
+ * nothing) and a collection no repository governs. `unscopedJoins` names collections the caller
14
+ * asserts are company-wide; a collection with a declared rule cannot be unscoped that way —
15
+ * `bypassTenant` is the explicit cross-tenant form.
16
+ */
17
+ const JOIN_ERROR_CODES = {
18
+ UNKNOWN_COLLECTION: "mongokit.join.unknown_collection",
19
+ UNGOVERNED_COLLECTION: "mongokit.join.ungoverned_collection",
20
+ UNSCOPE_REFUSED: "mongokit.join.unscope_refused",
21
+ UNRESOLVABLE_FROM: "mongokit.join.unresolvable_from"
22
+ };
23
+ const RULES = Symbol.for("@classytic/mongokit/collection-scope-rules");
24
+ const GOVERNED = Symbol.for("@classytic/mongokit/governed-model");
25
+ const TENANT_FIELDS = Symbol.for("@classytic/mongokit/tenant-fields");
26
+ /** Called by a policy plugin at bind: reads of this model's collection carry `rule`. */
27
+ function declareCollectionScope(model, rule, meta = {}) {
28
+ const holder = model;
29
+ if (!holder[RULES]) Object.defineProperty(holder, RULES, {
30
+ value: /* @__PURE__ */ new Set(),
31
+ enumerable: false
32
+ });
33
+ holder[RULES]?.add(rule);
34
+ if (meta.tenantField) {
35
+ if (!holder[TENANT_FIELDS]) Object.defineProperty(holder, TENANT_FIELDS, {
36
+ value: /* @__PURE__ */ new Set(),
37
+ enumerable: false
38
+ });
39
+ holder[TENANT_FIELDS]?.add(meta.tenantField);
40
+ }
41
+ }
42
+ /** The tenant fields declared on this model by its repositories' tenant plugins. */
43
+ function tenantFieldsOf(model) {
44
+ return [...model[TENANT_FIELDS] ?? []];
45
+ }
46
+ /** Called by every Repository: this model's scope is known (possibly none). */
47
+ function markGoverned(model) {
48
+ const holder = model;
49
+ if (!holder[GOVERNED]) Object.defineProperty(holder, GOVERNED, {
50
+ value: true,
51
+ enumerable: false
52
+ });
53
+ }
54
+ function owners(connection, collection) {
55
+ return Object.values(connection.models).filter((m) => m.collection?.collectionName === collection);
56
+ }
57
+ function resemblance(connection, from) {
58
+ const pluralize = mongoose.pluralize();
59
+ const target = from.toLowerCase();
60
+ return Object.values(connection.models).filter((m) => {
61
+ const name = m.modelName.toLowerCase();
62
+ return name === target || `${name}s` === target || pluralize?.(m.modelName) === target;
63
+ }).map((m) => `model '${m.modelName}' lives in collection '${m.collection?.collectionName}'`).join("; ");
64
+ }
65
+ /** The predicate a join into `from` must carry, `undefined` for none; throws when unknown. */
66
+ function scopeForCollection(from, env) {
67
+ if (typeof from !== "string" || from.length === 0) throw createError(500, `[mongokit] a join's 'from' must be a collection name, got ${JSON.stringify(from)}`, {
68
+ code: JOIN_ERROR_CODES.UNRESOLVABLE_FROM,
69
+ meta: { from }
70
+ });
71
+ const unscoped = env.unscopedJoins?.includes(from) === true;
72
+ const found = owners(env.connection, from);
73
+ if (found.length === 0) {
74
+ if (unscoped) return void 0;
75
+ const hint = resemblance(env.connection, from);
76
+ throw createError(500, `[mongokit] join into '${from}': no registered model owns that collection, so the join would match nothing.${hint ? ` Did you mean: ${hint}?` : ""} Take 'from' from Model.collection.name.`, {
77
+ code: JOIN_ERROR_CODES.UNKNOWN_COLLECTION,
78
+ meta: {
79
+ from,
80
+ suggestion: hint || void 0
81
+ }
82
+ });
83
+ }
84
+ const governed = found.some((m) => m[GOVERNED] === true);
85
+ const rules = /* @__PURE__ */ new Set();
86
+ for (const m of found) for (const r of m[RULES] ?? []) rules.add(r);
87
+ if (!governed && rules.size === 0) {
88
+ if (unscoped) return void 0;
89
+ throw createError(500, `[mongokit] join into '${from}': no repository governs it, so its tenant/soft-delete scope is unknown. Build its repository, or list it in unscopedJoins if it is company-wide.`, {
90
+ code: JOIN_ERROR_CODES.UNGOVERNED_COLLECTION,
91
+ meta: {
92
+ from,
93
+ models: found.map((m) => m.modelName)
94
+ }
95
+ });
96
+ }
97
+ const predicates = [];
98
+ for (const rule of rules) {
99
+ const p = rule(env.context, env.operation);
100
+ if (p && Object.keys(p).length > 0) predicates.push(p);
101
+ }
102
+ if (predicates.length === 0) return void 0;
103
+ if (unscoped) throw createError(500, `[mongokit] join into '${from}': it is scoped by its repository and cannot be listed in unscopedJoins. Use bypassTenant for a deliberate cross-tenant read.`, {
104
+ code: JOIN_ERROR_CODES.UNSCOPE_REFUSED,
105
+ meta: { from }
106
+ });
107
+ return predicates.length === 1 ? predicates[0] : { $and: predicates };
108
+ }
109
+ function scopedPipeline(sub, scope, env) {
110
+ const walked = walk(Array.isArray(sub) ? sub : [], env);
111
+ return scope ? [{ $match: scope }, ...walked] : walked;
112
+ }
113
+ /**
114
+ * Return `pipeline` with every join scoped; throws on an unknown or ungoverned `from`. The result
115
+ * is the one documented narrowing of a caller's stage array to mongoose's stage union (stage
116
+ * validity is the server's call); `aggregatePipeline` no longer carries its own.
117
+ */
118
+ function scopeJoins(pipeline, env) {
119
+ return walk(pipeline, env);
120
+ }
121
+ function walk(pipeline, env) {
122
+ return pipeline.map((raw) => {
123
+ const stage = raw;
124
+ if (stage.$lookup) {
125
+ const l = stage.$lookup;
126
+ if (l.from === void 0) return { $lookup: {
127
+ ...l,
128
+ pipeline: scopedPipeline(l.pipeline, void 0, env)
129
+ } };
130
+ const scope = scopeForCollection(l.from, env);
131
+ if (!scope && l.pipeline === void 0) return stage;
132
+ return { $lookup: {
133
+ ...l,
134
+ pipeline: scopedPipeline(l.pipeline, scope, env)
135
+ } };
136
+ }
137
+ if (stage.$unionWith) {
138
+ const u = typeof stage.$unionWith === "string" ? { coll: stage.$unionWith } : stage.$unionWith;
139
+ if (u.coll === void 0) return { $unionWith: {
140
+ ...u,
141
+ pipeline: scopedPipeline(u.pipeline, void 0, env)
142
+ } };
143
+ const scope = scopeForCollection(u.coll, env);
144
+ if (!scope && u.pipeline === void 0) return stage;
145
+ return { $unionWith: {
146
+ ...u,
147
+ pipeline: scopedPipeline(u.pipeline, scope, env)
148
+ } };
149
+ }
150
+ if (stage.$graphLookup) {
151
+ const g = stage.$graphLookup;
152
+ const scope = scopeForCollection(g.from, env);
153
+ if (!scope) return stage;
154
+ const existing = g.restrictSearchWithMatch;
155
+ return { $graphLookup: {
156
+ ...g,
157
+ restrictSearchWithMatch: existing ? { $and: [existing, scope] } : scope
158
+ } };
159
+ }
160
+ if (stage.$facet) {
161
+ const branches = stage.$facet;
162
+ return { $facet: Object.fromEntries(Object.entries(branches).map(([k, v]) => [k, walk(v, env)])) };
163
+ }
164
+ return stage;
165
+ });
166
+ }
167
+ //#endregion
168
+ export { scopeJoins as a, scopeForCollection as i, declareCollectionScope as n, tenantFieldsOf as o, markGoverned as r, JOIN_ERROR_CODES as t };
@@ -46,14 +46,18 @@ function buildCrudSchemasFromMongooseSchema(mongooseSchema, options = {}) {
46
46
  const jsonCreate = buildJsonSchemaFromPaths(mongooseSchema, options);
47
47
  const jsonUpdate = buildJsonSchemaForUpdate(jsonCreate, options);
48
48
  const jsonResponse = buildJsonSchemaForResponse(mongooseSchema, options);
49
+ const idPath = mongooseSchema.path("_id");
49
50
  return {
50
51
  createBody: jsonCreate,
51
52
  updateBody: jsonUpdate,
52
53
  params: {
53
54
  type: "object",
54
- properties: { id: {
55
+ properties: { id: idPath === void 0 || isObjectIdInstance(idPath.instance ?? "") ? {
55
56
  type: "string",
56
57
  pattern: "^[0-9a-fA-F]{24}$"
58
+ } : {
59
+ type: "string",
60
+ minLength: 1
57
61
  } },
58
62
  required: ["id"]
59
63
  },
@@ -1,5 +1,5 @@
1
- import { g as ObjectId } from "./pagination-BpIv2bdS.mjs";
2
- import { l as Plugin, p as RepositoryContext } from "./repository-odl03582.mjs";
1
+ import { A as ObjectId } from "./pagination-D6psses0.mjs";
2
+ import { l as Plugin, p as RepositoryContext } from "./repository-DNHfjAZX.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 { _ as PopulateSpec, g as ObjectId, i as CountStrategy, r as CollationOptions, v as ReadPreferenceType, x as SortSpec, y as SelectSpec } from "./pagination-BpIv2bdS.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-D6psses0.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";
@@ -312,13 +312,20 @@ interface SessionOptions {
312
312
  * substitute for access control — gate it at the host layer.
313
313
  */
314
314
  bypassAppendOnly?: boolean;
315
+ /** Write concern for this call; beats `queryDefaults.writeConcern`. Ignored by reads. */
316
+ writeConcern?: WriteConcernSpec;
315
317
  /** Extensible — plugins can read custom fields from options */
316
318
  [key: string]: unknown;
317
319
  }
318
- /** Read options — session + readPreference for read-only operations */
320
+ /** Read options. Each field beats the repository and deployment `queryDefaults`. */
319
321
  interface ReadOptions extends SessionOptions {
320
322
  /** Read preference for replica sets (e.g. 'secondaryPreferred') */
321
323
  readPreference?: ReadPreferenceType;
324
+ /** Server-side time bound for this command. */
325
+ maxTimeMS?: number;
326
+ readConcern?: ReadConcernLevel;
327
+ /** Profiler / log tag. */
328
+ comment?: string;
322
329
  }
323
330
  /** Repository operation options */
324
331
  interface OperationOptions extends ReadOptions {
@@ -436,21 +443,17 @@ interface FindOneAndUpdateOptions extends OperationOptions {
436
443
  runValidators?: boolean;
437
444
  /** Collation for locale-aware string comparison. */
438
445
  collation?: CollationOptions;
439
- /** Maximum execution time in milliseconds. */
440
- maxTimeMS?: number;
446
+ /** Force an index (name or key pattern). */
447
+ hint?: string | Record<string, 1 | -1>;
441
448
  }
442
449
  /** Aggregate operation options */
443
450
  interface AggregateOptions extends ReadOptions {
444
451
  /** Allow aggregation to use disk for large sorts/groups */
445
452
  allowDiskUse?: boolean;
446
- /** Comment for profiler/logs */
447
- comment?: string;
448
- /** Maximum execution time in milliseconds */
449
- maxTimeMS?: number;
450
- /** Read concern level */
451
- readConcern?: {
452
- level: string;
453
- };
453
+ /** Force an index (name or key pattern). */
454
+ hint?: string | Record<string, 1 | -1>;
455
+ /** Collections the caller asserts are company-wide, joinable without a governing repository. */
456
+ unscopedJoins?: readonly string[];
454
457
  /** Collation for locale-aware string comparison */
455
458
  collation?: CollationOptions;
456
459
  /** Maximum allowed pipeline stages (governance) */
@@ -532,5 +535,25 @@ interface MinMaxResult {
532
535
  min: unknown;
533
536
  max: unknown;
534
537
  }
538
+ /** Kit-native options for the portable `aggregate(req)`; each field beats every default. */
539
+ interface AggregateCallOptions {
540
+ /** Also return the winning plan (a second command). */
541
+ explain?: 'queryPlanner' | 'executionStats' | 'allPlansExecution';
542
+ /** Collections the caller asserts are company-wide (see `join-scope.ts`). */
543
+ unscopedJoins?: readonly string[];
544
+ maxTimeMS?: number;
545
+ readPreference?: ReadPreferenceType;
546
+ readConcern?: ReadConcernLevel;
547
+ comment?: string;
548
+ }
549
+ /** `getByIds` options: chunked `$in` reads, optionally aligned with the input order. */
550
+ interface GetByIdsOptions extends OperationOptions {
551
+ /** Ids per `$in` command. Default `DEFAULT_ID_CHUNK` (10,000). */
552
+ chunkSize?: number;
553
+ /** Chunks in flight at once outside a session. Default 4. */
554
+ concurrency?: number;
555
+ /** Return `Array<TDoc | undefined>` aligned with the input ids instead of a Map. */
556
+ preserveOrder?: boolean;
557
+ }
535
558
  //#endregion
536
- export { UpdateOptions as _, CreateOptions as a, LookupOptions as b, FindOneAndUpdateOptions as c, LookupPopulateResult as d, MinMaxResult as f, UpdateManyResult as g, SessionOptions as h, ConvenientTransactionOptions as i, GroupResult as l, ReadOptions as m, CacheOperationOptions as n, DeleteManyResult as o, OperationOptions as p, CacheableOptions as r, DeleteResult as s, AggregateOptions as t, LookupPopulateOptions as u, WithTransactionOptions as v, LookupBuilder as y };
559
+ export { LookupOptions as S, SessionOptions as _, ConvenientTransactionOptions as a, WithTransactionOptions as b, DeleteResult as c, GroupResult as d, LookupPopulateOptions as f, ReadOptions as g, OperationOptions as h, CacheableOptions as i, FindOneAndUpdateOptions as l, MinMaxResult as m, AggregateOptions as n, CreateOptions as o, LookupPopulateResult as p, CacheOperationOptions as r, DeleteManyResult as s, AggregateCallOptions as t, GetByIdsOptions as u, UpdateManyResult as v, LookupBuilder as x, UpdateOptions as y };
@@ -1,4 +1,4 @@
1
- import { r as Repository } from "../Repository-CR8Y9g2E.mjs";
1
+ import { r as Repository } from "../Repository-pnLw1rG5.mjs";
2
2
  import { Connection, Model } from "mongoose";
3
3
  //#region src/outbox/index.d.ts
4
4
  /** Delivery states. `dead_letter` is terminal and operator-visible. */
@@ -1,4 +1,4 @@
1
- import { n as Repository } from "../Repository-BMgPSkqZ.mjs";
1
+ import { n as Repository } from "../Repository-CYhSC9ut.mjs";
2
2
  import { n as batchOperationsPlugin, t as methodRegistryPlugin } from "../method-registry.plugin-C63pGn1d.mjs";
3
3
  import { Schema } from "mongoose";
4
4
  //#region src/outbox/index.ts
@@ -1,2 +1,2 @@
1
- import { t as PaginationEngine } from "../PaginationEngine-D-P19Y9m.mjs";
2
- export { PaginationEngine };
1
+ import { n as PaginationEngine, t as CountMemoKey } from "../PaginationEngine-CRHyuUtw.mjs";
2
+ export { CountMemoKey, PaginationEngine };
@@ -1,2 +1,2 @@
1
- import { t as PaginationEngine } from "../PaginationEngine-Dvl8jcRY.mjs";
1
+ import { t as PaginationEngine } from "../PaginationEngine-CfT2Cxkj.mjs";
2
2
  export { PaginationEngine };
@@ -1,4 +1,4 @@
1
- import { u as PaginationConfig } from "../../pagination-BpIv2bdS.mjs";
1
+ import { u as PaginationConfig } from "../../pagination-D6psses0.mjs";
2
2
  //#region src/pagination/utils/limits.d.ts
3
3
  /**
4
4
  * Validates and sanitizes limit value
@@ -1,4 +1,4 @@
1
- import { Document, Model, PipelineStage, PopulateOptions, Types } from "mongoose";
1
+ import { Connection, Document, Model, PipelineStage, PopulateOptions, Types } from "mongoose";
2
2
  //#region src/types/core.d.ts
3
3
  /**
4
4
  * Core query/document primitives shared across every mongokit module —
@@ -47,6 +47,79 @@ type SelectSpec = string | readonly string[] | Record<string, 0 | 1>;
47
47
  /** Filter query type for MongoDB queries (compatible with Mongoose 8 & 9) */
48
48
  type FilterQuery<_T = unknown> = Record<string, unknown>;
49
49
  //#endregion
50
+ //#region src/repository/query-defaults.d.ts
51
+ type ReadConcernLevel = 'local' | 'available' | 'majority' | 'linearizable' | 'snapshot';
52
+ interface WriteConcernSpec {
53
+ w?: number | 'majority';
54
+ j?: boolean;
55
+ wtimeoutMS?: number;
56
+ }
57
+ /** Defaults for every repository command. */
58
+ interface QueryDefaults {
59
+ maxTimeMS?: number;
60
+ readPreference?: ReadPreferenceType;
61
+ readConcern?: ReadConcernLevel;
62
+ writeConcern?: WriteConcernSpec;
63
+ /** Profiler / log tag. */
64
+ comment?: string;
65
+ }
66
+ /** Defaults for aggregations only; consulted before {@link QueryDefaults}. */
67
+ interface AggregateDefaults {
68
+ maxTimeMs?: number;
69
+ allowDiskUse?: boolean;
70
+ }
71
+ /** What one call may pass. Every field beats every default. */
72
+ interface PerCallQueryOptions {
73
+ maxTimeMS?: number;
74
+ readPreference?: ReadPreferenceType;
75
+ readConcern?: ReadConcernLevel;
76
+ writeConcern?: WriteConcernSpec;
77
+ comment?: string;
78
+ hint?: string | Record<string, 1 | -1>;
79
+ allowDiskUse?: boolean;
80
+ session?: unknown;
81
+ }
82
+ /** The options a command is executed with, after resolution. */
83
+ interface ResolvedQueryOptions {
84
+ maxTimeMS?: number;
85
+ readPreference?: ReadPreferenceType;
86
+ readConcern?: ReadConcernLevel;
87
+ writeConcern?: WriteConcernSpec;
88
+ comment?: string;
89
+ hint?: string | Record<string, 1 | -1>;
90
+ allowDiskUse?: boolean;
91
+ }
92
+ type CommandKind = 'read' | 'aggregate' | 'write' | 'findAndModify';
93
+ /** Set the deployment's query defaults. Merges; an explicit `undefined` changes nothing. */
94
+ declare function configureQueryDefaults(defaults: QueryDefaults): void;
95
+ /** Set the deployment's aggregation defaults. Merges; an explicit `undefined` changes nothing. */
96
+ declare function configureAggregateDefaults(defaults: AggregateDefaults): void;
97
+ /** A copy of the deployment defaults. */
98
+ declare function getQueryDefaults(): {
99
+ query: QueryDefaults;
100
+ aggregate: AggregateDefaults;
101
+ };
102
+ /** Back to no deployment defaults. For tests. */
103
+ declare function resetQueryDefaults(): void;
104
+ /**
105
+ * Boot check (M7): throws unless the deployment sets a query `maxTimeMS`, and — when a connection
106
+ * is passed — unless its client carries a CSOT `timeoutMS`. Returns what it verified.
107
+ */
108
+ declare function assertQueryDefaultsConfigured(connection?: Connection): {
109
+ maxTimeMS: number;
110
+ timeoutMS?: number;
111
+ };
112
+ declare const QUERY_DEFAULTS_ERROR_CODES: {
113
+ readonly NOT_CONFIGURED: "mongokit.query_defaults.not_configured";
114
+ /** A per-call bound inside a transaction that carries its own CSOT budget (the driver forbids both). */
115
+ readonly TIMEOUT_IN_TRANSACTION: "mongokit.query_defaults.timeout_in_transaction";
116
+ };
117
+ /** Resolve the options one command runs with. `repo` is the repository's own defaults. */
118
+ declare function resolveQueryOptions(kind: CommandKind, perCall: PerCallQueryOptions, repo?: {
119
+ query?: QueryDefaults;
120
+ aggregate?: AggregateDefaults;
121
+ }): ResolvedQueryOptions;
122
+ //#endregion
50
123
  //#region src/pagination/utils/cursor-signing.d.ts
51
124
  /**
52
125
  * HMAC signing for pagination cursors.
@@ -108,7 +181,11 @@ type CursorSecret = string | readonly string[];
108
181
  * Declared ONCE and referenced everywhere — this union was hand-copied to eight
109
182
  * declarations, which is how the aggregate variant silently lost a member.
110
183
  */
111
- type CountStrategy = 'exact' | 'estimated' | 'capped' | 'none';
184
+ /**
185
+ * `cached`: the capped count memoised per (collection, filter, collation, hint) for
186
+ * `countCacheTtlMs`; never inside a transaction (it would publish uncommitted rows).
187
+ */
188
+ type CountStrategy = 'exact' | 'estimated' | 'capped' | 'none' | 'cached';
112
189
  /**
113
190
  * What mongokit adds to repo-core's offset envelope, via its reserved `TExtra`
114
191
  * slot: `OffsetPaginationResult<TDoc, MongokitPageExtras>`.
@@ -123,16 +200,13 @@ type MongokitPageExtras = {
123
200
  /** Deep-page or clamped-limit advice. Human-readable; never branch on it. */
124
201
  warning?: string;
125
202
  /**
126
- * `total` is a FLOOR, not a count — the scan stopped at `countLimit`. Render
127
- * it as `10,000+`, and never compute a percentage or a last-page number from
128
- * a total carrying this flag.
129
- *
130
- * A separate boolean rather than a sentinel `total` (`-1`, `null`): a total
131
- * that lies in a field typed `number` is read as a number by every consumer
132
- * that has not been taught otherwise, which is the failure this flag exists
133
- * to make impossible to miss.
203
+ * `total` is not an exact count of the rows matching right now: a capped count at its
204
+ * ceiling (a floor — render `10,000+`), an estimate, a memoised (`cached`) count, or no count
205
+ * (`none`). Never derive a last-page number or a percentage from a total carrying it.
134
206
  */
135
- totalIsLowerBound?: boolean;
207
+ totalIsEstimate: boolean;
208
+ /** When `total` was counted; `null` when nothing was counted (`none`). */
209
+ countedAt: Date | null;
136
210
  };
137
211
  /** Pagination configuration */
138
212
  interface PaginationConfig {
@@ -142,6 +216,13 @@ interface PaginationConfig {
142
216
  maxLimit?: number;
143
217
  /** Maximum allowed page number (default: 10000) */
144
218
  maxPage?: number;
219
+ /**
220
+ * Deepest `skip` an offset page may ask for (default 100,000). Past it the call is refused
221
+ * with `mongokit.pagination.offset_too_deep` (400): walk with keyset (`after`) instead.
222
+ */
223
+ maxOffset?: number;
224
+ /** Freshness window of `countStrategy: 'cached'` (default 30,000 ms). */
225
+ countCacheTtlMs?: number;
145
226
  /** Page number that triggers performance warning (default: 100) */
146
227
  deepPageThreshold?: number;
147
228
  /** Cursor version for forward compatibility (default: 1) */
@@ -249,6 +330,13 @@ interface BasePaginationOptions {
249
330
  maxTimeMS?: number;
250
331
  /** Read preference for replica sets (e.g. 'secondaryPreferred') */
251
332
  readPreference?: ReadPreferenceType;
333
+ readConcern?: ReadConcernLevel;
334
+ comment?: string;
335
+ /**
336
+ * Already-resolved time bound and concerns (the Repository passes these, defaults folded in).
337
+ * When absent the engine resolves the per-call fields above against the deployment defaults.
338
+ */
339
+ queryOptions?: ResolvedQueryOptions;
252
340
  /** Collation for locale-aware string comparison and case-insensitive sorting */
253
341
  collation?: CollationOptions;
254
342
  }
@@ -325,9 +413,20 @@ interface AggregatePaginationOptions {
325
413
  mode?: 'offset';
326
414
  /** Read preference for replica sets (e.g. 'secondaryPreferred') */
327
415
  readPreference?: ReadPreferenceType;
416
+ readConcern?: ReadConcernLevel;
417
+ comment?: string;
418
+ /** Collections the caller asserts are company-wide (see `join-scope.ts`). */
419
+ unscopedJoins?: readonly string[];
420
+ /**
421
+ * The caller states the pipeline's last `$sort` is a TOTAL order (unique per output row), so the
422
+ * not-provably-total warning is silenced. Only for a sort total by domain knowledge.
423
+ */
424
+ sortIsTotal?: boolean;
425
+ /** Already-resolved options; see {@link BasePaginationOptions.queryOptions}. */
426
+ queryOptions?: ResolvedQueryOptions;
328
427
  }
329
428
  /** Value type identifier for cursor serialization */
330
- type ValueType = 'date' | 'objectid' | 'boolean' | 'number' | 'string' | 'null' | 'unknown';
429
+ type ValueType = 'date' | 'objectid' | 'decimal' | 'boolean' | 'number' | 'string' | 'null' | 'unknown';
331
430
  /** Cursor payload */
332
431
  interface CursorPayload {
333
432
  /** Primary sort field value (legacy single-field) */
@@ -346,6 +445,8 @@ interface CursorPayload {
346
445
  vals?: Record<string, string | number | boolean | null>;
347
446
  /** Compound sort value types */
348
447
  types?: Record<string, ValueType>;
448
+ /** Scope fingerprint: collection + post-policy filter + collation (see `utils/guards.ts`). */
449
+ fp: string;
349
450
  }
350
451
  /** Decoded cursor */
351
452
  interface DecodedCursor {
@@ -359,6 +460,8 @@ interface DecodedCursor {
359
460
  version: number;
360
461
  /** All sort field values (rehydrated) — for compound sort */
361
462
  values?: Record<string, unknown>;
463
+ /** Scope fingerprint the cursor was minted under. */
464
+ scope: string;
362
465
  }
363
466
  //#endregion
364
- export { PopulateSpec as _, CursorPayload as a, SortDirection as b, MongokitPageExtras as c, ValueType as d, CursorSecret as f, ObjectId as g, FilterQuery as h, CountStrategy as i, OffsetPaginationOptions as l, AnyModel as m, BasePaginationOptions as n, DecodedCursor as o, AnyDocument as p, CollationOptions as r, KeysetPaginationOptions as s, AggregatePaginationOptions as t, PaginationConfig as u, ReadPreferenceType as v, SortSpec as x, SelectSpec as y };
467
+ export { ObjectId as A, configureQueryDefaults as C, AnyDocument as D, resolveQueryOptions as E, SortSpec as F, ReadPreferenceType as M, SelectSpec as N, AnyModel as O, SortDirection as P, configureAggregateDefaults as S, resetQueryDefaults as T, QueryDefaults as _, CursorPayload as a, WriteConcernSpec as b, MongokitPageExtras as c, ValueType as d, CursorSecret as f, QUERY_DEFAULTS_ERROR_CODES as g, PerCallQueryOptions as h, CountStrategy as i, PopulateSpec as j, FilterQuery as k, OffsetPaginationOptions as l, CommandKind as m, BasePaginationOptions as n, DecodedCursor as o, AggregateDefaults as p, CollationOptions as r, KeysetPaginationOptions as s, AggregatePaginationOptions as t, PaginationConfig as u, ReadConcernLevel as v, getQueryDefaults as w, assertQueryDefaultsConfigured as x, ResolvedQueryOptions as y };
@@ -1,5 +1,5 @@
1
- import { _ as PopulateSpec, g as ObjectId, x as SortSpec, y as SelectSpec } from "./pagination-BpIv2bdS.mjs";
2
- import { h as RepositoryInstance, p as RepositoryContext } from "./repository-odl03582.mjs";
1
+ import { A as ObjectId, F as SortSpec, N as SelectSpec, j as PopulateSpec } from "./pagination-D6psses0.mjs";
2
+ import { h as RepositoryInstance, p as RepositoryContext } from "./repository-DNHfjAZX.mjs";
3
3
  import { OffsetPaginationResult } from "@classytic/repo-core/pagination";
4
4
  //#region src/types/plugin-options.d.ts
5
5
  /** Field preset configuration (field-filter plugin) */