monsqlize 2.0.0 → 2.0.2

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.
@@ -688,6 +688,13 @@ function serializeDocument(document) {
688
688
  }
689
689
 
690
690
  // src/capabilities/model/model-instance-helpers.ts
691
+ function resolveRegisteredCollectionName(registered, fallback) {
692
+ if (!registered) {
693
+ return fallback;
694
+ }
695
+ const definition = registered.definition;
696
+ return definition.collection ?? definition.name ?? registered.collectionName;
697
+ }
691
698
  async function populateModelPath(context, docs, path2) {
692
699
  const config = normalizePopulateConfig(path2);
693
700
  if (docs.length === 0) {
@@ -711,7 +718,8 @@ async function populateModelPath(context, docs, path2) {
711
718
  database: registered?.definition.connection?.database ?? context.dbName,
712
719
  pool: registered?.definition.connection?.pool ?? context.poolName
713
720
  };
714
- const relatedCollection = context.runtime.scopedCollection(relation.from, scope);
721
+ const relatedCollectionName = resolveRegisteredCollectionName(registered, relation.from);
722
+ const relatedCollection = context.runtime.scopedCollection(relatedCollectionName, scope);
715
723
  const relatedModel = Model.has(relation.from) ? context.runtime.scopedModel(relation.from, scope) : null;
716
724
  const relatedDocs = await relatedCollection.find({
717
725
  [relation.foreignField]: { $in: keys },
@@ -954,9 +962,85 @@ function forceDeleteManyDocuments(context, filter, options) {
954
962
  }
955
963
 
956
964
  // src/capabilities/model/model-instance-config.ts
965
+ var runtimeModelIndexTasks = /* @__PURE__ */ new WeakMap();
966
+ var fallbackModelIndexTasks = /* @__PURE__ */ new Map();
957
967
  function toCompatDefinition(definition) {
958
968
  return definition;
959
969
  }
970
+ function stableIndexStringify(value) {
971
+ if (value instanceof Date) {
972
+ return JSON.stringify(value.toISOString());
973
+ }
974
+ if (Array.isArray(value)) {
975
+ return `[${value.map((item) => stableIndexStringify(item)).join(",")}]`;
976
+ }
977
+ if (value && typeof value === "object") {
978
+ const entries = Object.entries(value).filter(([, current]) => current !== void 0).sort(([left], [right]) => left.localeCompare(right));
979
+ return `{${entries.map(([key, current]) => `${JSON.stringify(key)}:${stableIndexStringify(current)}`).join(",")}}`;
980
+ }
981
+ return JSON.stringify(value) ?? "undefined";
982
+ }
983
+ function getIndexTaskRegistry(runtime) {
984
+ if (!runtime) {
985
+ return fallbackModelIndexTasks;
986
+ }
987
+ let registry = runtimeModelIndexTasks.get(runtime);
988
+ if (!registry) {
989
+ registry = /* @__PURE__ */ new Map();
990
+ runtimeModelIndexTasks.set(runtime, registry);
991
+ }
992
+ return registry;
993
+ }
994
+ function resolveIndexTaskScope(collection, options) {
995
+ try {
996
+ const namespace = collection.getNamespace();
997
+ return {
998
+ dbName: options?.dbName ?? namespace.db,
999
+ poolName: options?.poolName ?? "default",
1000
+ collectionName: options?.collectionName ?? namespace.collection
1001
+ };
1002
+ } catch {
1003
+ return {
1004
+ dbName: options?.dbName ?? "default",
1005
+ poolName: options?.poolName ?? "default",
1006
+ collectionName: options?.collectionName ?? "unknown"
1007
+ };
1008
+ }
1009
+ }
1010
+ function warnIndexFailure(runtime, taskKey, error) {
1011
+ const logger = runtime;
1012
+ logger?.logger?.warn?.("[MonSQLize] model index creation failed", {
1013
+ taskKey,
1014
+ error: error instanceof Error ? error.message : String(error)
1015
+ });
1016
+ }
1017
+ function scheduleIndexTask(collection, key, indexOptions, options) {
1018
+ const scope = resolveIndexTaskScope(collection, options);
1019
+ const indexFingerprint = stableIndexStringify({ key, options: indexOptions });
1020
+ const taskKey = `${scope.poolName}:${scope.dbName}:${scope.collectionName}:${indexFingerprint}`;
1021
+ const registry = getIndexTaskRegistry(options?.runtime);
1022
+ const existing = registry.get(taskKey);
1023
+ if (existing && existing.status !== "failed") {
1024
+ return;
1025
+ }
1026
+ const task = {
1027
+ status: "pending",
1028
+ promise: new Promise((resolve) => {
1029
+ setImmediate(() => {
1030
+ Promise.resolve().then(() => collection.createIndex(key, indexOptions)).then(() => {
1031
+ task.status = "fulfilled";
1032
+ resolve();
1033
+ }).catch((error) => {
1034
+ task.status = "failed";
1035
+ task.error = error;
1036
+ warnIndexFailure(options?.runtime, taskKey, error);
1037
+ resolve();
1038
+ });
1039
+ });
1040
+ })
1041
+ };
1042
+ registry.set(taskKey, task);
1043
+ }
960
1044
  function getModelEnums(definition) {
961
1045
  return toCompatDefinition(definition).enums ?? {};
962
1046
  }
@@ -1089,31 +1173,27 @@ function initializeModelV1Methods(target, definition) {
1089
1173
  return {};
1090
1174
  }
1091
1175
  }
1092
- function scheduleModelIndexes(collection, definition, softDeleteConfig) {
1176
+ function scheduleModelIndexes(collection, definition, softDeleteConfig, options) {
1093
1177
  if (softDeleteConfig?.enabled && softDeleteConfig.type === "timestamp" && softDeleteConfig.ttl) {
1094
1178
  const softDeleteIndex = softDeleteConfig;
1095
- setImmediate(() => {
1096
- collection.createIndex(
1097
- { [softDeleteIndex.field]: 1 },
1098
- { expireAfterSeconds: softDeleteIndex.ttl }
1099
- ).catch(() => {
1100
- });
1101
- });
1179
+ scheduleIndexTask(
1180
+ collection,
1181
+ { [softDeleteIndex.field]: 1 },
1182
+ { expireAfterSeconds: softDeleteIndex.ttl },
1183
+ options
1184
+ );
1102
1185
  }
1103
1186
  const indexes = toCompatDefinition(definition).indexes;
1104
1187
  if (!Array.isArray(indexes) || indexes.length === 0) {
1105
1188
  return;
1106
1189
  }
1107
- setImmediate(() => {
1108
- for (const indexSpec of indexes) {
1109
- if (!indexSpec?.key) {
1110
- continue;
1111
- }
1112
- const { key, ...indexOptions } = indexSpec;
1113
- collection.createIndex(key, indexOptions).catch(() => {
1114
- });
1190
+ for (const indexSpec of indexes) {
1191
+ if (!indexSpec?.key) {
1192
+ continue;
1115
1193
  }
1116
- });
1194
+ const { key, ...indexOptions } = indexSpec;
1195
+ scheduleIndexTask(collection, key, indexOptions, options);
1196
+ }
1117
1197
  }
1118
1198
 
1119
1199
  // src/capabilities/model/model-write-helpers.ts
@@ -1676,7 +1756,12 @@ var ModelInstance = class {
1676
1756
  this.softDeleteConfig = this._softDeleteConfig;
1677
1757
  this._versionConfig = resolveModelVersionConfig(options.definition);
1678
1758
  this._v1HooksFactory = resolveModelHooksFactory(options.definition);
1679
- scheduleModelIndexes(this.collection, options.definition, this._softDeleteConfig);
1759
+ scheduleModelIndexes(this.collection, options.definition, this._softDeleteConfig, {
1760
+ runtime: this.runtime,
1761
+ dbName: options.dbName,
1762
+ poolName: options.poolName,
1763
+ collectionName: options.collectionName
1764
+ });
1680
1765
  this._v1InstanceMethods = initializeModelV1Methods(this, options.definition);
1681
1766
  }
1682
1767
  /** v1 compat: expose relations map as _relations */
@@ -3096,10 +3181,19 @@ var HealthChecker = class {
3096
3181
  };
3097
3182
 
3098
3183
  // src/capabilities/pool/pool-selector.ts
3184
+ var POOL_STRATEGY_ALIASES = {
3185
+ "round-robin": "roundRobin",
3186
+ "least-connections": "leastConnections",
3187
+ random: "weighted"
3188
+ };
3189
+ function normalizePoolStrategy(strategy) {
3190
+ const value = strategy ?? "auto";
3191
+ return POOL_STRATEGY_ALIASES[value] ?? value;
3192
+ }
3099
3193
  var PoolSelector = class {
3100
3194
  constructor(options = {}) {
3101
3195
  this._roundRobinIndex = /* @__PURE__ */ new Map();
3102
- this._strategy = options.strategy ?? "auto";
3196
+ this._strategy = normalizePoolStrategy(options.strategy);
3103
3197
  this._logger = options.logger ?? console;
3104
3198
  }
3105
3199
  select(pools, context) {
@@ -3212,8 +3306,8 @@ var PoolSelector = class {
3212
3306
  return pools[0].name;
3213
3307
  }
3214
3308
  setStrategy(strategy) {
3215
- this._strategy = strategy;
3216
- this._logger.info?.(`[PoolSelector] Strategy changed: ${strategy}`);
3309
+ this._strategy = normalizePoolStrategy(strategy);
3310
+ this._logger.info?.(`[PoolSelector] Strategy changed: ${this._strategy}`);
3217
3311
  }
3218
3312
  getStrategy() {
3219
3313
  return this._strategy;
@@ -7407,6 +7501,92 @@ function createRuntimeAccessors(config) {
7407
7501
  };
7408
7502
  }
7409
7503
 
7504
+ // src/entry/runtime-compat-accessors.ts
7505
+ function asRuntimeCompatRecord(value) {
7506
+ return value;
7507
+ }
7508
+ function requireCompatDbInstance(value) {
7509
+ const dbInstance = asRuntimeCompatRecord(value).dbInstance;
7510
+ if (!dbInstance) {
7511
+ throw createError(ErrorCodes.NOT_CONNECTED, "Database is not connected. Call connect() first.");
7512
+ }
7513
+ return dbInstance;
7514
+ }
7515
+ function requireCompatPoolManagerRecord(value) {
7516
+ const poolManager = asRuntimeCompatRecord(value)._poolManager;
7517
+ if (!poolManager) {
7518
+ throw createError(ErrorCodes.NO_POOL_MANAGER, "No pool manager configured. Add pools to MonSQLize constructor options.");
7519
+ }
7520
+ return poolManager;
7521
+ }
7522
+ function resolvePoolClientFromRecord(poolManager, poolName) {
7523
+ const getPoolV1 = poolManager["_getPool"];
7524
+ const getPoolV2 = poolManager["getPool"];
7525
+ if (typeof getPoolV1 === "function") {
7526
+ return getPoolV1.call(poolManager, poolName) ?? null;
7527
+ }
7528
+ if (typeof getPoolV2 === "function") {
7529
+ try {
7530
+ return getPoolV2.call(poolManager, poolName) ?? null;
7531
+ } catch {
7532
+ return null;
7533
+ }
7534
+ }
7535
+ return null;
7536
+ }
7537
+ function assertCompatPoolExists(poolManager, poolName) {
7538
+ if (resolvePoolClientFromRecord(poolManager, poolName)) {
7539
+ return;
7540
+ }
7541
+ const getNames = poolManager["getPoolNames"];
7542
+ const available = typeof getNames === "function" ? getNames.call(poolManager) : [];
7543
+ const error = createError(ErrorCodes.POOL_NOT_FOUND, `Pool '${poolName}' not found. Available pools: [${available.join(", ")}]`);
7544
+ error["available"] = available;
7545
+ throw error;
7546
+ }
7547
+ function createPoolScope(runtime, poolName) {
7548
+ return {
7549
+ collection: (name) => runtime.scopedCollection(name, { pool: poolName }),
7550
+ model: (name) => runtime.scopedModel(name, { pool: poolName }),
7551
+ use: (dbName) => ({
7552
+ collection: (name) => runtime.scopedCollection(name, { pool: poolName, database: dbName }),
7553
+ model: (name) => runtime.scopedModel(name, { pool: poolName, database: dbName })
7554
+ })
7555
+ };
7556
+ }
7557
+ function getRegisteredModelMetadata(registered) {
7558
+ const definition = registered.definition;
7559
+ return {
7560
+ actualCollectionName: definition.collection ?? definition.name ?? registered.collectionName,
7561
+ connection: definition.connection
7562
+ };
7563
+ }
7564
+ function getRuntimeDatabaseName(value) {
7565
+ return asRuntimeCompatRecord(value).databaseName ?? "default";
7566
+ }
7567
+ function getCompatModelInstanceCache(value) {
7568
+ const record = asRuntimeCompatRecord(value);
7569
+ if (!record._modelInstances) {
7570
+ record._modelInstances = new MemoryCache({
7571
+ maxEntries: 1e5,
7572
+ enableStats: false
7573
+ });
7574
+ }
7575
+ return record._modelInstances;
7576
+ }
7577
+ function createCompatModelInstance(config) {
7578
+ return new ModelInstance(
7579
+ config.collection,
7580
+ config.runtime,
7581
+ {
7582
+ collectionName: config.collectionName,
7583
+ dbName: config.dbName,
7584
+ poolName: config.poolName,
7585
+ definition: config.definition
7586
+ }
7587
+ );
7588
+ }
7589
+
7410
7590
  // src/entry/runtime-model.ts
7411
7591
  function createRuntimeModelHost(config) {
7412
7592
  return {
@@ -7421,19 +7601,20 @@ function createRuntimeModelInstance(host, name, scope) {
7421
7601
  if (!registered) {
7422
7602
  throw createError(ErrorCodes.MODEL_NOT_DEFINED, `Model '${name}' is not defined.`);
7423
7603
  }
7424
- const databaseName = registered.definition.connection?.database ?? scope.database ?? resolveDatabaseName(host.options);
7425
- const poolName = registered.definition.connection?.pool ?? scope.pool;
7426
- const cacheKey = `${poolName ?? "default"}:${databaseName}:${registered.collectionName}`;
7604
+ const { actualCollectionName, connection } = getRegisteredModelMetadata(registered);
7605
+ const databaseName = connection?.database ?? scope.database ?? resolveDatabaseName(host.options);
7606
+ const poolName = connection?.pool ?? scope.pool;
7607
+ const cacheKey = `${poolName ?? "default"}:${databaseName}:${registered.collectionName}:${actualCollectionName}`;
7427
7608
  const revision = Model.getRevision(registered.collectionName);
7428
7609
  const cached = host._modelInstances.get(cacheKey);
7429
7610
  if (cached && cached.revision === revision) {
7430
7611
  return cached.instance;
7431
7612
  }
7432
7613
  const instance = new ModelInstance(
7433
- host.scopedCollection(registered.collectionName, { database: databaseName }),
7614
+ host.scopedCollection(actualCollectionName, { database: databaseName, pool: poolName }),
7434
7615
  host.runtime,
7435
7616
  {
7436
- collectionName: registered.collectionName,
7617
+ collectionName: actualCollectionName,
7437
7618
  dbName: databaseName,
7438
7619
  poolName,
7439
7620
  definition: registered.definition
@@ -7516,92 +7697,6 @@ function createRuntimeCoreAccessors(runtime) {
7516
7697
  });
7517
7698
  }
7518
7699
 
7519
- // src/entry/runtime-compat-accessors.ts
7520
- function asRuntimeCompatRecord(value) {
7521
- return value;
7522
- }
7523
- function requireCompatDbInstance(value) {
7524
- const dbInstance = asRuntimeCompatRecord(value).dbInstance;
7525
- if (!dbInstance) {
7526
- throw createError(ErrorCodes.NOT_CONNECTED, "Database is not connected. Call connect() first.");
7527
- }
7528
- return dbInstance;
7529
- }
7530
- function requireCompatPoolManagerRecord(value) {
7531
- const poolManager = asRuntimeCompatRecord(value)._poolManager;
7532
- if (!poolManager) {
7533
- throw createError(ErrorCodes.NO_POOL_MANAGER, "No pool manager configured. Add pools to MonSQLize constructor options.");
7534
- }
7535
- return poolManager;
7536
- }
7537
- function resolvePoolClientFromRecord(poolManager, poolName) {
7538
- const getPoolV1 = poolManager["_getPool"];
7539
- const getPoolV2 = poolManager["getPool"];
7540
- if (typeof getPoolV1 === "function") {
7541
- return getPoolV1.call(poolManager, poolName) ?? null;
7542
- }
7543
- if (typeof getPoolV2 === "function") {
7544
- try {
7545
- return getPoolV2.call(poolManager, poolName) ?? null;
7546
- } catch {
7547
- return null;
7548
- }
7549
- }
7550
- return null;
7551
- }
7552
- function assertCompatPoolExists(poolManager, poolName) {
7553
- if (resolvePoolClientFromRecord(poolManager, poolName)) {
7554
- return;
7555
- }
7556
- const getNames = poolManager["getPoolNames"];
7557
- const available = typeof getNames === "function" ? getNames.call(poolManager) : [];
7558
- const error = createError(ErrorCodes.POOL_NOT_FOUND, `Pool '${poolName}' not found. Available pools: [${available.join(", ")}]`);
7559
- error["available"] = available;
7560
- throw error;
7561
- }
7562
- function createPoolScope(runtime, poolName) {
7563
- return {
7564
- collection: (name) => runtime.scopedCollection(name, { pool: poolName }),
7565
- model: (name) => runtime.scopedModel(name, { pool: poolName }),
7566
- use: (dbName) => ({
7567
- collection: (name) => runtime.scopedCollection(name, { pool: poolName, database: dbName }),
7568
- model: (name) => runtime.scopedModel(name, { pool: poolName, database: dbName })
7569
- })
7570
- };
7571
- }
7572
- function getRegisteredModelMetadata(registered) {
7573
- const definition = registered.definition;
7574
- return {
7575
- actualCollectionName: definition.collection ?? definition.name ?? registered.collectionName,
7576
- connection: definition.connection
7577
- };
7578
- }
7579
- function getRuntimeDatabaseName(value) {
7580
- return asRuntimeCompatRecord(value).databaseName ?? "default";
7581
- }
7582
- function getCompatModelInstanceCache(value) {
7583
- const record = asRuntimeCompatRecord(value);
7584
- if (!record._modelInstances) {
7585
- record._modelInstances = new MemoryCache({
7586
- maxEntries: 1e5,
7587
- enableStats: false
7588
- });
7589
- }
7590
- return record._modelInstances;
7591
- }
7592
- function createCompatModelInstance(config) {
7593
- return new ModelInstance(
7594
- config.collection,
7595
- config.runtime,
7596
- {
7597
- collectionName: config.collectionName,
7598
- dbName: config.dbName,
7599
- poolName: config.poolName,
7600
- definition: config.definition
7601
- }
7602
- );
7603
- }
7604
-
7605
7700
  // src/entry/runtime-admin-bridge.ts
7606
7701
  import { performance } from "node:perf_hooks";
7607
7702
  function createLegacyCollectionBridge(config) {
@@ -9675,31 +9770,132 @@ function toOptionalNumber(value) {
9675
9770
  function toOptionalBoolean(value) {
9676
9771
  return typeof value === "boolean" ? value : void 0;
9677
9772
  }
9773
+ function isObjectRecord(value) {
9774
+ return !!value && typeof value === "object" && !Array.isArray(value);
9775
+ }
9776
+ function buildMemoryCache(input, fallback = {}) {
9777
+ if (input instanceof MemoryCache) return input;
9778
+ if (isCacheLike(input)) return input;
9779
+ const opts = isObjectRecord(input) ? input : {};
9780
+ return new MemoryCache({
9781
+ maxEntries: toOptionalNumber(opts.maxEntries ?? opts.maxSize ?? fallback.maxEntries ?? fallback.maxSize),
9782
+ maxMemory: toOptionalNumber(opts.maxMemory ?? fallback.maxMemory),
9783
+ defaultTtl: toOptionalNumber(opts.defaultTtl ?? opts.ttl ?? fallback.defaultTtl ?? fallback.ttl),
9784
+ enableStats: toOptionalBoolean(opts.enableStats ?? fallback.enableStats),
9785
+ enableTags: toOptionalBoolean(opts.enableTags ?? fallback.enableTags),
9786
+ cleanupInterval: toOptionalNumber(opts.cleanupInterval ?? fallback.cleanupInterval),
9787
+ enabled: toOptionalBoolean(opts.enabled ?? fallback.enabled)
9788
+ });
9789
+ }
9790
+ function prefixKey(prefix, key) {
9791
+ if (!prefix || key.startsWith(prefix)) return key;
9792
+ return `${prefix}${key}`;
9793
+ }
9794
+ function unprefixKey(prefix, key) {
9795
+ if (!prefix || !key.startsWith(prefix)) return key;
9796
+ return key.slice(prefix.length);
9797
+ }
9798
+ function wrapRemoteCache(cache, options = {}) {
9799
+ const prefix = typeof options.prefix === "string" && options.prefix.length > 0 ? options.prefix : void 0;
9800
+ const defaultTtl = options.defaultTtl;
9801
+ if (!prefix && defaultTtl === void 0) return cache;
9802
+ const key = (value) => prefixKey(prefix, value);
9803
+ const keys = (values) => values.map(key);
9804
+ const resolveTtl = (ttl) => ttl ?? defaultTtl;
9805
+ return new Proxy(cache, {
9806
+ get(target, prop, receiver) {
9807
+ switch (prop) {
9808
+ case "get":
9809
+ return (cacheKey) => target.get(key(cacheKey));
9810
+ case "set":
9811
+ return (cacheKey, value, ttl) => target.set(key(cacheKey), value, resolveTtl(ttl));
9812
+ case "del":
9813
+ return (cacheKey) => target.del(key(cacheKey));
9814
+ case "exists":
9815
+ return (cacheKey) => target.exists(key(cacheKey));
9816
+ case "has":
9817
+ return (cacheKey) => target.has(key(cacheKey));
9818
+ case "getMany":
9819
+ return async (cacheKeys) => {
9820
+ const result = await target.getMany(keys(cacheKeys));
9821
+ return Object.fromEntries(
9822
+ Object.entries(result).map(([cacheKey, value]) => [unprefixKey(prefix, cacheKey), value])
9823
+ );
9824
+ };
9825
+ case "setMany":
9826
+ return (entries, ttl) => target.setMany(
9827
+ Object.fromEntries(
9828
+ Object.entries(entries).map(([cacheKey, value]) => [key(cacheKey), value])
9829
+ ),
9830
+ resolveTtl(ttl)
9831
+ );
9832
+ case "delMany":
9833
+ return (cacheKeys) => target.delMany(keys(cacheKeys));
9834
+ case "delPattern":
9835
+ return (pattern) => target.delPattern(key(pattern));
9836
+ case "keys":
9837
+ return async (pattern) => {
9838
+ const result = await target.keys(pattern ? key(pattern) : void 0);
9839
+ return result.map((cacheKey) => unprefixKey(prefix, cacheKey));
9840
+ };
9841
+ default: {
9842
+ const value = Reflect.get(target, prop, receiver);
9843
+ return typeof value === "function" ? value.bind(target) : value;
9844
+ }
9845
+ }
9846
+ }
9847
+ });
9848
+ }
9849
+ function buildRedisCache(input) {
9850
+ if (!input || !isObjectRecord(input)) return void 0;
9851
+ const prefix = typeof input.prefix === "string" ? input.prefix : void 0;
9852
+ const defaultTtl = toOptionalNumber(input.defaultTtl ?? input.ttl);
9853
+ if (isCacheLike(input)) {
9854
+ return wrapRemoteCache(input, { prefix, defaultTtl });
9855
+ }
9856
+ if (input.enabled === false) return void 0;
9857
+ const embeddedCache = input.cache ?? input.adapter;
9858
+ if (isCacheLike(embeddedCache)) {
9859
+ return wrapRemoteCache(embeddedCache, { prefix, defaultTtl });
9860
+ }
9861
+ const redisTarget = input.url ?? input.uri ?? input.instance ?? input.client ?? input.redis;
9862
+ if (redisTarget === void 0 && input.enabled !== true) {
9863
+ return void 0;
9864
+ }
9865
+ const redisCache = createRedisCacheAdapter(redisTarget);
9866
+ return wrapRemoteCache(redisCache, { prefix, defaultTtl });
9867
+ }
9868
+ function isVextCacheShape(input) {
9869
+ return isObjectRecord(input.memory) || isObjectRecord(input.redis);
9870
+ }
9678
9871
  function normalizeRuntimeCache(cache) {
9679
9872
  if (cache instanceof MemoryCache) return cache;
9680
9873
  if (isCacheLike(cache)) return cache;
9681
9874
  const input = cache ?? {};
9682
- if (input.multiLevel === true || input.local !== void 0 && typeof input.local === "object") {
9683
- const localOpts = input.local ?? {};
9684
- const local = new MemoryCache({
9685
- maxEntries: toOptionalNumber(localOpts.maxEntries ?? localOpts.maxSize),
9686
- maxMemory: toOptionalNumber(localOpts.maxMemory),
9687
- defaultTtl: toOptionalNumber(localOpts.defaultTtl ?? localOpts.ttl),
9688
- enableStats: toOptionalBoolean(localOpts.enableStats),
9689
- enableTags: toOptionalBoolean(localOpts.enableTags),
9690
- cleanupInterval: toOptionalNumber(localOpts.cleanupInterval),
9691
- enabled: toOptionalBoolean(localOpts.enabled)
9875
+ if (isVextCacheShape(input)) {
9876
+ const memoryInput = isObjectRecord(input.memory) ? input.memory : {};
9877
+ const local = buildMemoryCache(
9878
+ input.memory,
9879
+ {
9880
+ ...input,
9881
+ enabled: memoryInput.enabled === false ? false : input.enabled
9882
+ }
9883
+ );
9884
+ const remote = buildRedisCache(input.redis);
9885
+ const policy = input.policy ?? {};
9886
+ return new MultiLevelCache({
9887
+ local,
9888
+ remote,
9889
+ writePolicy: policy.writePolicy ?? "both",
9890
+ backfillOnRemoteHit: policy.backfillLocalOnRemoteHit ?? true,
9891
+ remoteTimeoutMs: isObjectRecord(input.redis) ? toOptionalNumber(input.redis.timeoutMs) : void 0,
9892
+ publish: input.publish
9692
9893
  });
9894
+ }
9895
+ if (input.multiLevel === true || input.local !== void 0 && typeof input.local === "object") {
9896
+ const local = buildMemoryCache(input.local);
9693
9897
  const remoteInput = input.remote;
9694
- const remote = isCacheLike(remoteInput) ? remoteInput : remoteInput ? new MemoryCache({
9695
- maxEntries: toOptionalNumber(remoteInput.maxEntries ?? remoteInput.maxSize),
9696
- maxMemory: toOptionalNumber(remoteInput.maxMemory),
9697
- defaultTtl: toOptionalNumber(remoteInput.defaultTtl ?? remoteInput.ttl),
9698
- enableStats: toOptionalBoolean(remoteInput.enableStats),
9699
- enableTags: toOptionalBoolean(remoteInput.enableTags),
9700
- cleanupInterval: toOptionalNumber(remoteInput.cleanupInterval),
9701
- enabled: toOptionalBoolean(remoteInput.enabled)
9702
- }) : void 0;
9898
+ const remote = isCacheLike(remoteInput) ? remoteInput : remoteInput ? buildMemoryCache(remoteInput) : void 0;
9703
9899
  const policy = input.policy ?? {};
9704
9900
  return new MultiLevelCache({
9705
9901
  local,
@@ -9710,15 +9906,7 @@ function normalizeRuntimeCache(cache) {
9710
9906
  publish: input.publish
9711
9907
  });
9712
9908
  }
9713
- return new MemoryCache({
9714
- maxEntries: toOptionalNumber(input.maxEntries ?? input.maxSize),
9715
- maxMemory: toOptionalNumber(input.maxMemory),
9716
- defaultTtl: toOptionalNumber(input.defaultTtl ?? input.ttl),
9717
- enableStats: toOptionalBoolean(input.enableStats),
9718
- enableTags: toOptionalBoolean(input.enableTags),
9719
- cleanupInterval: toOptionalNumber(input.cleanupInterval),
9720
- enabled: toOptionalBoolean(input.enabled)
9721
- });
9909
+ return buildMemoryCache(input);
9722
9910
  }
9723
9911
 
9724
9912
  // src/capabilities/function-cache/index.ts