@ignex/ninox 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -16,8 +16,11 @@ var withTimeout = (promise, timeoutMs, label) => {
16
16
  if (timer !== void 0) clearTimeout(timer);
17
17
  });
18
18
  };
19
- var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
20
- var sleepJittered = (ms, jitterMs = 1e3) => sleep(ms + Math.floor(Math.random() * Math.min(ms, jitterMs)));
19
+ var sleep = (ms, unref = false) => new Promise((resolve) => {
20
+ const timer = setTimeout(resolve, ms);
21
+ if (unref) timer.unref?.();
22
+ });
23
+ var sleepJittered = (ms, jitterMs = 1e3, unref = false) => sleep(ms + Math.floor(Math.random() * Math.min(ms, jitterMs)), unref);
21
24
 
22
25
  // src/capabilities.ts
23
26
  var createMongoCapabilitiesStore = () => {
@@ -82,7 +85,7 @@ var AppError = class extends Error {
82
85
  this.name = "AppError";
83
86
  this.code = code;
84
87
  this.statusCode = statusCode;
85
- this.extra = extra;
88
+ if (extra !== void 0) this.extra = extra;
86
89
  }
87
90
  /**
88
91
  * Stable, client-safe serialization for framework error middleware: a
@@ -466,6 +469,18 @@ var RefreshTicker = class {
466
469
  // src/cache/hot-cache/types.ts
467
470
  var WATCH_SEP = "\0";
468
471
 
472
+ // src/utils/watch-errors.ts
473
+ var isPermanentWatchError = (message) => (
474
+ // Standalone server: `$changeStream` is not supported at all.
475
+ /only supported on replica sets/i.test(message) || // Credentials/authorization problems: the user cannot run `aggregate` on
476
+ // this collection/server. Wrong creds, missing roles, or a user created
477
+ // without the `read`/`changeStream` privilege will never start working by
478
+ // retrying — fail once and stop instead of retrying forever.
479
+ /requires authentication|not authorized|unauthorized|authentication failed|Command .* requires authentication/i.test(
480
+ message
481
+ )
482
+ );
483
+
469
484
  // src/cache/hot-cache/watcher.ts
470
485
  var WatchCoordinator = class {
471
486
  constructor(host) {
@@ -513,7 +528,10 @@ var WatchCoordinator = class {
513
528
  * Long-lived change-stream consumer for one collection. Each data change
514
529
  * invalidates the queries bound to it. On permanent "not supported" errors the
515
530
  * watcher is dropped and, if every planned stream failed, the cache falls back
516
- * to the standalone ticker. Transient errors retry with backoff.
531
+ * to the standalone ticker. Transient errors (including resume-token expiry,
532
+ * rollbacks, and `invalidate` events) retry with backoff and invalidate the
533
+ * collection on reopen so entries missed during the outage are re-fetched —
534
+ * see the module doc for the full failure-semantics contract.
517
535
  */
518
536
  async watchLoop(key, ref) {
519
537
  let backoff = 0;
@@ -548,14 +566,14 @@ var WatchCoordinator = class {
548
566
  } catch {
549
567
  }
550
568
  }
551
- if (/only supported on replica sets|not supported|ChangeStream/i.test(message)) {
569
+ if (isPermanentWatchError(message)) {
552
570
  this.markStreamFailed(key);
553
571
  return;
554
572
  }
555
573
  }
556
574
  if (this.stopped) return;
557
575
  backoff = Math.min(backoff + 1e3, 5e3);
558
- await sleepJittered(backoff);
576
+ await sleepJittered(backoff, 1e3, true);
559
577
  }
560
578
  }
