monsqlize 2.0.0 → 2.0.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.
@@ -0,0 +1,39 @@
1
+ # v2.0.1 — 2026-06-03
2
+
3
+ ## Overview
4
+
5
+ v2.0.1 is a compatibility and documentation patch for the v2 TypeScript line. It preserves the v2.0.0 public package shape while tightening v1 smooth-upgrade behavior and aligning the published documentation with the current runtime.
6
+
7
+ ---
8
+
9
+ ## Fixes
10
+
11
+ - **Model routing:** `definition.collection` is now used as the actual MongoDB collection name, with `definition.name` and the registered model name as fallbacks.
12
+ - **Model pool scope:** Model connection metadata now routes through the configured pool and database when creating scoped collections.
13
+ - **Model cache identity:** Model instance cache keys include pool, database, registered model name, and actual collection name to avoid cross-scope reuse.
14
+ - **Populate compatibility:** `relations.from` resolves registered Model names to their actual MongoDB collection names, while still allowing direct collection names when no registered Model exists.
15
+ - **Automatic indexes:** Model index creation is deduplicated per runtime / pool / database / collection / index fingerprint; pending and fulfilled tasks are reused, while failed tasks may retry.
16
+ - **Cache compatibility:** Runtime cache option normalization preserves v1 aliases and millisecond TTL semantics.
17
+ - **Pool compatibility:** Pool selector operation names accept v1-style string inputs.
18
+
19
+ ---
20
+
21
+ ## Documentation and Types
22
+
23
+ - Added `ModelDefinition.collection` and `ModelDefinition.name` to the public type surface.
24
+ - Documented the actual automatic-index flow: `connect()` only registers models; `ModelInstance` creation schedules per-index `createIndex()` calls; no `listIndexes()` preflight is performed.
25
+ - Updated the current release status, API index count, examples count, and advanced capability index entries.
26
+ - Kept historical changelog archive links unchanged; current published package links remain clean.
27
+
28
+ ---
29
+
30
+ ## Validation
31
+
32
+ - `npm run build`
33
+ - `npm run type-check`
34
+ - `npm run lint`
35
+ - `npm run test:runtime`
36
+ - `npm run test:compatibility`
37
+ - `npm run check:sizes:strict`
38
+ - `npm run test:examples`
39
+ - `npm pack --json --dry-run`
@@ -705,6 +705,13 @@ function serializeDocument(document) {
705
705
  }
706
706
 
707
707
  // src/capabilities/model/model-instance-helpers.ts
708
+ function resolveRegisteredCollectionName(registered, fallback) {
709
+ if (!registered) {
710
+ return fallback;
711
+ }
712
+ const definition = registered.definition;
713
+ return definition.collection ?? definition.name ?? registered.collectionName;
714
+ }
708
715
  async function populateModelPath(context, docs, path2) {
709
716
  const config = normalizePopulateConfig(path2);
710
717
  if (docs.length === 0) {
@@ -728,7 +735,8 @@ async function populateModelPath(context, docs, path2) {
728
735
  database: registered?.definition.connection?.database ?? context.dbName,
729
736
  pool: registered?.definition.connection?.pool ?? context.poolName
730
737
  };
731
- const relatedCollection = context.runtime.scopedCollection(relation.from, scope);
738
+ const relatedCollectionName = resolveRegisteredCollectionName(registered, relation.from);
739
+ const relatedCollection = context.runtime.scopedCollection(relatedCollectionName, scope);
732
740
  const relatedModel = Model.has(relation.from) ? context.runtime.scopedModel(relation.from, scope) : null;
733
741
  const relatedDocs = await relatedCollection.find({
734
742
  [relation.foreignField]: { $in: keys },
@@ -971,9 +979,85 @@ function forceDeleteManyDocuments(context, filter, options) {
971
979
  }
972
980
 
973
981
  // src/capabilities/model/model-instance-config.ts
982
+ var runtimeModelIndexTasks = /* @__PURE__ */ new WeakMap();
983
+ var fallbackModelIndexTasks = /* @__PURE__ */ new Map();
974
984
  function toCompatDefinition(definition) {
975
985
  return definition;
976
986
  }
987
+ function stableIndexStringify(value) {
988
+ if (value instanceof Date) {
989
+ return JSON.stringify(value.toISOString());
990
+ }
991
+ if (Array.isArray(value)) {
992
+ return `[${value.map((item) => stableIndexStringify(item)).join(",")}]`;
993
+ }
994
+ if (value && typeof value === "object") {
995
+ const entries = Object.entries(value).filter(([, current]) => current !== void 0).sort(([left], [right]) => left.localeCompare(right));
996
+ return `{${entries.map(([key, current]) => `${JSON.stringify(key)}:${stableIndexStringify(current)}`).join(",")}}`;
997
+ }
998
+ return JSON.stringify(value) ?? "undefined";
999
+ }
1000
+ function getIndexTaskRegistry(runtime) {
1001
+ if (!runtime) {
1002
+ return fallbackModelIndexTasks;
1003
+ }
1004
+ let registry = runtimeModelIndexTasks.get(runtime);
1005
+ if (!registry) {
1006
+ registry = /* @__PURE__ */ new Map();
1007
+ runtimeModelIndexTasks.set(runtime, registry);
1008
+ }
1009
+ return registry;
1010
+ }
1011
+ function resolveIndexTaskScope(collection, options) {
1012
+ try {
1013
+ const namespace = collection.getNamespace();
1014
+ return {
1015
+ dbName: options?.dbName ?? namespace.db,
1016
+ poolName: options?.poolName ?? "default",
1017
+ collectionName: options?.collectionName ?? namespace.collection
1018
+ };
1019
+ } catch {
1020
+ return {
1021
+ dbName: options?.dbName ?? "default",
1022
+ poolName: options?.poolName ?? "default",
1023
+ collectionName: options?.collectionName ?? "unknown"
1024
+ };
1025
+ }
1026
+ }
1027
+ function warnIndexFailure(runtime, taskKey, error) {
1028
+ const logger = runtime;
1029
+ logger?.logger?.warn?.("[MonSQLize] model index creation failed", {
1030
+ taskKey,
1031
+ error: error instanceof Error ? error.message : String(error)
1032
+ });
1033
+ }
1034
+ function scheduleIndexTask(collection, key, indexOptions, options) {
1035
+ const scope = resolveIndexTaskScope(collection, options);
1036
+ const indexFingerprint = stableIndexStringify({ key, options: indexOptions });
1037
+ const taskKey = `${scope.poolName}:${scope.dbName}:${scope.collectionName}:${indexFingerprint}`;
1038
+ const registry = getIndexTaskRegistry(options?.runtime);
1039
+ const existing = registry.get(taskKey);
1040
+ if (existing && existing.status !== "failed") {
1041
+ return;
1042
+ }
1043
+ const task = {
1044
+ status: "pending",
1045
+ promise: new Promise((resolve) => {
1046
+ setImmediate(() => {
1047
+ Promise.resolve().then(() => collection.createIndex(key, indexOptions)).then(() => {
1048
+ task.status = "fulfilled";
1049
+ resolve();
1050
+ }).catch((error) => {
1051
+ task.status = "failed";
1052
+ task.error = error;
1053
+ warnIndexFailure(options?.runtime, taskKey, error);
1054
+ resolve();
1055
+ });
1056
+ });
1057
+ })
1058
+ };
1059
+ registry.set(taskKey, task);
1060
+ }
977
1061
  function getModelEnums(definition) {
978
1062
  return toCompatDefinition(definition).enums ?? {};
979
1063
  }
@@ -1106,31 +1190,27 @@ function initializeModelV1Methods(target, definition) {
1106
1190
  return {};
1107
1191
  }
1108
1192
  }
1109
- function scheduleModelIndexes(collection, definition, softDeleteConfig) {
1193
+ function scheduleModelIndexes(collection, definition, softDeleteConfig, options) {
1110
1194
  if (softDeleteConfig?.enabled && softDeleteConfig.type === "timestamp" && softDeleteConfig.ttl) {
1111
1195
  const softDeleteIndex = softDeleteConfig;
1112
- setImmediate(() => {
1113
- collection.createIndex(
1114
- { [softDeleteIndex.field]: 1 },
1115
- { expireAfterSeconds: softDeleteIndex.ttl }
1116
- ).catch(() => {
1117
- });
1118
- });
1196
+ scheduleIndexTask(
1197
+ collection,
1198
+ { [softDeleteIndex.field]: 1 },
1199
+ { expireAfterSeconds: softDeleteIndex.ttl },
1200
+ options
1201
+ );
1119
1202
  }
1120
1203
  const indexes = toCompatDefinition(definition).indexes;
1121
1204
  if (!Array.isArray(indexes) || indexes.length === 0) {
1122
1205
  return;
1123
1206
  }
1124
- setImmediate(() => {
1125
- for (const indexSpec of indexes) {
1126
- if (!indexSpec?.key) {
1127
- continue;
1128
- }
1129
- const { key, ...indexOptions } = indexSpec;
1130
- collection.createIndex(key, indexOptions).catch(() => {
1131
- });
1207
+ for (const indexSpec of indexes) {
1208
+ if (!indexSpec?.key) {
1209
+ continue;
1132
1210
  }
1133
- });
1211
+ const { key, ...indexOptions } = indexSpec;
1212
+ scheduleIndexTask(collection, key, indexOptions, options);
1213
+ }
1134
1214
  }
1135
1215
 
1136
1216
  // src/capabilities/model/model-write-helpers.ts
@@ -1693,7 +1773,12 @@ var ModelInstance = class {
1693
1773
  this.softDeleteConfig = this._softDeleteConfig;
1694
1774
  this._versionConfig = resolveModelVersionConfig(options.definition);
1695
1775
  this._v1HooksFactory = resolveModelHooksFactory(options.definition);
1696
- scheduleModelIndexes(this.collection, options.definition, this._softDeleteConfig);
1776
+ scheduleModelIndexes(this.collection, options.definition, this._softDeleteConfig, {
1777
+ runtime: this.runtime,
1778
+ dbName: options.dbName,
1779
+ poolName: options.poolName,
1780
+ collectionName: options.collectionName
1781
+ });
1697
1782
  this._v1InstanceMethods = initializeModelV1Methods(this, options.definition);
1698
1783
  }
1699
1784
  /** v1 compat: expose relations map as _relations */
@@ -3113,10 +3198,19 @@ var HealthChecker = class {
3113
3198
  };
3114
3199
 
3115
3200
  // src/capabilities/pool/pool-selector.ts
3201
+ var POOL_STRATEGY_ALIASES = {
3202
+ "round-robin": "roundRobin",
3203
+ "least-connections": "leastConnections",
3204
+ random: "weighted"
3205
+ };
3206
+ function normalizePoolStrategy(strategy) {
3207
+ const value = strategy ?? "auto";
3208
+ return POOL_STRATEGY_ALIASES[value] ?? value;
3209
+ }
3116
3210
  var PoolSelector = class {
3117
3211
  constructor(options = {}) {
3118
3212
  this._roundRobinIndex = /* @__PURE__ */ new Map();
3119
- this._strategy = options.strategy ?? "auto";
3213
+ this._strategy = normalizePoolStrategy(options.strategy);
3120
3214
  this._logger = options.logger ?? console;
3121
3215
  }
3122
3216
  select(pools, context) {
@@ -3229,8 +3323,8 @@ var PoolSelector = class {
3229
3323
  return pools[0].name;
3230
3324
  }
3231
3325
  setStrategy(strategy) {
3232
- this._strategy = strategy;
3233
- this._logger.info?.(`[PoolSelector] Strategy changed: ${strategy}`);
3326
+ this._strategy = normalizePoolStrategy(strategy);
3327
+ this._logger.info?.(`[PoolSelector] Strategy changed: ${this._strategy}`);
3234
3328
  }
3235
3329
  getStrategy() {
3236
3330
  return this._strategy;
@@ -7424,6 +7518,92 @@ function createRuntimeAccessors(config) {
7424
7518
  };
7425
7519
  }
7426
7520
 
7521
+ // src/entry/runtime-compat-accessors.ts
7522
+ function asRuntimeCompatRecord(value) {
7523
+ return value;
7524
+ }
7525
+ function requireCompatDbInstance(value) {
7526
+ const dbInstance = asRuntimeCompatRecord(value).dbInstance;
7527
+ if (!dbInstance) {
7528
+ throw createError(ErrorCodes.NOT_CONNECTED, "Database is not connected. Call connect() first.");
7529
+ }
7530
+ return dbInstance;
7531
+ }
7532
+ function requireCompatPoolManagerRecord(value) {
7533
+ const poolManager = asRuntimeCompatRecord(value)._poolManager;
7534
+ if (!poolManager) {
7535
+ throw createError(ErrorCodes.NO_POOL_MANAGER, "No pool manager configured. Add pools to MonSQLize constructor options.");
7536
+ }
7537
+ return poolManager;
7538
+ }
7539
+ function resolvePoolClientFromRecord(poolManager, poolName) {
7540
+ const getPoolV1 = poolManager["_getPool"];
7541
+ const getPoolV2 = poolManager["getPool"];
7542
+ if (typeof getPoolV1 === "function") {
7543
+ return getPoolV1.call(poolManager, poolName) ?? null;
7544
+ }
7545
+ if (typeof getPoolV2 === "function") {
7546
+ try {
7547
+ return getPoolV2.call(poolManager, poolName) ?? null;
7548
+ } catch {
7549
+ return null;
7550
+ }
7551
+ }
7552
+ return null;
7553
+ }
7554
+ function assertCompatPoolExists(poolManager, poolName) {
7555
+ if (resolvePoolClientFromRecord(poolManager, poolName)) {
7556
+ return;
7557
+ }
7558
+ const getNames = poolManager["getPoolNames"];
7559
+ const available = typeof getNames === "function" ? getNames.call(poolManager) : [];
7560
+ const error = createError(ErrorCodes.POOL_NOT_FOUND, `Pool '${poolName}' not found. Available pools: [${available.join(", ")}]`);
7561
+ error["available"] = available;
7562
+ throw error;
7563
+ }
7564
+ function createPoolScope(runtime, poolName) {
7565
+ return {
7566
+ collection: (name) => runtime.scopedCollection(name, { pool: poolName }),
7567
+ model: (name) => runtime.scopedModel(name, { pool: poolName }),
7568
+ use: (dbName) => ({
7569
+ collection: (name) => runtime.scopedCollection(name, { pool: poolName, database: dbName }),
7570
+ model: (name) => runtime.scopedModel(name, { pool: poolName, database: dbName })
7571
+ })
7572
+ };
7573
+ }
7574
+ function getRegisteredModelMetadata(registered) {
7575
+ const definition = registered.definition;
7576
+ return {
7577
+ actualCollectionName: definition.collection ?? definition.name ?? registered.collectionName,
7578
+ connection: definition.connection
7579
+ };
7580
+ }
7581
+ function getRuntimeDatabaseName(value) {
7582
+ return asRuntimeCompatRecord(value).databaseName ?? "default";
7583
+ }
7584
+ function getCompatModelInstanceCache(value) {
7585
+ const record = asRuntimeCompatRecord(value);
7586
+ if (!record._modelInstances) {
7587
+ record._modelInstances = new MemoryCache({
7588
+ maxEntries: 1e5,
7589
+ enableStats: false
7590
+ });
7591
+ }
7592
+ return record._modelInstances;
7593
+ }
7594
+ function createCompatModelInstance(config) {
7595
+ return new ModelInstance(
7596
+ config.collection,
7597
+ config.runtime,
7598
+ {
7599
+ collectionName: config.collectionName,
7600
+ dbName: config.dbName,
7601
+ poolName: config.poolName,
7602
+ definition: config.definition
7603
+ }
7604
+ );
7605
+ }
7606
+
7427
7607
  // src/entry/runtime-model.ts
7428
7608
  function createRuntimeModelHost(config) {
7429
7609
  return {
@@ -7438,19 +7618,20 @@ function createRuntimeModelInstance(host, name, scope) {
7438
7618
  if (!registered) {
7439
7619
  throw createError(ErrorCodes.MODEL_NOT_DEFINED, `Model '${name}' is not defined.`);
7440
7620
  }
7441
- const databaseName = registered.definition.connection?.database ?? scope.database ?? resolveDatabaseName(host.options);
7442
- const poolName = registered.definition.connection?.pool ?? scope.pool;
7443
- const cacheKey = `${poolName ?? "default"}:${databaseName}:${registered.collectionName}`;
7621
+ const { actualCollectionName, connection } = getRegisteredModelMetadata(registered);
7622
+ const databaseName = connection?.database ?? scope.database ?? resolveDatabaseName(host.options);
7623
+ const poolName = connection?.pool ?? scope.pool;
7624
+ const cacheKey = `${poolName ?? "default"}:${databaseName}:${registered.collectionName}:${actualCollectionName}`;
7444
7625
  const revision = Model.getRevision(registered.collectionName);
7445
7626
  const cached = host._modelInstances.get(cacheKey);
7446
7627
  if (cached && cached.revision === revision) {
7447
7628
  return cached.instance;
7448
7629
  }
7449
7630
  const instance = new ModelInstance(
7450
- host.scopedCollection(registered.collectionName, { database: databaseName }),
7631
+ host.scopedCollection(actualCollectionName, { database: databaseName, pool: poolName }),
7451
7632
  host.runtime,
7452
7633
  {
7453
- collectionName: registered.collectionName,
7634
+ collectionName: actualCollectionName,
7454
7635
  dbName: databaseName,
7455
7636
  poolName,
7456
7637
  definition: registered.definition
@@ -7533,92 +7714,6 @@ function createRuntimeCoreAccessors(runtime) {
7533
7714
  });
7534
7715
  }
7535
7716
 
7536
- // src/entry/runtime-compat-accessors.ts
7537
- function asRuntimeCompatRecord(value) {
7538
- return value;
7539
- }
7540
- function requireCompatDbInstance(value) {
7541
- const dbInstance = asRuntimeCompatRecord(value).dbInstance;
7542
- if (!dbInstance) {
7543
- throw createError(ErrorCodes.NOT_CONNECTED, "Database is not connected. Call connect() first.");
7544
- }
7545
- return dbInstance;
7546
- }
7547
- function requireCompatPoolManagerRecord(value) {
7548
- const poolManager = asRuntimeCompatRecord(value)._poolManager;
7549
- if (!poolManager) {
7550
- throw createError(ErrorCodes.NO_POOL_MANAGER, "No pool manager configured. Add pools to MonSQLize constructor options.");
7551
- }
7552
- return poolManager;
7553
- }
7554
- function resolvePoolClientFromRecord(poolManager, poolName) {
7555
- const getPoolV1 = poolManager["_getPool"];
7556
- const getPoolV2 = poolManager["getPool"];
7557
- if (typeof getPoolV1 === "function") {
7558
- return getPoolV1.call(poolManager, poolName) ?? null;
7559
- }
7560
- if (typeof getPoolV2 === "function") {
7561
- try {
7562
- return getPoolV2.call(poolManager, poolName) ?? null;
7563
- } catch {
7564
- return null;
7565
- }
7566
- }
7567
- return null;
7568
- }
7569
- function assertCompatPoolExists(poolManager, poolName) {
7570
- if (resolvePoolClientFromRecord(poolManager, poolName)) {
7571
- return;
7572
- }
7573
- const getNames = poolManager["getPoolNames"];
7574
- const available = typeof getNames === "function" ? getNames.call(poolManager) : [];
7575
- const error = createError(ErrorCodes.POOL_NOT_FOUND, `Pool '${poolName}' not found. Available pools: [${available.join(", ")}]`);
7576
- error["available"] = available;
7577
- throw error;
7578
- }
7579
- function createPoolScope(runtime, poolName) {
7580
- return {
7581
- collection: (name) => runtime.scopedCollection(name, { pool: poolName }),
7582
- model: (name) => runtime.scopedModel(name, { pool: poolName }),
7583
- use: (dbName) => ({
7584
- collection: (name) => runtime.scopedCollection(name, { pool: poolName, database: dbName }),
7585
- model: (name) => runtime.scopedModel(name, { pool: poolName, database: dbName })
7586
- })
7587
- };
7588
- }
7589
- function getRegisteredModelMetadata(registered) {
7590
- const definition = registered.definition;
7591
- return {
7592
- actualCollectionName: definition.collection ?? definition.name ?? registered.collectionName,
7593
- connection: definition.connection
7594
- };
7595
- }
7596
- function getRuntimeDatabaseName(value) {
7597
- return asRuntimeCompatRecord(value).databaseName ?? "default";
7598
- }
7599
- function getCompatModelInstanceCache(value) {
7600
- const record = asRuntimeCompatRecord(value);
7601
- if (!record._modelInstances) {
7602
- record._modelInstances = new MemoryCache({
7603
- maxEntries: 1e5,
7604
- enableStats: false
7605
- });
7606
- }
7607
- return record._modelInstances;
7608
- }
7609
- function createCompatModelInstance(config) {
7610
- return new ModelInstance(
7611
- config.collection,
7612
- config.runtime,
7613
- {
7614
- collectionName: config.collectionName,
7615
- dbName: config.dbName,
7616
- poolName: config.poolName,
7617
- definition: config.definition
7618
- }
7619
- );
7620
- }
7621
-
7622
7717
  // src/entry/runtime-admin-bridge.ts
7623
7718
  var import_node_perf_hooks = require("node:perf_hooks");
7624
7719
  function createLegacyCollectionBridge(config) {
@@ -9692,31 +9787,132 @@ function toOptionalNumber(value) {
9692
9787
  function toOptionalBoolean(value) {
9693
9788
  return typeof value === "boolean" ? value : void 0;
9694
9789
  }
9790
+ function isObjectRecord(value) {
9791
+ return !!value && typeof value === "object" && !Array.isArray(value);
9792
+ }
9793
+ function buildMemoryCache(input, fallback = {}) {
9794
+ if (input instanceof MemoryCache) return input;
9795
+ if (isCacheLike(input)) return input;
9796
+ const opts = isObjectRecord(input) ? input : {};
9797
+ return new MemoryCache({
9798
+ maxEntries: toOptionalNumber(opts.maxEntries ?? opts.maxSize ?? fallback.maxEntries ?? fallback.maxSize),
9799
+ maxMemory: toOptionalNumber(opts.maxMemory ?? fallback.maxMemory),
9800
+ defaultTtl: toOptionalNumber(opts.defaultTtl ?? opts.ttl ?? fallback.defaultTtl ?? fallback.ttl),
9801
+ enableStats: toOptionalBoolean(opts.enableStats ?? fallback.enableStats),
9802
+ enableTags: toOptionalBoolean(opts.enableTags ?? fallback.enableTags),
9803
+ cleanupInterval: toOptionalNumber(opts.cleanupInterval ?? fallback.cleanupInterval),
9804
+ enabled: toOptionalBoolean(opts.enabled ?? fallback.enabled)
9805
+ });
9806
+ }
9807
+ function prefixKey(prefix, key) {
9808
+ if (!prefix || key.startsWith(prefix)) return key;
9809
+ return `${prefix}${key}`;
9810
+ }
9811
+ function unprefixKey(prefix, key) {
9812
+ if (!prefix || !key.startsWith(prefix)) return key;
9813
+ return key.slice(prefix.length);
9814
+ }
9815
+ function wrapRemoteCache(cache, options = {}) {
9816
+ const prefix = typeof options.prefix === "string" && options.prefix.length > 0 ? options.prefix : void 0;
9817
+ const defaultTtl = options.defaultTtl;
9818
+ if (!prefix && defaultTtl === void 0) return cache;
9819
+ const key = (value) => prefixKey(prefix, value);
9820
+ const keys = (values) => values.map(key);
9821
+ const resolveTtl = (ttl) => ttl ?? defaultTtl;
9822
+ return new Proxy(cache, {
9823
+ get(target, prop, receiver) {
9824
+ switch (prop) {
9825
+ case "get":
9826
+ return (cacheKey) => target.get(key(cacheKey));
9827
+ case "set":
9828
+ return (cacheKey, value, ttl) => target.set(key(cacheKey), value, resolveTtl(ttl));
9829
+ case "del":
9830
+ return (cacheKey) => target.del(key(cacheKey));
9831
+ case "exists":
9832
+ return (cacheKey) => target.exists(key(cacheKey));
9833
+ case "has":
9834
+ return (cacheKey) => target.has(key(cacheKey));
9835
+ case "getMany":
9836
+ return async (cacheKeys) => {
9837
+ const result = await target.getMany(keys(cacheKeys));
9838
+ return Object.fromEntries(
9839
+ Object.entries(result).map(([cacheKey, value]) => [unprefixKey(prefix, cacheKey), value])
9840
+ );
9841
+ };
9842
+ case "setMany":
9843
+ return (entries, ttl) => target.setMany(
9844
+ Object.fromEntries(
9845
+ Object.entries(entries).map(([cacheKey, value]) => [key(cacheKey), value])
9846
+ ),
9847
+ resolveTtl(ttl)
9848
+ );
9849
+ case "delMany":
9850
+ return (cacheKeys) => target.delMany(keys(cacheKeys));
9851
+ case "delPattern":
9852
+ return (pattern) => target.delPattern(key(pattern));
9853
+ case "keys":
9854
+ return async (pattern) => {
9855
+ const result = await target.keys(pattern ? key(pattern) : void 0);
9856
+ return result.map((cacheKey) => unprefixKey(prefix, cacheKey));
9857
+ };
9858
+ default: {
9859
+ const value = Reflect.get(target, prop, receiver);
9860
+ return typeof value === "function" ? value.bind(target) : value;
9861
+ }
9862
+ }
9863
+ }
9864
+ });
9865
+ }
9866
+ function buildRedisCache(input) {
9867
+ if (!input || !isObjectRecord(input)) return void 0;
9868
+ const prefix = typeof input.prefix === "string" ? input.prefix : void 0;
9869
+ const defaultTtl = toOptionalNumber(input.defaultTtl ?? input.ttl);
9870
+ if (isCacheLike(input)) {
9871
+ return wrapRemoteCache(input, { prefix, defaultTtl });
9872
+ }
9873
+ if (input.enabled === false) return void 0;
9874
+ const embeddedCache = input.cache ?? input.adapter;
9875
+ if (isCacheLike(embeddedCache)) {
9876
+ return wrapRemoteCache(embeddedCache, { prefix, defaultTtl });
9877
+ }
9878
+ const redisTarget = input.url ?? input.uri ?? input.instance ?? input.client ?? input.redis;
9879
+ if (redisTarget === void 0 && input.enabled !== true) {
9880
+ return void 0;
9881
+ }
9882
+ const redisCache = createRedisCacheAdapter(redisTarget);
9883
+ return wrapRemoteCache(redisCache, { prefix, defaultTtl });
9884
+ }
9885
+ function isVextCacheShape(input) {
9886
+ return isObjectRecord(input.memory) || isObjectRecord(input.redis);
9887
+ }
9695
9888
  function normalizeRuntimeCache(cache) {
9696
9889
  if (cache instanceof MemoryCache) return cache;
9697
9890
  if (isCacheLike(cache)) return cache;
9698
9891
  const input = cache ?? {};
9699
- if (input.multiLevel === true || input.local !== void 0 && typeof input.local === "object") {
9700
- const localOpts = input.local ?? {};
9701
- const local = new MemoryCache({
9702
- maxEntries: toOptionalNumber(localOpts.maxEntries ?? localOpts.maxSize),
9703
- maxMemory: toOptionalNumber(localOpts.maxMemory),
9704
- defaultTtl: toOptionalNumber(localOpts.defaultTtl ?? localOpts.ttl),
9705
- enableStats: toOptionalBoolean(localOpts.enableStats),
9706
- enableTags: toOptionalBoolean(localOpts.enableTags),
9707
- cleanupInterval: toOptionalNumber(localOpts.cleanupInterval),
9708
- enabled: toOptionalBoolean(localOpts.enabled)
9892
+ if (isVextCacheShape(input)) {
9893
+ const memoryInput = isObjectRecord(input.memory) ? input.memory : {};
9894
+ const local = buildMemoryCache(
9895
+ input.memory,
9896
+ {
9897
+ ...input,
9898
+ enabled: memoryInput.enabled === false ? false : input.enabled
9899
+ }
9900
+ );
9901
+ const remote = buildRedisCache(input.redis);
9902
+ const policy = input.policy ?? {};
9903
+ return new MultiLevelCache({
9904
+ local,
9905
+ remote,
9906
+ writePolicy: policy.writePolicy ?? "both",
9907
+ backfillOnRemoteHit: policy.backfillLocalOnRemoteHit ?? true,
9908
+ remoteTimeoutMs: isObjectRecord(input.redis) ? toOptionalNumber(input.redis.timeoutMs) : void 0,
9909
+ publish: input.publish
9709
9910
  });
9911
+ }
9912
+ if (input.multiLevel === true || input.local !== void 0 && typeof input.local === "object") {
9913
+ const local = buildMemoryCache(input.local);
9710
9914
  const remoteInput = input.remote;
9711
- const remote = isCacheLike(remoteInput) ? remoteInput : remoteInput ? new MemoryCache({
9712
- maxEntries: toOptionalNumber(remoteInput.maxEntries ?? remoteInput.maxSize),
9713
- maxMemory: toOptionalNumber(remoteInput.maxMemory),
9714
- defaultTtl: toOptionalNumber(remoteInput.defaultTtl ?? remoteInput.ttl),
9715
- enableStats: toOptionalBoolean(remoteInput.enableStats),
9716
- enableTags: toOptionalBoolean(remoteInput.enableTags),
9717
- cleanupInterval: toOptionalNumber(remoteInput.cleanupInterval),
9718
- enabled: toOptionalBoolean(remoteInput.enabled)
9719
- }) : void 0;
9915
+ const remote = isCacheLike(remoteInput) ? remoteInput : remoteInput ? buildMemoryCache(remoteInput) : void 0;
9720
9916
  const policy = input.policy ?? {};
9721
9917
  return new MultiLevelCache({
9722
9918
  local,
@@ -9727,15 +9923,7 @@ function normalizeRuntimeCache(cache) {
9727
9923
  publish: input.publish
9728
9924
  });
9729
9925
  }
9730
- return new MemoryCache({
9731
- maxEntries: toOptionalNumber(input.maxEntries ?? input.maxSize),
9732
- maxMemory: toOptionalNumber(input.maxMemory),
9733
- defaultTtl: toOptionalNumber(input.defaultTtl ?? input.ttl),
9734
- enableStats: toOptionalBoolean(input.enableStats),
9735
- enableTags: toOptionalBoolean(input.enableTags),
9736
- cleanupInterval: toOptionalNumber(input.cleanupInterval),
9737
- enabled: toOptionalBoolean(input.enabled)
9738
- });
9926
+ return buildMemoryCache(input);
9739
9927
  }
9740
9928
 
9741
9929
  // src/capabilities/function-cache/index.ts