561
579
  markStreamFailed(key) {
@@ -657,7 +675,7 @@ var HotCache = class {
657
675
  ttlMs: config.ttlMs ?? this.options.defaultTtlMs ?? 0,
658
676
  refreshIntervalMs: config.refreshIntervalMs ?? 0,
659
677
  clone: config.clone ?? false,
660
- maxValueBytes: config.maxValueBytes,
678
+ ...config.maxValueBytes !== void 0 ? { maxValueBytes: config.maxValueBytes } : {},
661
679
  gen: 0,
662
680
  hits: 0,
663
681
  misses: 0,
@@ -880,6 +898,8 @@ var cacheCollectionKey = (dbName, physical) => `${dbName}${NS_SEP}${physical}`;
880
898
  var QueryCache = class {
881
899
  lru;
882
900
  index = /* @__PURE__ */ new Map();
901
+ /** Per-collection version — bumped on every invalidation/clear of that collection. */
902
+ colVersions = /* @__PURE__ */ new Map();
883
903
  ttlMs;
884
904
  clone;
885
905
  hits = 0;
@@ -914,6 +934,14 @@ var QueryCache = class {
914
934
  key(collection, filterHash) {
915
935
  return `${collection}${SEP}${filterHash}`;
916
936
  }
937
+ /**
938
+ * Current version of a collection key (db-namespaced, e.g. from
939
+ * `cacheCollectionKey`). Reads capture it BEFORE fetching and pass it to
940
+ * `set()` so a concurrent invalidation can never be hidden by a late write.
941
+ */
942
+ versionOf(collection) {
943
+ return this.colVersions.get(collection) ?? 0;
944
+ }
917
945
  get(key) {
918
946
  const entry = this.lru.get(key);
919
947
  if (!entry) {
@@ -925,22 +953,38 @@ var QueryCache = class {
925
953
  this.misses++;
926
954
  return void 0;
927
955
  }
956
+ if (entry.versions) {
957
+ for (const [col, ver] of Object.entries(entry.versions)) {
958
+ if (this.versionOf(col) !== ver) {
959
+ this.lru.delete(key);
960
+ this.misses++;
961
+ return void 0;
962
+ }
963
+ }
964
+ }
928
965
  this.hits++;
929
966
  return this.clone ? cloneDeep(entry.value) : entry.value;
930
967
  }
931
- set(key, value, ttlMs) {
968
+ set(key, value, ttlMs, collections, versions) {
932
969
  this.sets++;
933
970
  const ttl = ttlMs ?? this.ttlMs;
934
971
  const expiresAt = ttl > 0 ? Date.now() + ttl : Infinity;
935
- this.lru.set(key, { value, expiresAt });
972
+ this.lru.set(key, {
973
+ value,
974
+ expiresAt,
975
+ ...versions !== void 0 ? { versions } : {}
976
+ });
936
977
  const sep = key.indexOf(SEP);
937
- const collection = sep === -1 ? key : key.slice(0, sep);
938
- let keys = this.index.get(collection);
939
- if (!keys) {
940
- keys = /* @__PURE__ */ new Set();
941
- this.index.set(collection, keys);
978
+ const primary = sep === -1 ? key : key.slice(0, sep);
979
+ const collectionKeys = /* @__PURE__ */ new Set([primary, ...collections ?? []]);
980
+ for (const collection of collectionKeys) {
981
+ let keys = this.index.get(collection);
982
+ if (!keys) {
983
+ keys = /* @__PURE__ */ new Set();
984
+ this.index.set(collection, keys);
985
+ }
986
+ keys.add(key);
942
987
  }
943
- keys.add(key);
944
988
  }
945
989
  delete(key) {
946
990
  this.deletes++;
@@ -948,6 +992,7 @@ var QueryCache = class {
948
992
  }
949
993
  /** Drop every cached entry belonging to a physical collection. */
950
994
  invalidateByCollection(collection) {
995
+ this.colVersions.set(collection, (this.colVersions.get(collection) ?? 0) + 1);
951
996
  const keys = this.index.get(collection);
952
997
  if (!keys) return;
953
998
  this.invalidateEvents++;
@@ -956,6 +1001,9 @@ var QueryCache = class {
956
1001
  }
957
1002
  clear() {
958
1003
  this.clearEvents++;
1004
+ for (const collection of this.index.keys()) {
1005
+ this.colVersions.set(collection, (this.colVersions.get(collection) ?? 0) + 1);
1006
+ }
959
1007
  this.lru.clear();
960
1008
  this.index.clear();
961
1009
  }
@@ -963,7 +1011,9 @@ var QueryCache = class {
963
1011
 
964
1012
  // src/graceful-transaction.ts
965
1013
  var withGracefulMongoTransaction = async (runner, fn, options = {}) => {
966
- const enabled = options.capabilities === void 0 || mongoTransactionsEnabled(options.capabilities, { urlHint: options.urlHint });
1014
+ const enabled = options.capabilities === void 0 || mongoTransactionsEnabled(options.capabilities, {
1015
+ ...options.urlHint !== void 0 ? { urlHint: options.urlHint } : {}
1016
+ });
967
1017
  if (!enabled) return fn(null);
968
1018
  try {
969
1019
  return await runner.transaction(fn);
@@ -2227,13 +2277,13 @@ var CacheInvalidator = class {
2227
2277
  } catch {
2228
2278
  }
2229
2279
  }
2230
- if (/only supported on replica sets/i.test(message)) {
2280
+ if (isPermanentWatchError(message)) {
2231
2281
  this.disabled = true;
2232
2282
  if (!this.warnedUnsupported) {
2233
2283
  this.warnedUnsupported = true;
2234
2284
  this.logger.warn?.(
2235
2285
  { collection: ref.collection, error: message },
2236
- "cacheWatch: change streams are not supported here (standalone?) \u2014 the query cache stays write-through only; set cache: { ttlMs } or disable cacheWatch"
2286
+ "cacheWatch: change streams unavailable permanently \u2014 the query cache stays write-through only; set cache: { ttlMs } or disable cacheWatch"
2237
2287
  );
2238
2288
  }
2239
2289
  return;
@@ -2244,7 +2294,7 @@ var CacheInvalidator = class {
2244
2294
  "cacheWatch: change stream error, retrying with backoff"
2245
2295
  );
2246
2296
  backoff = Math.min(backoff + 1e3, 5e3);
2247
- await sleepJittered(backoff);
2297
+ await sleepJittered(backoff, 1e3, true);
2248
2298
  }
2249
2299
  }
2250
2300
  }
@@ -2393,15 +2443,15 @@ var createHealth = (db) => async () => {
2393
2443
 
2394
2444
  // src/relation/populate.ts
2395
2445
  var createForeignKeyLoader = (deps2, collection, foreignField, options = {}) => new DataLoader({
2396
- maxBatchSize: options.maxBatchSize,
2397
- cache: options.cache,
2446
+ ...options.maxBatchSize !== void 0 ? { maxBatchSize: options.maxBatchSize } : {},
2447
+ ...options.cache !== void 0 ? { cache: options.cache } : {},
2398
2448
  keyOf: canonicalKey,
2399
2449
  batch: async (keys) => {
2400
2450
  const docs = await deps2.findMany(
2401
2451
  collection,
2402
2452
  { [foreignField]: { $in: keys } },
2403
2453
  {
2404
- limit: options.maxDocs
2454
+ ...options.maxDocs !== void 0 ? { limit: options.maxDocs } : {}
2405
2455
  }
2406
2456
  );
2407
2457
  const index = /* @__PURE__ */ new Map();
@@ -2506,11 +2556,23 @@ var asCollectionDefinition = (value) => {
2506
2556
  };
2507
2557
 
2508
2558
  // src/service/trace-db-op.ts
2559
+ var debugQueryCache;
2560
+ var probeDebugQuery = async () => {
2561
+ if (debugQueryCache !== void 0) return;
2562
+ try {
2563
+ const mod = await import('@ignex/core/debug');
2564
+ debugQueryCache = typeof mod.debugQuery === "function" ? mod.debugQuery : null;
2565
+ } catch {
2566
+ debugQueryCache = null;
2567
+ }
2568
+ };
2509
2569
  var traceDbOp = async (logger, meta, fn, options = {}) => {
2510
2570
  const start = performance.now();
2511
2571
  logger.debug({ ...meta, operation: "start" }, "start");
2512
2572
  try {
2513
- const result = await fn();
2573
+ if (debugQueryCache === void 0) await probeDebugQuery();
2574
+ const debugQuery = debugQueryCache;
2575
+ const result = debugQuery ? await debugQuery(`${meta.collection}.${meta.op}`, void 0, () => fn()) : await fn();
2514
2576
  const durationMs = performance.now() - start;
2515
2577
  logger.info({ ...meta, durationMs }, "ok");
2516
2578
  return result;
@@ -2555,10 +2617,16 @@ var resolveQueryOptions = (options) => {
2555
2617
  return {
2556
2618
  maxAttempts,
2557
2619
  retryDelayMs,
2558
- dedupe,
2559
- cache,
2560
- retryWrites,
2561
- sdk: { session, maxTimeMS, hint, batchSize, drift },
2620
+ ...dedupe !== void 0 ? { dedupe } : {},
2621
+ ...cache !== void 0 ? { cache } : {},
2622
+ ...retryWrites !== void 0 ? { retryWrites } : {},
2623
+ sdk: {
2624
+ ...session !== void 0 ? { session } : {},
2625
+ ...maxTimeMS !== void 0 ? { maxTimeMS } : {},
2626
+ ...hint !== void 0 ? { hint } : {},
2627
+ ...batchSize !== void 0 ? { batchSize } : {},
2628
+ ...drift !== void 0 ? { drift } : {}
2629
+ },
2562
2630
  driverOpts: rest
2563
2631
  };
2564
2632
  };
@@ -2664,28 +2732,48 @@ var PipelineBuilder = class _PipelineBuilder {
2664
2732
  /* ------------------------------ terminals ---------------------------- */
2665
2733
  /** Run the pipeline and return every output document. */
2666
2734
  toArray() {
2735
+ const run = (r) => this.deps.coll().aggregate(this.stages, this.deps.mergeDriver(r.driverOpts, r.sdk)).toArray();
2736
+ if (this.deps.cachedAggregate) {
2737
+ return this.deps.cachedAggregate({
2738
+ collection: this.deps.logical,
2739
+ opName: "mongo.pipeline",
2740
+ pipeline: this.stages,
2741
+ ...this.deps.options !== void 0 ? { options: this.deps.options } : {},
2742
+ execute: run
2743
+ });
2744
+ }
2667
2745
  return defineCrudOp(
2668
2746
  this.deps.crudDeps,
2669
2747
  this.deps.logical,
2670
2748
  "mongo.pipeline",
2671
- (r) => this.deps.coll().aggregate(this.stages, this.deps.mergeDriver(r.driverOpts, r.sdk)).toArray(),
2749
+ run,
2672
2750
  this.deps.options
2673
2751
  );
2674
2752
  }
2675
2753
  /** Run the pipeline and return the first output document (or null). */
2676
2754
  async first() {
2755
+ const run = async (r) => {
2756
+ const cursor = this.deps.coll().aggregate(this.stages, this.deps.mergeDriver(r.driverOpts, r.sdk));
2757
+ try {
2758
+ return await cursor.tryNext();
2759
+ } finally {
2760
+ await cursor.close();
2761
+ }
2762
+ };
2763
+ if (this.deps.cachedAggregate) {
2764
+ return this.deps.cachedAggregate({
2765
+ collection: this.deps.logical,
2766
+ opName: "mongo.pipeline.first",
2767
+ pipeline: this.stages,
2768
+ ...this.deps.options !== void 0 ? { options: this.deps.options } : {},
2769
+ execute: run
2770
+ });
2771
+ }
2677
2772
  return defineCrudOp(
2678
2773
  this.deps.crudDeps,
2679
2774
  this.deps.logical,
2680
- "mongo.pipeline",
2681
- async (r) => {
2682
- const cursor = this.deps.coll().aggregate(this.stages, this.deps.mergeDriver(r.driverOpts, r.sdk));
2683
- try {
2684
- return await cursor.tryNext();
2685
- } finally {
2686
- await cursor.close();
2687
- }
2688
- },
2775
+ "mongo.pipeline.first",
2776
+ run,
2689
2777
  this.deps.options
2690
2778
  );
2691
2779
  }
@@ -2797,7 +2885,7 @@ var buildAggregationStages = () => {
2797
2885
 
2798
2886
  // src/service/aggregation/aggregate.ts
2799
2887
  var makeAggregateOps = (ctx) => {
2800
- const { deps: deps2, coll, mergeAggOptions: mergeAggOptions2 } = ctx;
2888
+ const { deps: deps2, coll, mergeAggOptions: mergeAggOptions2, cachedAggregate } = ctx;
2801
2889
  const aggregate = async (collection, pipelineBuilder, options) => {
2802
2890
  const stages = buildAggregationStages();
2803
2891
  const pipeline2 = pipelineBuilder(stages);
@@ -2814,7 +2902,8 @@ var makeAggregateOps = (ctx) => {
2814
2902
  coll: () => coll(collection),
2815
2903
  crudDeps: deps2,
2816
2904
  mergeDriver: mergeAggOptions2,
2817
- options
2905
+ ...options !== void 0 ? { options } : {},
2906
+ cachedAggregate
2818
2907
  });
2819
2908
  return { aggregate, pipeline };
2820
2909
  };
@@ -2822,10 +2911,10 @@ var makeAggregateOps = (ctx) => {
2822
2911
  // src/service/aggregation/helpers.ts
2823
2912
  var mergeAggOptions = (driverOpts, sdk) => ({
2824
2913
  ...driverOpts,
2825
- session: sdk.session,
2826
- maxTimeMS: sdk.maxTimeMS,
2827
- hint: sdk.hint,
2828
- batchSize: sdk.batchSize
2914
+ ...sdk.session !== void 0 ? { session: sdk.session } : {},
2915
+ ...sdk.maxTimeMS !== void 0 ? { maxTimeMS: sdk.maxTimeMS } : {},
2916
+ ...sdk.hint !== void 0 ? { hint: sdk.hint } : {},
2917
+ ...sdk.batchSize !== void 0 ? { batchSize: sdk.batchSize } : {}
2829
2918
  });
2830
2919
  var DATE_PART_FORMATS = {
2831
2920
  hour: "%Y-%m-%dT%H",
@@ -2834,10 +2923,103 @@ var DATE_PART_FORMATS = {
2834
2923
  month: "%Y-%m",
2835
2924
  year: "%Y"
2836
2925
  };
2926
+ var collectAggSources = (primaryLogical, pipeline, extraLogical = []) => {
2927
+ const sources = /* @__PURE__ */ new Set([primaryLogical, ...extraLogical]);
2928
+ const visit = (stages) => {
2929
+ if (!Array.isArray(stages)) return;
2930
+ for (const stage of stages) {
2931
+ if (!stage || typeof stage !== "object") continue;
2932
+ const s2 = stage;
2933
+ const lookup = s2.$lookup;
2934
+ if (lookup && typeof lookup === "object") {
2935
+ if (typeof lookup.from === "string") sources.add(lookup.from);
2936
+ if (Array.isArray(lookup.pipeline)) visit(lookup.pipeline);
2937
+ }
2938
+ const union = s2.$unionWith;
2939
+ if (union && typeof union === "object") {
2940
+ if (typeof union.coll === "string") sources.add(union.coll);
2941
+ if (Array.isArray(union.pipeline)) visit(union.pipeline);
2942
+ }
2943
+ const facet = s2.$facet;
2944
+ if (facet && typeof facet === "object") {
2945
+ for (const branch of Object.values(facet)) visit(branch);
2946
+ }
2947
+ }
2948
+ };
2949
+ visit(pipeline);
2950
+ return [...sources];
2951
+ };
2952
+ var isCacheablePipeline = (pipeline) => {
2953
+ for (const stage of pipeline) {
2954
+ if (!stage || typeof stage !== "object") continue;
2955
+ if ("$out" in stage || "$merge" in stage || "$sample" in stage) return false;
2956
+ const s2 = stage;
2957
+ const lookup = s2.$lookup;
2958
+ if (lookup && Array.isArray(lookup.pipeline) && !isCacheablePipeline(lookup.pipeline)) {
2959
+ return false;
2960
+ }
2961
+ const facet = s2.$facet;
2962
+ if (facet && typeof facet === "object") {
2963
+ for (const branch of Object.values(facet)) {
2964
+ if (Array.isArray(branch) && !isCacheablePipeline(branch)) return false;
2965
+ }
2966
+ }
2967
+ }
2968
+ return true;
2969
+ };
2970
+
2971
+ // src/service/aggregation/cached-read.ts
2972
+ var createCachedAggregate = (ctx) => {
2973
+ const { client, deps: deps2, opts, resolve } = ctx;
2974
+ const cache = opts.cache;
2975
+ return async (input) => {
2976
+ const { collection: collectionName, opName, pipeline, options, sources = [], execute } = input;
2977
+ const collection = collectionName;
2978
+ const physical = resolve(String(collection));
2979
+ const resolved = resolveQueryOptions(options);
2980
+ const noSession = !resolved.sdk.session;
2981
+ const useCache = cache !== void 0 && noSession && resolved.cache !== false && isCacheablePipeline(pipeline);
2982
+ const shouldDedupe = resolved.dedupe !== false && (resolved.dedupe === true || opts.dedupeReads === true && noSession) && opts.inFlight !== void 0;
2983
+ const runDriver = () => defineCrudOp(deps2, collection, opName, execute, options);
2984
+ const key = useCache ? cache.key(
2985
+ cacheCollectionKey(client.databaseName, physical),
2986
+ stableHash([opName, pipeline, options])
2987
+ ) : void 0;
2988
+ const toSourceKeys = () => collectAggSources(String(collection), pipeline, sources).map((name) => {
2989
+ try {
2990
+ return cacheCollectionKey(client.databaseName, resolve(name));
2991
+ } catch {
2992
+ return cacheCollectionKey(client.databaseName, name);
2993
+ }
2994
+ });
2995
+ const runOnce = () => {
2996
+ if (useCache && key) {
2997
+ const sourceKeys = toSourceKeys();
2998
+ const primaryKey = cacheCollectionKey(client.databaseName, physical);
2999
+ const versions = { [primaryKey]: cache.versionOf(primaryKey) };
3000
+ for (const col of sourceKeys) {
3001
+ if (!(col in versions)) versions[col] = cache.versionOf(col);
3002
+ }
3003
+ const hit = cache.get(key);
3004
+ if (hit !== void 0) return Promise.resolve(hit);
3005
+ return runDriver().then((value) => {
3006
+ cache.set(key, value, void 0, sourceKeys, versions);
3007
+ return value;
3008
+ });
3009
+ }
3010
+ return runDriver();
3011
+ };
3012
+ if (shouldDedupe) {
3013
+ const dedupeKey = `${cacheCollectionKey(client.databaseName, physical)}|${opName}|${stableHash([pipeline, options])}`;
3014
+ return opts.inFlight.run(dedupeKey, runOnce);
3015
+ }
3016
+ return runOnce();
3017
+ };
3018
+ };
2837
3019
 
2838
3020
  // src/service/aggregation/group.ts
2839
3021
  var makeGroupOps = (ctx) => {
2840
- const { deps: deps2, coll, mergeAggOptions: mergeAggOptions2 } = ctx;
3022
+ const { coll, mergeAggOptions: mergeAggOptions2, cachedAggregate } = ctx;
2841
3023
  const groupBy = async (collection, baseFilter, config, customization = {}, options) => {
2842
3024
  if (config.limit !== void 0 && (!Number.isFinite(config.limit) || config.limit < 0)) {
2843
3025
  throw new BadRequest("groupBy: `limit` must be a non-negative finite number");
@@ -2850,13 +3032,13 @@ var makeGroupOps = (ctx) => {
2850
3032
  ...config.limit !== void 0 ? [{ $limit: config.limit }] : [],
2851
3033
  ...customization.postPipeline ?? []
2852
3034
  ];
2853
- return defineCrudOp(
2854
- deps2,
3035
+ return cachedAggregate({
2855
3036
  collection,
2856
- "mongo.groupBy",
2857
- (r) => coll(collection).aggregate(pipeline, mergeAggOptions2(r.driverOpts, r.sdk)).toArray(),
2858
- options
2859
- );
3037
+ opName: "mongo.groupBy",
3038
+ pipeline,
3039
+ ...options !== void 0 ? { options } : {},
3040
+ execute: (r) => coll(collection).aggregate(pipeline, mergeAggOptions2(r.driverOpts, r.sdk)).toArray()
3041
+ });
2860
3042
  };
2861
3043
  const dateRangeAnalysis = async (collection, baseFilter, dateConfig, analysisConfig = {}, customization = {}, options) => {
2862
3044
  if (analysisConfig.limit !== void 0 && (!Number.isFinite(analysisConfig.limit) || analysisConfig.limit < 0)) {
@@ -2880,20 +3062,20 @@ var makeGroupOps = (ctx) => {
2880
3062
  ...analysisConfig.limit !== void 0 ? [{ $limit: analysisConfig.limit }] : [],
2881
3063
  ...customization.postPipeline ?? []
2882
3064
  ];
2883
- return defineCrudOp(
2884
- deps2,
3065
+ return cachedAggregate({
2885
3066
  collection,
2886
- "mongo.dateRangeAnalysis",
2887
- (r) => coll(collection).aggregate(pipeline, mergeAggOptions2(r.driverOpts, r.sdk)).toArray(),
2888
- options
2889
- );
3067
+ opName: "mongo.dateRangeAnalysis",
3068
+ pipeline,
3069
+ ...options !== void 0 ? { options } : {},
3070
+ execute: (r) => coll(collection).aggregate(pipeline, mergeAggOptions2(r.driverOpts, r.sdk)).toArray()
3071
+ });
2890
3072
  };
2891
3073
  return { groupBy, dateRangeAnalysis };
2892
3074
  };
2893
3075
 
2894
3076
  // src/service/aggregation/lookup-join.ts
2895
3077
  var makeLookupJoinOp = (ctx) => {
2896
- const { deps: deps2, coll, mergeAggOptions: mergeAggOptions2, resolve } = ctx;
3078
+ const { coll, mergeAggOptions: mergeAggOptions2, resolve, cachedAggregate } = ctx;
2897
3079
  const lookupJoin = async (collection, baseFilter, lookups, customization = {}, options) => {
2898
3080
  const pipeline = [
2899
3081
  ...customization.prePipeline ?? [],
@@ -2912,13 +3094,15 @@ var makeLookupJoinOp = (ctx) => {
2912
3094
  })),
2913
3095
  ...customization.postPipeline ?? []
2914
3096
  ];
2915
- return defineCrudOp(
2916
- deps2,
3097
+ return cachedAggregate({
2917
3098
  collection,
2918
- "mongo.lookupJoin",
2919
- (r) => coll(collection).aggregate(pipeline, mergeAggOptions2(r.driverOpts, r.sdk)).toArray(),
2920
- options
2921
- );
3099
+ opName: "mongo.lookupJoin",
3100
+ pipeline,
3101
+ ...options !== void 0 ? { options } : {},
3102
+ // Writes to any joined collection must invalidate the cached join.
3103
+ sources: lookups.map((l) => l.fromCollection),
3104
+ execute: (r) => coll(collection).aggregate(pipeline, mergeAggOptions2(r.driverOpts, r.sdk)).toArray()
3105
+ });
2922
3106
  };
2923
3107
  return { lookupJoin };
2924
3108
  };
@@ -2985,10 +3169,13 @@ var buildSearchStages = (baseFilter, searchConfig) => searchConfig.useRegex ? bu
2985
3169
 
2986
3170
  // src/service/aggregation/text-search.ts
2987
3171
  var makeTextSearchOp = (ctx) => {
2988
- const { deps: deps2, coll, mergeAggOptions: mergeAggOptions2 } = ctx;
3172
+ const { coll, mergeAggOptions: mergeAggOptions2, cachedAggregate } = ctx;
2989
3173
  const textSearch = async (collection, baseFilter, searchConfig, paginationConfig = {}, customization = {}, options) => {
2990
3174
  const normalized = normalizePageLimit(
2991
- { page: paginationConfig.page, limit: paginationConfig.limit },
3175
+ {
3176
+ ...paginationConfig.page !== void 0 ? { page: paginationConfig.page } : {},
3177
+ ...paginationConfig.limit !== void 0 ? { limit: paginationConfig.limit } : {}
3178
+ },
2992
3179
  paginationConfig.maxLimit ?? DEFAULT_MAX_LIMIT
2993
3180
  );
2994
3181
  const searchStages = buildSearchStages(
@@ -3011,19 +3198,19 @@ var makeTextSearchOp = (ctx) => {
3011
3198
  }
3012
3199
  }
3013
3200
  ];
3014
- return defineCrudOp(
3015
- deps2,
3201
+ return cachedAggregate({
3016
3202
  collection,
3017
- "mongo.textSearch",
3018
- async (r) => {
3203
+ opName: "mongo.textSearch",
3204
+ pipeline,
3205
+ ...options !== void 0 ? { options } : {},
3206
+ execute: async (r) => {
3019
3207
  const rows = await coll(collection).aggregate(pipeline, mergeAggOptions2(r.driverOpts, r.sdk)).toArray();
3020
3208
  const row = rows[0];
3021
3209
  const data = row?.data ?? [];
3022
3210
  const totalCount = row?.totalCount?.[0]?.count ?? 0;
3023
3211
  return buildPaginationResult(data, totalCount, normalized.page, normalized.limit);
3024
- },
3025
- options
3026
- );
3212
+ }
3213
+ });
3027
3214
  };
3028
3215
  return { textSearch };
3029
3216
  };
@@ -3037,7 +3224,8 @@ var makeAggregationOps = (client, dbLabel, logger, opts) => {
3037
3224
  resolve,
3038
3225
  deps: deps2,
3039
3226
  coll,
3040
- mergeAggOptions
3227
+ mergeAggOptions,
3228
+ cachedAggregate: createCachedAggregate({ client, deps: deps2, opts, resolve })
3041
3229
  };
3042
3230
  return {
3043
3231
  ...makeAggregateOps(ctx),
@@ -3132,7 +3320,12 @@ var createCrudContext = (client, dbLabel, logger, opts) => {
3132
3320
  if (resolved.driverOpts.projection !== void 0) return;
3133
3321
  const docs = shape === "many" ? value : shape === "one" && value !== null ? [value] : [];
3134
3322
  checkDocsDrift(
3135
- { logger, db: dbLabel, drift: mode, getSchema: opts.getSchema },
3323
+ {
3324
+ logger,
3325
+ db: dbLabel,
3326
+ drift: mode,
3327
+ ...opts.getSchema !== void 0 ? { getSchema: opts.getSchema } : {}
3328
+ },
3136
3329
  String(collection),
3137
3330
  opName,
3138
3331
  docs
@@ -3155,10 +3348,12 @@ var createCrudContext = (client, dbLabel, logger, opts) => {
3155
3348
  });
3156
3349
  const runOnce = () => {
3157
3350
  if (useCache && key) {
3351
+ const colKey = cacheCollectionKey(client.databaseName, physical);
3352
+ const versions = { [colKey]: opts.cache.versionOf(colKey) };
3158
3353
  const hit = opts.cache.get(key);
3159
3354
  if (hit !== void 0) return Promise.resolve(hit);
3160
3355
  return fetchAndCheck().then((value) => {
3161
- opts.cache.set(key, value);
3356
+ opts.cache.set(key, value, void 0, void 0, versions);
3162
3357
  return value;
3163
3358
  });
3164
3359
  }
@@ -3210,7 +3405,7 @@ var mergeMongoActiveFilter = (softDelete, filter) => {
3210
3405
 
3211
3406
  // src/service/crud/read-ops.ts
3212
3407
  var makeReadOps = (ctx) => {
3213
- const { client, opts } = ctx;
3408
+ const { opts } = ctx;
3214
3409
  const { coll, toDriverFilter, read, normalizeFindOptions } = ctx;
3215
3410
  const getOne = async (collection, filter, options) => {
3216
3411
  const f = filter ?? {};
@@ -3223,9 +3418,9 @@ var makeReadOps = (ctx) => {
3223
3418
  async (r) => {
3224
3419
  const opts2 = {
3225
3420
  ...r.driverOpts,
3226
- session: r.sdk.session,
3227
- maxTimeMS: r.sdk.maxTimeMS,
3228
- hint: r.sdk.hint
3421
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3422
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
3423
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {}
3229
3424
  };
3230
3425
  return coll(collection).findOne(toDriverFilter(f), opts2);
3231
3426
  },
@@ -3268,9 +3463,9 @@ var makeReadOps = (ctx) => {
3268
3463
  async (r) => {
3269
3464
  const opts2 = {
3270
3465
  ...r.driverOpts,
3271
- session: r.sdk.session,
3272
- maxTimeMS: r.sdk.maxTimeMS,
3273
- hint: r.sdk.hint
3466
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3467
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
3468
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {}
3274
3469
  };
3275
3470
  return coll(collection).find(toDriverFilter(filter ?? {}), opts2).toArray();
3276
3471
  },
@@ -3317,9 +3512,9 @@ var makeReadOps = (ctx) => {
3317
3512
  async (r) => {
3318
3513
  const opts2 = {
3319
3514
  ...r.driverOpts,
3320
- session: r.sdk.session,
3321
- maxTimeMS: r.sdk.maxTimeMS,
3322
- hint: r.sdk.hint
3515
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3516
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
3517
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {}
3323
3518
  };
3324
3519
  return coll(collection).countDocuments(toDriverFilter(filter ?? {}), opts2);
3325
3520
  },
@@ -3336,9 +3531,9 @@ var makeReadOps = (ctx) => {
3336
3531
  async (r) => {
3337
3532
  const opts2 = {
3338
3533
  ...r.driverOpts,
3339
- session: r.sdk.session,
3340
- maxTimeMS: r.sdk.maxTimeMS,
3341
- hint: r.sdk.hint
3534
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3535
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
3536
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {}
3342
3537
  };
3343
3538
  return coll(collection).distinct(
3344
3539
  String(field),
@@ -3355,8 +3550,8 @@ var makeReadOps = (ctx) => {
3355
3550
  async (r) => {
3356
3551
  const opts2 = {
3357
3552
  ...r.driverOpts,
3358
- session: r.sdk.session,
3359
- maxTimeMS: r.sdk.maxTimeMS
3553
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3554
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {}
3360
3555
  };
3361
3556
  return coll(collection).estimatedDocumentCount(opts2);
3362
3557
  },
@@ -3393,9 +3588,9 @@ var makeWatchOps = (ctx) => {
3393
3588
  async (r) => {
3394
3589
  const opts = {
3395
3590
  ...r.driverOpts,
3396
- session: r.sdk.session,
3397
- maxTimeMS: r.sdk.maxTimeMS,
3398
- hint: r.sdk.hint
3591
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3592
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
3593
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {}
3399
3594
  };
3400
3595
  return execute(opts);
3401
3596
  },
@@ -3426,7 +3621,7 @@ var makeWriteOps = (ctx, readOps) => {
3426
3621
  async (r) => {
3427
3622
  const insertOpts = {
3428
3623
  ...r.driverOpts,
3429
- session: r.sdk.session
3624
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3430
3625
  };
3431
3626
  return coll(collection).insertOne(doc, insertOpts);
3432
3627
  },
@@ -3456,7 +3651,7 @@ var makeWriteOps = (ctx, readOps) => {
3456
3651
  async (r) => {
3457
3652
  const bulkOpts = {
3458
3653
  ...r.driverOpts,
3459
- session: r.sdk.session
3654
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3460
3655
  };
3461
3656
  return coll(collection).insertMany(
3462
3657
  docs,
@@ -3484,7 +3679,7 @@ var makeWriteOps = (ctx, readOps) => {
3484
3679
  async (r) => {
3485
3680
  const updateOpts = {
3486
3681
  ...r.driverOpts,
3487
- session: r.sdk.session
3682
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3488
3683
  };
3489
3684
  return coll(collection).updateOne(
3490
3685
  toDriverFilter(filter),
@@ -3509,7 +3704,7 @@ var makeWriteOps = (ctx, readOps) => {
3509
3704
  async (r) => {
3510
3705
  const updateOpts = {
3511
3706
  ...r.driverOpts,
3512
- session: r.sdk.session
3707
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3513
3708
  };
3514
3709
  return coll(collection).updateMany(
3515
3710
  toDriverFilter(filter),
@@ -3530,9 +3725,9 @@ var makeWriteOps = (ctx, readOps) => {
3530
3725
  async (r) => {
3531
3726
  const fOpts = {
3532
3727
  ...r.driverOpts,
3533
- session: r.sdk.session,
3534
- maxTimeMS: r.sdk.maxTimeMS,
3535
- hint: r.sdk.hint,
3728
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3729
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
3730
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {},
3536
3731
  // `after` is the ORM's default, but honor an explicit caller override.
3537
3732
  returnDocument: r.driverOpts.returnDocument ?? "after"
3538
3733
  };
@@ -3555,9 +3750,9 @@ var makeWriteOps = (ctx, readOps) => {
3555
3750
  async (r) => {
3556
3751
  const fOpts = {
3557
3752
  ...r.driverOpts,
3558
- session: r.sdk.session,
3559
- maxTimeMS: r.sdk.maxTimeMS,
3560
- hint: r.sdk.hint,
3753
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3754
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
3755
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {},
3561
3756
  // `after` is the ORM's default, but honor an explicit caller override.
3562
3757
  returnDocument: r.driverOpts.returnDocument ?? "after"
3563
3758
  };
@@ -3579,7 +3774,7 @@ var makeWriteOps = (ctx, readOps) => {
3579
3774
  async (r) => {
3580
3775
  const repOpts = {
3581
3776
  ...r.driverOpts,
3582
- session: r.sdk.session
3777
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3583
3778
  };
3584
3779
  return coll(collection).replaceOne(
3585
3780
  toDriverFilter(filter),
@@ -3603,7 +3798,7 @@ var makeWriteOps = (ctx, readOps) => {
3603
3798
  async (r) => {
3604
3799
  const delOpts = {
3605
3800
  ...r.driverOpts,
3606
- session: r.sdk.session
3801
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3607
3802
  };
3608
3803
  return coll(collection).deleteOne(toDriverFilter(filter), delOpts);
3609
3804
  },
@@ -3623,7 +3818,7 @@ var makeWriteOps = (ctx, readOps) => {
3623
3818
  async (r) => {
3624
3819
  const delOpts = {
3625
3820
  ...r.driverOpts,
3626
- session: r.sdk.session
3821
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3627
3822
  };
3628
3823
  return coll(collection).deleteMany(toDriverFilter(filter), delOpts);
3629
3824
  },
@@ -3639,9 +3834,9 @@ var makeWriteOps = (ctx, readOps) => {
3639
3834
  async (r) => {
3640
3835
  const fOpts = {
3641
3836
  ...r.driverOpts,
3642
- session: r.sdk.session,
3643
- maxTimeMS: r.sdk.maxTimeMS,
3644
- hint: r.sdk.hint
3837
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3838
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
3839
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {}
3645
3840
  };
3646
3841
  return coll(collection).findOneAndDelete(
3647
3842
  toDriverFilter(filter),
@@ -3661,7 +3856,7 @@ var makeWriteOps = (ctx, readOps) => {
3661
3856
  async (r) => {
3662
3857
  const updateOpts = {
3663
3858
  ...r.driverOpts,
3664
- session: r.sdk.session
3859
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3665
3860
  };
3666
3861
  return coll(collection).updateOne(
3667
3862
  active,
@@ -3682,7 +3877,7 @@ var makeWriteOps = (ctx, readOps) => {
3682
3877
  async (r) => {
3683
3878
  const updateOpts = {
3684
3879
  ...r.driverOpts,
3685
- session: r.sdk.session,
3880
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
3686
3881
  upsert: true
3687
3882
  };
3688
3883
  return coll(collection).updateOne(toDriverFilter(filter), formatted, updateOpts);
@@ -3711,7 +3906,7 @@ var makeWriteOps = (ctx, readOps) => {
3711
3906
  async (r) => {
3712
3907
  const bulkOpts = {
3713
3908
  ...r.driverOpts,
3714
- session: r.sdk.session
3909
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3715
3910
  };
3716
3911
  return coll(collection).bulkWrite(writes, bulkOpts);
3717
3912
  },
@@ -3732,7 +3927,7 @@ var makeWriteOps = (ctx, readOps) => {
3732
3927
  async (r) => {
3733
3928
  const bulkOpts = {
3734
3929
  ...r.driverOpts,
3735
- session: r.sdk.session
3930
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {}
3736
3931
  };
3737
3932
  return coll(collection).bulkWrite(operations, bulkOpts);
3738
3933
  },
@@ -3870,7 +4065,7 @@ var makePaginateCursor = (ctx) => {
3870
4065
  throw new BadRequest("paginateCursor: provide either `after` or `before`, not both");
3871
4066
  }
3872
4067
  const limit = normalizePageLimit(
3873
- { page: 1, limit: config.limit },
4068
+ { page: 1, ...config.limit !== void 0 ? { limit: config.limit } : {} },
3874
4069
  config.maxLimit ?? DEFAULT_MAX_LIMIT,
3875
4070
  { limit: DEFAULT_FIND_LIMIT }
3876
4071
  ).limit;
@@ -3897,9 +4092,9 @@ var makePaginateCursor = (ctx) => {
3897
4092
  async (r) => {
3898
4093
  const findOpts = {
3899
4094
  ...r.driverOpts,
3900
- session: r.sdk.session,
3901
- maxTimeMS: r.sdk.maxTimeMS,
3902
- hint: r.sdk.hint
4095
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
4096
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
4097
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {}
3903
4098
  };
3904
4099
  return client.collection(resolve(String(collection))).find(filter, findOpts).sort(effectiveSort).limit(limit + 1).toArray();
3905
4100
  },
@@ -3912,7 +4107,7 @@ var makePaginateCursor = (ctx) => {
3912
4107
  logger,
3913
4108
  db: dbLabel,
3914
4109
  drift: resolveDriftMode(config.queryOptions?.drift, opts.drift),
3915
- getSchema: opts.getSchema
4110
+ ...opts.getSchema !== void 0 ? { getSchema: opts.getSchema } : {}
3916
4111
  },
3917
4112
  String(collection),
3918
4113
  "mongo.paginateCursor",
@@ -3930,10 +4125,13 @@ var makePaginateCursor = (ctx) => {
3930
4125
 
3931
4126
  // src/service/pagination/offset.ts
3932
4127
  var makePaginateFlexible = (ctx) => {
3933
- const { client, dbLabel, logger, opts, resolve, deps: deps2 } = ctx;
4128
+ const { client, dbLabel, logger, opts, resolve, cachedAggregate } = ctx;
3934
4129
  const paginateFlexible = async (collection, baseFilter, config = {}) => {
3935
4130
  const normalized = normalizePageLimit(
3936
- { page: config.page, limit: config.limit },
4131
+ {
4132
+ ...config.page !== void 0 ? { page: config.page } : {},
4133
+ ...config.limit !== void 0 ? { limit: config.limit } : {}
4134
+ },
3937
4135
  config.maxLimit ?? DEFAULT_MAX_LIMIT
3938
4136
  );
3939
4137
  const dataStages = [
@@ -3952,17 +4150,18 @@ var makePaginateFlexible = (ctx) => {
3952
4150
  }
3953
4151
  }
3954
4152
  ];
3955
- return defineCrudOp(
3956
- deps2,
4153
+ return cachedAggregate({
3957
4154
  collection,
3958
- "mongo.paginateFlexible",
3959
- async (r) => {
4155
+ opName: "mongo.paginateFlexible",
4156
+ pipeline,
4157
+ ...config.queryOptions !== void 0 ? { options: config.queryOptions } : {},
4158
+ execute: async (r) => {
3960
4159
  const aggOptions = {
3961
4160
  ...r.driverOpts,
3962
- session: r.sdk.session,
3963
- maxTimeMS: r.sdk.maxTimeMS,
3964
- hint: r.sdk.hint,
3965
- batchSize: r.sdk.batchSize
4161
+ ...r.sdk.session !== void 0 ? { session: r.sdk.session } : {},
4162
+ ...r.sdk.maxTimeMS !== void 0 ? { maxTimeMS: r.sdk.maxTimeMS } : {},
4163
+ ...r.sdk.hint !== void 0 ? { hint: r.sdk.hint } : {},
4164
+ ...r.sdk.batchSize !== void 0 ? { batchSize: r.sdk.batchSize } : {}
3966
4165
  };
3967
4166
  const rows = await client.collection(resolve(String(collection))).aggregate(pipeline, aggOptions).toArray();
3968
4167
  const row = rows[0];
@@ -3973,16 +4172,15 @@ var makePaginateFlexible = (ctx) => {
3973
4172
  logger,
3974
4173
  db: dbLabel,
3975
4174
  drift: resolveDriftMode(config.queryOptions?.drift, opts.drift),
3976
- getSchema: opts.getSchema
4175
+ ...opts.getSchema !== void 0 ? { getSchema: opts.getSchema } : {}
3977
4176
  },
3978
4177
  String(collection),
3979
4178
  "mongo.paginateFlexible",
3980
4179
  data
3981
4180
  );
3982
4181
  return buildPaginationResult(data, totalCount, normalized.page, normalized.limit);
3983
- },
3984
- config.queryOptions
3985
- );
4182
+ }
4183
+ });
3986
4184
  };
3987
4185
  return { paginateFlexible };
3988
4186
  };
@@ -3997,7 +4195,8 @@ var makePaginationOps = (client, dbLabel, logger, opts) => {
3997
4195
  logger,
3998
4196
  opts,
3999
4197
  resolve,
4000
- deps: deps2
4198
+ deps: deps2,
4199
+ cachedAggregate: createCachedAggregate({ client, deps: deps2, opts, resolve })
4001
4200
  };
4002
4201
  return {
4003
4202
  ...makePaginateFlexible(ctx),
@@ -4174,23 +4373,29 @@ var buildManager = (deps2, dbKey, dbName, client) => {
4174
4373
  const crud = makeCrudOps(handle, dbName, logger, {
4175
4374
  resolveCollectionName,
4176
4375
  wrapMongoErrors: deps2.wrapMongoErrors,
4177
- cache: sharedCache,
4376
+ ...sharedCache !== void 0 ? { cache: sharedCache } : {},
4178
4377
  dedupeReads,
4179
4378
  inFlight,
4180
4379
  hooks,
4181
4380
  timestamps,
4182
- drift: deps2.drift,
4183
- getSchema: (logical) => getDefinition(logical)?.schema
4381
+ ...deps2.drift !== void 0 ? { drift: deps2.drift } : {},
4382
+ ...getDefinition !== void 0 ? { getSchema: (logical) => getDefinition(logical)?.schema } : {}
4184
4383
  });
4185
4384
  const pagination = makePaginationOps(handle, dbName, logger, {
4186
4385
  resolveCollectionName,
4187
4386
  wrapMongoErrors: deps2.wrapMongoErrors,
4188
- drift: deps2.drift,
4189
- getSchema: (logical) => getDefinition(logical)?.schema
4387
+ ...deps2.drift !== void 0 ? { drift: deps2.drift } : {},
4388
+ ...getDefinition !== void 0 ? { getSchema: (logical) => getDefinition(logical)?.schema } : {},
4389
+ ...sharedCache !== void 0 ? { cache: sharedCache } : {},
4390
+ dedupeReads,
4391
+ inFlight
4190
4392
  });
4191
4393
  const aggregation = makeAggregationOps(handle, dbName, logger, {
4192
4394
  resolveCollectionName,
4193
- wrapMongoErrors: deps2.wrapMongoErrors
4395
+ wrapMongoErrors: deps2.wrapMongoErrors,
4396
+ ...sharedCache !== void 0 ? { cache: sharedCache } : {},
4397
+ dedupeReads,
4398
+ inFlight
4194
4399
  });
4195
4400
  const schema = makeSchemaOps(handle, logger, { resolveCollectionName, getDefinition });
4196
4401
  const transactionOps = makeTransactionOps(client, handle, logger, config, {
@@ -4201,7 +4406,7 @@ var buildManager = (deps2, dbKey, dbName, client) => {
4201
4406
  findMany: async (logical, filter, options) => {
4202
4407
  const physical = resolveCollectionName(logical);
4203
4408
  return handle.collection(physical).find(filter, {
4204
- projection: options?.projection,
4409
+ ...options?.projection !== void 0 ? { projection: options.projection } : {},
4205
4410
  limit: options?.limit ?? 1e4
4206
4411
  }).toArray();
4207
4412
  }
@@ -4241,11 +4446,11 @@ var createMongoService = (dbClients, configOverrides = {}) => {
4241
4446
  dbClients,
4242
4447
  config,
4243
4448
  logger,
4244
- sharedCache,
4449
+ ...sharedCache !== void 0 ? { sharedCache } : {},
4245
4450
  dedupeReads,
4246
4451
  inFlight,
4247
4452
  wrapMongoErrors: runtime.wrapMongoErrors,
4248
- drift: configOverrides.drift,
4453
+ ...configOverrides.drift !== void 0 ? { drift: configOverrides.drift } : {},
4249
4454
  collectionRefs,
4250
4455
  db
4251
4456
  },