@frontmcp/utils 1.8.6 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/env/browser-env.d.ts +1 -0
  2. package/env/browser-env.js +5 -0
  3. package/env/browser-runtime-context.d.ts +2 -1
  4. package/env/browser-runtime-context.js +104 -0
  5. package/env/index.d.ts +1 -1
  6. package/env/node-env.d.ts +11 -0
  7. package/env/node-env.js +8 -2
  8. package/env/runtime-context.d.ts +12 -5
  9. package/env/runtime-context.js +192 -0
  10. package/esm/async-context/browser-async-context.mjs +3 -2
  11. package/esm/async-context/node-async-context.mjs +3 -2
  12. package/esm/crypto/browser.mjs +3 -2
  13. package/esm/crypto/node.mjs +3 -2
  14. package/esm/env/browser-env.mjs +7 -2
  15. package/esm/env/browser-runtime-context.mjs +77 -0
  16. package/esm/env/node-env.mjs +10 -4
  17. package/esm/env/runtime-context.mjs +172 -0
  18. package/esm/event-emitter/browser-event-emitter.mjs +3 -2
  19. package/esm/event-emitter/node-event-emitter.mjs +3 -2
  20. package/esm/index.mjs +251 -250
  21. package/esm/path/browser-path.mjs +50 -0
  22. package/esm/path/node-path.mjs +16 -0
  23. package/index.d.ts +3 -3
  24. package/index.js +261 -274
  25. package/package.json +26 -8
  26. package/path/browser-path.js +79 -0
  27. package/path/node-path.js +45 -0
  28. package/storage/adapters/base.d.ts +6 -1
  29. package/storage/adapters/cloudflare-kv.d.ts +1 -0
  30. package/storage/adapters/index.d.ts +2 -1
  31. package/storage/adapters/memory.d.ts +2 -1
  32. package/storage/adapters/redis.d.ts +19 -0
  33. package/storage/adapters/upstash.d.ts +2 -1
  34. package/storage/adapters/vercel-kv.d.ts +2 -1
  35. package/storage/index.d.ts +4 -2
  36. package/storage/namespace.d.ts +2 -1
  37. package/storage/redis-error-listener.d.ts +27 -0
  38. package/storage/types.d.ts +11 -0
  39. package/storage/utils/compare-and-delete.d.ts +2 -0
package/index.js CHANGED
@@ -596,9 +596,9 @@ function createSecretData(options) {
596
596
  }
597
597
  async function getOrCreateSecret(options) {
598
598
  const cacheKey = resolveSecretPath(options);
599
- const cached4 = secretCache.get(cacheKey);
600
- if (cached4) {
601
- return cached4;
599
+ const cached = secretCache.get(cacheKey);
600
+ if (cached) {
601
+ return cached;
602
602
  }
603
603
  const existing = generationPromises.get(cacheKey);
604
604
  if (existing) {
@@ -836,8 +836,8 @@ var init_key_persistence = __esm({
836
836
  */
837
837
  async get(kid) {
838
838
  if (this.enableCache) {
839
- const cached4 = this.cache.get(kid);
840
- if (cached4) return cached4;
839
+ const cached = this.cache.get(kid);
840
+ if (cached) return cached;
841
841
  }
842
842
  const raw = await this.storage.get(kid);
843
843
  if (!raw) return null;
@@ -1088,6 +1088,75 @@ var init_errors = __esm({
1088
1088
  }
1089
1089
  });
1090
1090
 
1091
+ // libs/utils/src/storage/utils/pattern.ts
1092
+ function globToRegex(pattern) {
1093
+ if (pattern.length > MAX_PATTERN_LENGTH) {
1094
+ throw new StoragePatternError(
1095
+ pattern.substring(0, 50) + "...",
1096
+ `Pattern exceeds maximum length of ${MAX_PATTERN_LENGTH} characters`
1097
+ );
1098
+ }
1099
+ const wildcardCount = (pattern.match(/[*?]/g) || []).length;
1100
+ if (wildcardCount > MAX_WILDCARDS) {
1101
+ throw new StoragePatternError(pattern, `Pattern has too many wildcards (max: ${MAX_WILDCARDS})`);
1102
+ }
1103
+ if (pattern === "" || pattern === "*") {
1104
+ return /^.*$/;
1105
+ }
1106
+ let regexStr = "^";
1107
+ let prevChar = "";
1108
+ for (let i = 0; i < pattern.length; i++) {
1109
+ const char = pattern[i];
1110
+ switch (char) {
1111
+ case "*":
1112
+ if (prevChar !== "*") {
1113
+ regexStr += ".*";
1114
+ }
1115
+ break;
1116
+ case "?":
1117
+ regexStr += ".";
1118
+ break;
1119
+ default:
1120
+ regexStr += char.replace(REGEX_SPECIAL_CHARS, "\\$&");
1121
+ }
1122
+ prevChar = char;
1123
+ }
1124
+ regexStr += "$";
1125
+ try {
1126
+ return new RegExp(regexStr);
1127
+ } catch {
1128
+ throw new StoragePatternError(pattern, "Failed to compile pattern to regex");
1129
+ }
1130
+ }
1131
+ function matchesPattern(key, pattern) {
1132
+ const regex = globToRegex(pattern);
1133
+ return regex.test(key);
1134
+ }
1135
+ function validatePattern(pattern) {
1136
+ try {
1137
+ globToRegex(pattern);
1138
+ return { valid: true };
1139
+ } catch (e) {
1140
+ return {
1141
+ valid: false,
1142
+ error: e instanceof Error ? e.message : "Invalid pattern"
1143
+ };
1144
+ }
1145
+ }
1146
+ function escapeGlob(literal) {
1147
+ return literal.replace(/[*?\\]/g, "\\$&");
1148
+ }
1149
+ var MAX_PATTERN_LENGTH, MAX_WILDCARDS, REGEX_SPECIAL_CHARS;
1150
+ var init_pattern = __esm({
1151
+ "libs/utils/src/storage/utils/pattern.ts"() {
1152
+ "use strict";
1153
+ init_errors();
1154
+ MAX_PATTERN_LENGTH = 500;
1155
+ MAX_WILDCARDS = 20;
1156
+ REGEX_SPECIAL_CHARS = /[.+^${}()|[\]\\]/g;
1157
+ }
1158
+ });
1159
+
1091
1160
  // libs/utils/src/storage/utils/ttl.ts
1092
1161
  function validateTTL(ttlSeconds) {
1093
1162
  if (typeof ttlSeconds !== "number") {
@@ -1202,6 +1271,17 @@ var init_base = __esm({
1202
1271
  const matchedKeys = await this.keys(pattern);
1203
1272
  return matchedKeys.length;
1204
1273
  }
1274
+ /**
1275
+ * Default: not supported, since a read followed by a delete is not atomic.
1276
+ * Override in adapters whose backend can compare and delete in one step.
1277
+ */
1278
+ async deleteIfEquals(_key, _expectedValue) {
1279
+ throw new StorageNotSupportedError(
1280
+ "deleteIfEquals",
1281
+ this.backendName,
1282
+ "Use the memory, Redis, Upstash or Vercel KV adapter for an atomic compare-and-delete."
1283
+ );
1284
+ }
1205
1285
  // ============================================
1206
1286
  // Pub/Sub (default: not supported)
1207
1287
  // ============================================
@@ -1250,85 +1330,16 @@ var init_base = __esm({
1250
1330
  }
1251
1331
  });
1252
1332
 
1253
- // libs/utils/src/storage/utils/pattern.ts
1254
- function globToRegex(pattern) {
1255
- if (pattern.length > MAX_PATTERN_LENGTH) {
1256
- throw new StoragePatternError(
1257
- pattern.substring(0, 50) + "...",
1258
- `Pattern exceeds maximum length of ${MAX_PATTERN_LENGTH} characters`
1259
- );
1260
- }
1261
- const wildcardCount = (pattern.match(/[*?]/g) || []).length;
1262
- if (wildcardCount > MAX_WILDCARDS) {
1263
- throw new StoragePatternError(pattern, `Pattern has too many wildcards (max: ${MAX_WILDCARDS})`);
1264
- }
1265
- if (pattern === "" || pattern === "*") {
1266
- return /^.*$/;
1267
- }
1268
- let regexStr = "^";
1269
- let prevChar = "";
1270
- for (let i = 0; i < pattern.length; i++) {
1271
- const char = pattern[i];
1272
- switch (char) {
1273
- case "*":
1274
- if (prevChar !== "*") {
1275
- regexStr += ".*";
1276
- }
1277
- break;
1278
- case "?":
1279
- regexStr += ".";
1280
- break;
1281
- default:
1282
- regexStr += char.replace(REGEX_SPECIAL_CHARS, "\\$&");
1283
- }
1284
- prevChar = char;
1285
- }
1286
- regexStr += "$";
1287
- try {
1288
- return new RegExp(regexStr);
1289
- } catch {
1290
- throw new StoragePatternError(pattern, "Failed to compile pattern to regex");
1291
- }
1292
- }
1293
- function matchesPattern(key, pattern) {
1294
- const regex = globToRegex(pattern);
1295
- return regex.test(key);
1296
- }
1297
- function validatePattern(pattern) {
1298
- try {
1299
- globToRegex(pattern);
1300
- return { valid: true };
1301
- } catch (e) {
1302
- return {
1303
- valid: false,
1304
- error: e instanceof Error ? e.message : "Invalid pattern"
1305
- };
1306
- }
1307
- }
1308
- function escapeGlob(literal) {
1309
- return literal.replace(/[*?\\]/g, "\\$&");
1310
- }
1311
- var MAX_PATTERN_LENGTH, MAX_WILDCARDS, REGEX_SPECIAL_CHARS;
1312
- var init_pattern = __esm({
1313
- "libs/utils/src/storage/utils/pattern.ts"() {
1314
- "use strict";
1315
- init_errors();
1316
- MAX_PATTERN_LENGTH = 500;
1317
- MAX_WILDCARDS = 20;
1318
- REGEX_SPECIAL_CHARS = /[.+^${}()|[\]\\]/g;
1319
- }
1320
- });
1321
-
1322
1333
  // libs/utils/src/storage/adapters/memory.ts
1323
1334
  var import_event_emitter, DEFAULT_SWEEP_INTERVAL_SECONDS, MAX_TIMEOUT_MS, MemoryStorageAdapter;
1324
1335
  var init_memory = __esm({
1325
1336
  "libs/utils/src/storage/adapters/memory.ts"() {
1326
1337
  "use strict";
1327
1338
  import_event_emitter = require("#event-emitter");
1328
- init_base();
1329
1339
  init_errors();
1330
1340
  init_pattern();
1331
1341
  init_ttl();
1342
+ init_base();
1332
1343
  DEFAULT_SWEEP_INTERVAL_SECONDS = 60;
1333
1344
  MAX_TIMEOUT_MS = 2147483647;
1334
1345
  MemoryStorageAdapter = class extends BaseStorageAdapter {
@@ -1420,6 +1431,12 @@ var init_memory = __esm({
1420
1431
  this.ensureConnected();
1421
1432
  return this.deleteEntry(key);
1422
1433
  }
1434
+ async deleteIfEquals(key, expectedValue) {
1435
+ this.ensureConnected();
1436
+ const entry = this.store.get(key);
1437
+ if (!entry || isExpired(entry.expiresAt) || entry.value !== expectedValue) return false;
1438
+ return this.deleteEntry(key);
1439
+ }
1423
1440
  async exists(key) {
1424
1441
  this.ensureConnected();
1425
1442
  const entry = this.store.get(key);
@@ -2965,6 +2982,41 @@ var init_crypto = __esm({
2965
2982
  }
2966
2983
  });
2967
2984
 
2985
+ // libs/utils/src/storage/redis-error-listener.ts
2986
+ function attachRedisErrorListener(client, options = {}) {
2987
+ const label = options.label ?? "redis";
2988
+ const intervalMs = options.intervalMs ?? DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS;
2989
+ let lastLoggedAt = 0;
2990
+ let suppressed = 0;
2991
+ const listener = (error) => {
2992
+ const now = Date.now();
2993
+ if (lastLoggedAt !== 0 && now - lastLoggedAt < intervalMs) {
2994
+ suppressed++;
2995
+ return;
2996
+ }
2997
+ const extra = suppressed > 0 ? ` (${suppressed} similar error(s) suppressed)` : "";
2998
+ suppressed = 0;
2999
+ lastLoggedAt = now;
3000
+ const message = `[${label}] Redis connection error: ${error?.message ?? String(error)}${extra}`;
3001
+ if (options.logger) {
3002
+ options.logger.warn(message);
3003
+ } else {
3004
+ console.warn(message);
3005
+ }
3006
+ };
3007
+ client.on("error", listener);
3008
+ return () => {
3009
+ client.removeListener("error", listener);
3010
+ };
3011
+ }
3012
+ var DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS;
3013
+ var init_redis_error_listener = __esm({
3014
+ "libs/utils/src/storage/redis-error-listener.ts"() {
3015
+ "use strict";
3016
+ DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS = 3e4;
3017
+ }
3018
+ });
3019
+
2968
3020
  // libs/utils/src/storage/utils/index.ts
2969
3021
  var init_utils = __esm({
2970
3022
  "libs/utils/src/storage/utils/index.ts"() {
@@ -2973,10 +3025,20 @@ var init_utils = __esm({
2973
3025
  }
2974
3026
  });
2975
3027
 
3028
+ // libs/utils/src/storage/utils/compare-and-delete.ts
3029
+ var COMPARE_AND_DELETE_SCRIPT;
3030
+ var init_compare_and_delete = __esm({
3031
+ "libs/utils/src/storage/utils/compare-and-delete.ts"() {
3032
+ "use strict";
3033
+ COMPARE_AND_DELETE_SCRIPT = `if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0`;
3034
+ }
3035
+ });
3036
+
2976
3037
  // libs/utils/src/storage/adapters/redis.ts
2977
3038
  var redis_exports = {};
2978
3039
  __export(redis_exports, {
2979
- RedisStorageAdapter: () => RedisStorageAdapter
3040
+ RedisStorageAdapter: () => RedisStorageAdapter,
3041
+ createRedisClient: () => createRedisClient
2980
3042
  });
2981
3043
  function getRedisClass() {
2982
3044
  try {
@@ -2994,12 +3056,28 @@ function getRedisClass() {
2994
3056
  });
2995
3057
  }
2996
3058
  }
3059
+ function createRedisClient(options) {
3060
+ const RedisClass = getRedisClass();
3061
+ const baseOptions = { lazyConnect: false, maxRetriesPerRequest: 3 };
3062
+ const client = options.url ? new RedisClass(options.url, baseOptions) : new RedisClass({
3063
+ ...baseOptions,
3064
+ host: options.host ?? "localhost",
3065
+ port: options.port ?? 6379,
3066
+ password: options.password,
3067
+ db: options.db ?? 0,
3068
+ tls: options.tls ? {} : void 0
3069
+ });
3070
+ attachRedisErrorListener(client, { label: options.label ?? "redis", logger: options.logger });
3071
+ return client;
3072
+ }
2997
3073
  var RedisStorageAdapter;
2998
3074
  var init_redis = __esm({
2999
3075
  "libs/utils/src/storage/adapters/redis.ts"() {
3000
3076
  "use strict";
3001
3077
  init_errors();
3078
+ init_redis_error_listener();
3002
3079
  init_utils();
3080
+ init_compare_and_delete();
3003
3081
  init_base();
3004
3082
  RedisStorageAdapter = class extends BaseStorageAdapter {
3005
3083
  backendName = "redis";
@@ -3007,6 +3085,7 @@ var init_redis = __esm({
3007
3085
  subscriber;
3008
3086
  options;
3009
3087
  ownsClient;
3088
+ detachErrorListener;
3010
3089
  connecting;
3011
3090
  keyPrefix;
3012
3091
  subscriptionHandlers = /* @__PURE__ */ new Map();
@@ -3063,7 +3142,10 @@ var init_redis = __esm({
3063
3142
  await client.ping();
3064
3143
  this.client = client;
3065
3144
  this.connected = true;
3066
- if (this.ownsClient) client.removeListener("error", recordSocketError);
3145
+ if (this.ownsClient) {
3146
+ client.removeListener("error", recordSocketError);
3147
+ this.detachErrorListener = attachRedisErrorListener(client, { label: "RedisStorage" });
3148
+ }
3067
3149
  } catch (e) {
3068
3150
  if (this.ownsClient && client) {
3069
3151
  try {
@@ -3084,6 +3166,8 @@ var init_redis = __esm({
3084
3166
  if (this.ownsClient && this.client) {
3085
3167
  await this.client.quit();
3086
3168
  }
3169
+ this.detachErrorListener?.();
3170
+ this.detachErrorListener = void 0;
3087
3171
  this.client = void 0;
3088
3172
  this.connected = false;
3089
3173
  this.subscriptionHandlers.clear();
@@ -3148,6 +3232,15 @@ var init_redis = __esm({
3148
3232
  const result = await this.getConnectedClient().del(this.prefixKey(key));
3149
3233
  return result > 0;
3150
3234
  }
3235
+ async deleteIfEquals(key, expectedValue) {
3236
+ const deleted = await this.getConnectedClient().eval(
3237
+ COMPARE_AND_DELETE_SCRIPT,
3238
+ 1,
3239
+ this.prefixKey(key),
3240
+ expectedValue
3241
+ );
3242
+ return deleted === 1;
3243
+ }
3151
3244
  async exists(key) {
3152
3245
  const result = await this.getConnectedClient().exists(this.prefixKey(key));
3153
3246
  return result > 0;
@@ -3311,6 +3404,7 @@ var init_redis = __esm({
3311
3404
  } else {
3312
3405
  throw new StorageConfigError("redis", "Cannot create subscriber without url, config, or client");
3313
3406
  }
3407
+ attachRedisErrorListener(subscriber, { label: "RedisStorage:subscriber" });
3314
3408
  subscriber.on("message", (channel, message) => {
3315
3409
  const handlers = this.subscriptionHandlers.get(channel);
3316
3410
  if (handlers) {
@@ -3355,9 +3449,12 @@ var vercel_kv_exports = {};
3355
3449
  __export(vercel_kv_exports, {
3356
3450
  VercelKvStorageAdapter: () => VercelKvStorageAdapter
3357
3451
  });
3358
- function getVercelKv() {
3452
+ async function loadVercelKv() {
3359
3453
  try {
3360
- return require("@vercel/kv");
3454
+ const mod = await import("@vercel/kv");
3455
+ const resolved = typeof mod.createClient === "function" ? mod : mod.default;
3456
+ if (!resolved || typeof resolved.createClient !== "function") throw new Error("createClient missing");
3457
+ return resolved;
3361
3458
  } catch {
3362
3459
  throw new Error("@vercel/kv is required for Vercel KV storage adapter. Install it with: npm install @vercel/kv");
3363
3460
  }
@@ -3366,9 +3463,10 @@ var VercelKvStorageAdapter;
3366
3463
  var init_vercel_kv = __esm({
3367
3464
  "libs/utils/src/storage/adapters/vercel-kv.ts"() {
3368
3465
  "use strict";
3369
- init_base();
3370
3466
  init_errors();
3467
+ init_compare_and_delete();
3371
3468
  init_ttl();
3469
+ init_base();
3372
3470
  VercelKvStorageAdapter = class extends BaseStorageAdapter {
3373
3471
  backendName = "vercel-kv";
3374
3472
  client;
@@ -3393,17 +3491,13 @@ var init_vercel_kv = __esm({
3393
3491
  async connect() {
3394
3492
  if (this.connected) return;
3395
3493
  try {
3396
- const { createClient, kv } = getVercelKv();
3397
- if (this.options.url === process.env["KV_REST_API_URL"]) {
3398
- this.client = kv;
3399
- } else {
3400
- const url = this.options.url;
3401
- const token = this.options.token;
3402
- if (!url || !token) {
3403
- throw new StorageConfigError("vercel-kv", "URL and token are required");
3404
- }
3405
- this.client = createClient({ url, token });
3494
+ const { createClient } = await loadVercelKv();
3495
+ const url = this.options.url;
3496
+ const token = this.options.token;
3497
+ if (!url || !token) {
3498
+ throw new StorageConfigError("vercel-kv", "URL and token are required");
3406
3499
  }
3500
+ this.client = createClient({ url, token, cache: void 0 });
3407
3501
  await this.client.exists("__healthcheck__");
3408
3502
  this.connected = true;
3409
3503
  } catch (e) {
@@ -3465,6 +3559,14 @@ var init_vercel_kv = __esm({
3465
3559
  const result = await this.getConnectedClient().del(this.prefixKey(key));
3466
3560
  return result > 0;
3467
3561
  }
3562
+ async deleteIfEquals(key, expectedValue) {
3563
+ const deleted = await this.getConnectedClient().eval(
3564
+ COMPARE_AND_DELETE_SCRIPT,
3565
+ [this.prefixKey(key)],
3566
+ [expectedValue]
3567
+ );
3568
+ return deleted === 1;
3569
+ }
3468
3570
  async exists(key) {
3469
3571
  const result = await this.getConnectedClient().exists(this.prefixKey(key));
3470
3572
  return result > 0;
@@ -3582,9 +3684,10 @@ var PUBSUB_POLL_INTERVAL_MS, UpstashStorageAdapter;
3582
3684
  var init_upstash = __esm({
3583
3685
  "libs/utils/src/storage/adapters/upstash.ts"() {
3584
3686
  "use strict";
3585
- init_base();
3586
3687
  init_errors();
3688
+ init_compare_and_delete();
3587
3689
  init_ttl();
3690
+ init_base();
3588
3691
  PUBSUB_POLL_INTERVAL_MS = 100;
3589
3692
  UpstashStorageAdapter = class extends BaseStorageAdapter {
3590
3693
  backendName = "upstash";
@@ -3689,6 +3792,14 @@ var init_upstash = __esm({
3689
3792
  const result = await this.getConnectedClient().del(this.prefixKey(key));
3690
3793
  return result > 0;
3691
3794
  }
3795
+ async deleteIfEquals(key, expectedValue) {
3796
+ const deleted = await this.getConnectedClient().eval(
3797
+ COMPARE_AND_DELETE_SCRIPT,
3798
+ [this.prefixKey(key)],
3799
+ [expectedValue]
3800
+ );
3801
+ return deleted === 1;
3802
+ }
3692
3803
  async exists(key) {
3693
3804
  const result = await this.getConnectedClient().exists(this.prefixKey(key));
3694
3805
  return result > 0;
@@ -3878,7 +3989,9 @@ var init_cloudflare_kv = __esm({
3878
3989
  constructor(options) {
3879
3990
  super();
3880
3991
  if (!options?.namespace) {
3881
- throw new Error("CloudflareKvStorageAdapter: a bound KV `namespace` is required (resolve it from the Worker `env`).");
3992
+ throw new Error(
3993
+ "CloudflareKvStorageAdapter: a bound KV `namespace` is required (resolve it from the Worker `env`)."
3994
+ );
3882
3995
  }
3883
3996
  this.kv = options.namespace;
3884
3997
  this.keyPrefix = options.keyPrefix ?? "";
@@ -3972,6 +4085,13 @@ var init_cloudflare_kv = __esm({
3972
4085
  "Cloudflare KV has no atomic increment (a get+put is racy under eventual consistency). Use a Durable Object for atomic counters."
3973
4086
  );
3974
4087
  }
4088
+ async deleteIfEquals(_key, _expectedValue) {
4089
+ throw new StorageNotSupportedError(
4090
+ "deleteIfEquals",
4091
+ this.backendName,
4092
+ "Cloudflare KV has no compare-and-set; use a Durable Object for an atomic compare-and-delete."
4093
+ );
4094
+ }
3975
4095
  // ── Pub/Sub (unsupported) ────────────────────────────────────────────────
3976
4096
  getPubSubSuggestion() {
3977
4097
  return "Cloudflare KV does not support pub/sub. Use a Durable Object (or Queues) for fan-out.";
@@ -4007,6 +4127,7 @@ __export(index_exports, {
4007
4127
  BaseStorageAdapter: () => BaseStorageAdapter,
4008
4128
  DEFAULT_CODE_VERIFIER_LENGTH: () => DEFAULT_CODE_VERIFIER_LENGTH,
4009
4129
  DEFAULT_MAX_INPUT_LENGTH: () => DEFAULT_MAX_INPUT_LENGTH,
4130
+ DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS: () => DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS,
4010
4131
  EncryptedBlobError: () => EncryptedBlobError,
4011
4132
  EncryptedStorageError: () => EncryptedStorageError,
4012
4133
  EncryptedTypedStorage: () => EncryptedTypedStorage,
@@ -4039,16 +4160,17 @@ __export(index_exports, {
4039
4160
  assertNode: () => assertNode,
4040
4161
  asymmetricAlgSchema: () => asymmetricAlgSchema,
4041
4162
  asymmetricKeyDataSchema: () => asymmetricKeyDataSchema,
4163
+ attachRedisErrorListener: () => attachRedisErrorListener,
4042
4164
  awaitOutsideRequest: () => import_async_context.awaitOutsideRequest,
4043
4165
  base64Decode: () => base64Decode,
4044
4166
  base64Encode: () => base64Encode,
4045
4167
  base64urlDecode: () => base64urlDecode,
4046
4168
  base64urlEncode: () => base64urlEncode,
4047
- basename: () => import_path.basename,
4169
+ basename: () => import_path2.basename,
4048
4170
  buildPrefix: () => buildPrefix,
4049
4171
  buildSetCookie: () => buildSetCookie,
4050
4172
  bytesToHex: () => bytesToHex,
4051
- checkEntryAvailability: () => checkEntryAvailability,
4173
+ checkEntryAvailability: () => import_runtime_context.checkEntryAvailability,
4052
4174
  classifyIpAddress: () => classifyIpAddress,
4053
4175
  clearCachedSecret: () => clearCachedSecret,
4054
4176
  collapseChar: () => collapseChar,
@@ -4059,6 +4181,7 @@ __export(index_exports, {
4059
4181
  createKeyPersistenceWithStorage: () => createKeyPersistenceWithStorage,
4060
4182
  createMemoryStorage: () => createMemoryStorage,
4061
4183
  createNamespacedStorage: () => createNamespacedStorage,
4184
+ createRedisClient: () => createRedisClient,
4062
4185
  createRootStorage: () => createRootStorage,
4063
4186
  createSafeRegExp: () => createSafeRegExp,
4064
4187
  createSecretData: () => createSecretData,
@@ -4068,15 +4191,15 @@ __export(index_exports, {
4068
4191
  deleteSecret: () => deleteSecret,
4069
4192
  deserializeAndDecrypt: () => deserializeAndDecrypt,
4070
4193
  deserializeBlob: () => deserializeBlob,
4071
- detectRuntimeContext: () => detectRuntimeContext,
4072
- dirname: () => import_path.dirname,
4194
+ detectRuntimeContext: () => import_runtime_context.detectRuntimeContext,
4195
+ dirname: () => import_path2.dirname,
4073
4196
  encryptAesGcm: () => encryptAesGcm,
4074
4197
  encryptAndSerialize: () => encryptAndSerialize,
4075
4198
  encryptValue: () => encryptValue,
4076
4199
  ensureDir: () => ensureDir,
4077
4200
  ensureDirSync: () => ensureDirSync,
4078
4201
  ensureMaxLen: () => ensureMaxLen,
4079
- entryAvailabilitySchema: () => entryAvailabilitySchema,
4202
+ entryAvailabilitySchema: () => import_runtime_context.entryAvailabilitySchema,
4080
4203
  escapeGlob: () => escapeGlob,
4081
4204
  escapeHtml: () => escapeHtml,
4082
4205
  escapeHtmlAttr: () => escapeHtmlAttr,
@@ -4086,7 +4209,7 @@ __export(index_exports, {
4086
4209
  expandTemplate: () => expandTemplate,
4087
4210
  expandUriTemplate: () => expandUriTemplate,
4088
4211
  expiresAtToTTL: () => expiresAtToTTL,
4089
- extname: () => import_path.extname,
4212
+ extname: () => import_path2.extname,
4090
4213
  extractBracedParams: () => extractBracedParams,
4091
4214
  extractEmbeddedIpv4: () => extractEmbeddedIpv4,
4092
4215
  extractTemplateParams: () => extractTemplateParams,
@@ -4101,13 +4224,14 @@ __export(index_exports, {
4101
4224
  getAsyncContextMode: () => import_async_context.getAsyncContextMode,
4102
4225
  getCookie: () => getCookie,
4103
4226
  getCrypto: () => getCrypto,
4104
- getCwd: () => import_env3.getCwd,
4227
+ getCwd: () => import_env2.getCwd,
4105
4228
  getDetectedStorageType: () => getDetectedStorageType,
4106
- getEnv: () => import_env3.getEnv,
4107
- getEnvFlag: () => import_env3.getEnvFlag,
4229
+ getEnv: () => import_env2.getEnv,
4230
+ getEnvFlag: () => import_env2.getEnvFlag,
4108
4231
  getMachineId: () => getMachineId,
4232
+ getNodeEnv: () => import_env2.getNodeEnv,
4109
4233
  getOrCreateSecret: () => getOrCreateSecret,
4110
- getRuntimeContext: () => getRuntimeContext,
4234
+ getRuntimeContext: () => import_runtime_context.getRuntimeContext,
4111
4235
  getSpawnFn: () => getSpawnFn,
4112
4236
  globToRegex: () => globToRegex,
4113
4237
  hasTemplatePlaceholders: () => hasTemplatePlaceholders,
@@ -4115,27 +4239,27 @@ __export(index_exports, {
4115
4239
  hmacSha256: () => hmacSha256,
4116
4240
  idFromString: () => idFromString,
4117
4241
  inferMimeType: () => inferMimeType,
4118
- isAbsolute: () => import_path.isAbsolute,
4242
+ isAbsolute: () => import_path2.isAbsolute,
4119
4243
  isAsymmetricKeyData: () => isAsymmetricKeyData,
4120
4244
  isBrowser: () => isBrowser,
4121
- isDebug: () => import_env3.isDebug,
4122
- isDevelopment: () => import_env3.isDevelopment,
4245
+ isDebug: () => import_env2.isDebug,
4246
+ isDevelopment: () => import_env2.isDevelopment,
4123
4247
  isDirEmpty: () => isDirEmpty,
4124
- isEdgeRuntime: () => import_env3.isEdgeRuntime,
4125
- isEntryAvailable: () => isEntryAvailable,
4248
+ isEdgeRuntime: () => import_env2.isEdgeRuntime,
4249
+ isEntryAvailable: () => import_runtime_context.isEntryAvailable,
4126
4250
  isExpired: () => isExpired,
4127
4251
  isInputLengthSafe: () => isInputLengthSafe,
4128
4252
  isLocalhost: () => isLocalhost,
4129
4253
  isNode: () => isNode,
4130
4254
  isPatternSafe: () => isPatternSafe,
4131
- isProduction: () => import_env3.isProduction,
4255
+ isProduction: () => import_env2.isProduction,
4132
4256
  isRedirectResponse: () => isRedirectResponse,
4133
4257
  isRsaPssAlg: () => isRsaPssAlg,
4134
4258
  isSecretCached: () => isSecretCached,
4135
4259
  isSecretKeyData: () => isSecretKeyData,
4136
4260
  isSecretPersistenceEnabled: () => isSecretPersistenceEnabled,
4137
4261
  isSecureRequest: () => isSecureRequest,
4138
- isServerless: () => import_env3.isServerless,
4262
+ isServerless: () => import_env2.isServerless,
4139
4263
  isSignedData: () => isSignedData,
4140
4264
  isUriTemplate: () => isUriTemplate,
4141
4265
  isValidCodeChallenge: () => isValidCodeChallenge,
@@ -4156,9 +4280,9 @@ __export(index_exports, {
4156
4280
  parseKeyData: () => parseKeyData,
4157
4281
  parseSecretData: () => parseSecretData,
4158
4282
  parseUriTemplate: () => parseUriTemplate,
4159
- pathJoin: () => import_path.join,
4160
- pathResolve: () => import_path.resolve,
4161
- pathToFileURL: () => import_url.pathToFileURL,
4283
+ pathJoin: () => import_path2.join,
4284
+ pathResolve: () => import_path2.resolve,
4285
+ pathToFileURL: () => import_path2.pathToFileURL,
4162
4286
  pemToPublicJwk: () => pemToPublicJwk2,
4163
4287
  processPlatformToolCalls: () => processPlatformToolCalls,
4164
4288
  randomBytes: () => randomBytes,
@@ -4171,7 +4295,7 @@ __export(index_exports, {
4171
4295
  readdirSync: () => readdirSync,
4172
4296
  realpath: () => realpath,
4173
4297
  rename: () => rename,
4174
- resetRuntimeContext: () => resetRuntimeContext,
4298
+ resetRuntimeContext: () => import_runtime_context.resetRuntimeContext,
4175
4299
  resolveSecretPath: () => resolveSecretPath,
4176
4300
  rm: () => rm,
4177
4301
  rsaSignBase64Url: () => rsaSignBase64Url2,
@@ -4189,10 +4313,10 @@ __export(index_exports, {
4189
4313
  saveSecret: () => saveSecret,
4190
4314
  secretDataSchema: () => secretDataSchema,
4191
4315
  secretKeyDataSchema: () => secretKeyDataSchema,
4192
- sep: () => import_path.sep,
4316
+ sep: () => import_path2.sep,
4193
4317
  sepFor: () => sepFor,
4194
4318
  serializeBlob: () => serializeBlob,
4195
- setEnv: () => import_env3.setEnv,
4319
+ setEnv: () => import_env2.setEnv,
4196
4320
  setMachineIdOverride: () => setMachineIdOverride,
4197
4321
  sha256: () => sha256,
4198
4322
  sha256Base64url: () => sha256Base64url,
@@ -4201,7 +4325,7 @@ __export(index_exports, {
4201
4325
  signData: () => signData,
4202
4326
  splitWords: () => splitWords,
4203
4327
  stat: () => stat,
4204
- supportsAnsi: () => import_env3.supportsAnsi,
4328
+ supportsAnsi: () => import_env2.supportsAnsi,
4205
4329
  symlink: () => symlink,
4206
4330
  timingSafeEqual: () => timingSafeEqual,
4207
4331
  toCase: () => toCase,
@@ -4631,9 +4755,8 @@ function joinPath(...parts) {
4631
4755
  return cleaned.length ? `/${cleaned.join("/")}` : "";
4632
4756
  }
4633
4757
 
4634
- // libs/utils/src/path/node-path.ts
4635
- var import_path = require("path");
4636
- var import_url = require("url");
4758
+ // libs/utils/src/path/index.ts
4759
+ var import_path2 = require("#path");
4637
4760
 
4638
4761
  // libs/utils/src/content/content.ts
4639
4762
  function sanitizeToJson(value) {
@@ -5051,161 +5174,14 @@ var import_async_context = require("#async-context");
5051
5174
  var import_event_emitter2 = require("#event-emitter");
5052
5175
 
5053
5176
  // libs/utils/src/env/index.ts
5054
- var import_env3 = require("#env");
5055
-
5056
- // libs/utils/src/env/runtime-context.ts
5057
- var import_lazy_zod3 = require("@frontmcp/lazy-zod");
5058
5177
  var import_env2 = require("#env");
5059
-
5060
- // libs/utils/src/env/build-target.ts
5061
- var cached;
5062
- function getBuildTarget() {
5063
- if (cached) return cached;
5064
- cached = resolveBuildTarget();
5065
- return cached;
5066
- }
5067
- function resolveBuildTarget() {
5068
- const globalAny = globalThis;
5069
- const fromGlobal = globalAny.FRONTMCP_BUILD_TARGET;
5070
- if (typeof fromGlobal === "string" && isKnownTarget(fromGlobal)) return fromGlobal;
5071
- const fromEnv = typeof process !== "undefined" ? process.env?.["FRONTMCP_BUILD_TARGET"] : void 0;
5072
- if (typeof fromEnv === "string" && isKnownTarget(fromEnv)) return fromEnv;
5073
- return "unknown";
5074
- }
5075
- function isKnownTarget(value) {
5076
- return value === "unknown" || value === "node" || value === "distributed" || value === "cli" || value === "vercel" || value === "lambda" || value === "cloudflare" || value === "browser" || value === "sdk" || value === "mcpb";
5077
- }
5078
-
5079
- // libs/utils/src/env/provider.ts
5080
- var cached2;
5081
- function detectProvider() {
5082
- if (cached2) return cached2;
5083
- cached2 = detectProviderUncached();
5084
- return cached2;
5085
- }
5086
- function detectProviderUncached() {
5087
- const explicit = process.env["FRONTMCP_PROVIDER"];
5088
- if (explicit && isKnownProvider(explicit)) return explicit;
5089
- if (process.env["VERCEL"]) return "vercel";
5090
- if (process.env["AWS_LAMBDA_FUNCTION_NAME"]) return "lambda";
5091
- if (process.env["CF_PAGES"]) return "cloudflare";
5092
- if (process.env["NETLIFY"]) return "netlify";
5093
- if (process.env["AZURE_FUNCTIONS_ENVIRONMENT"]) return "azure";
5094
- if (process.env["K_SERVICE"]) return "gcp";
5095
- if (process.env["FLY_APP_NAME"]) return "fly";
5096
- if (process.env["RENDER"]) return "render";
5097
- if (process.env["RAILWAY_ENVIRONMENT"]) return "railway";
5098
- try {
5099
- const nodeFs = require("node:fs");
5100
- if (nodeFs.existsSync("/.dockerenv")) return "docker";
5101
- } catch {
5102
- }
5103
- return "bare";
5104
- }
5105
- function isKnownProvider(value) {
5106
- return value === "bare" || value === "docker" || value === "vercel" || value === "lambda" || value === "cloudflare" || value === "netlify" || value === "azure" || value === "gcp" || value === "fly" || value === "render" || value === "railway";
5107
- }
5108
-
5109
- // libs/utils/src/env/runtime-context.ts
5110
- var entryAvailabilitySchema = import_lazy_zod3.z.object({
5111
- os: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
5112
- platform: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
5113
- runtime: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
5114
- deployment: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
5115
- provider: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
5116
- target: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional(),
5117
- surface: import_lazy_zod3.z.array(import_lazy_zod3.z.enum(["mcp", "cli", "http-trigger", "job", "agent"])).optional(),
5118
- env: import_lazy_zod3.z.array(import_lazy_zod3.z.string().min(1)).optional()
5119
- }).strict();
5120
- var PROCESS_AXES = [
5121
- // `os` and `platform` both compare against the same RuntimeContext value
5122
- // — keeping `platform` as a deprecated alias lets pre-#417 metadata
5123
- // continue to match without any code change.
5124
- { key: "os", ctxKey: "os" },
5125
- { key: "platform", ctxKey: "platform" },
5126
- { key: "runtime", ctxKey: "runtime" },
5127
- { key: "deployment", ctxKey: "deployment" },
5128
- { key: "provider", ctxKey: "provider" },
5129
- { key: "target", ctxKey: "target" },
5130
- { key: "env", ctxKey: "env" }
5131
- ];
5132
- function isEntryAvailable(availability, ctx, callCtx) {
5133
- if (!availability) return true;
5134
- for (const { key, ctxKey } of PROCESS_AXES) {
5135
- const allowed = availability[key];
5136
- if (allowed === void 0) continue;
5137
- if (allowed.length === 0) return false;
5138
- if (!allowed.includes(ctx[ctxKey])) return false;
5139
- }
5140
- if (availability.surface !== void 0) {
5141
- if (availability.surface.length === 0) return false;
5142
- if (callCtx?.surface !== void 0 && !availability.surface.includes(callCtx.surface)) {
5143
- return false;
5144
- }
5145
- }
5146
- return true;
5147
- }
5148
- function checkEntryAvailability(availability, ctx, callCtx) {
5149
- if (!availability) return { available: true, missingAxes: [] };
5150
- const missing = [];
5151
- for (const { key, ctxKey } of PROCESS_AXES) {
5152
- const allowed = availability[key];
5153
- if (allowed === void 0) continue;
5154
- if (allowed.length === 0 || !allowed.includes(ctx[ctxKey])) {
5155
- missing.push(key);
5156
- }
5157
- }
5158
- if (availability.surface !== void 0) {
5159
- if (availability.surface.length === 0) {
5160
- missing.push("surface");
5161
- } else if (callCtx?.surface !== void 0 && !availability.surface.includes(callCtx.surface)) {
5162
- missing.push("surface");
5163
- }
5164
- }
5165
- return { available: missing.length === 0, missingAxes: missing };
5166
- }
5167
- function detectRuntime() {
5168
- if (typeof globalThis !== "undefined" && "Bun" in globalThis) return "bun";
5169
- if (typeof globalThis !== "undefined" && "Deno" in globalThis) return "deno";
5170
- if ((0, import_env2.isEdgeRuntime)()) return "edge";
5171
- return "node";
5172
- }
5173
- function detectDeployment(runtime) {
5174
- const explicit = process.env["FRONTMCP_DEPLOYMENT_MODE"];
5175
- if (explicit === "distributed") return "distributed";
5176
- if (explicit === "serverless") return "serverless";
5177
- if (runtime === "browser") return "browser";
5178
- if ((0, import_env2.isServerless)()) return "serverless";
5179
- return "standalone";
5180
- }
5181
- function detectRuntimeContext() {
5182
- const runtime = detectRuntime();
5183
- const os = process.platform;
5184
- return {
5185
- os,
5186
- platform: os,
5187
- runtime,
5188
- deployment: detectDeployment(runtime),
5189
- provider: detectProvider(),
5190
- target: getBuildTarget(),
5191
- env: process.env["NODE_ENV"] || "development"
5192
- };
5193
- }
5194
- var cached3;
5195
- function getRuntimeContext() {
5196
- if (!cached3) {
5197
- cached3 = detectRuntimeContext();
5198
- }
5199
- return cached3;
5200
- }
5201
- function resetRuntimeContext() {
5202
- cached3 = void 0;
5203
- }
5178
+ var import_runtime_context = require("#runtime-context");
5204
5179
 
5205
5180
  // libs/utils/src/storage/factory.ts
5206
5181
  init_errors();
5207
5182
 
5208
5183
  // libs/utils/src/storage/namespace.ts
5184
+ init_errors();
5209
5185
  var NAMESPACE_SEPARATOR = ":";
5210
5186
  function buildPrefix(name, id) {
5211
5187
  if (id) {
@@ -5337,6 +5313,12 @@ var NamespacedStorageImpl = class _NamespacedStorageImpl {
5337
5313
  async incrBy(key, amount) {
5338
5314
  return this.adapter.incrBy(this.prefixKey(key), amount);
5339
5315
  }
5316
+ async deleteIfEquals(key, expectedValue) {
5317
+ if (!this.adapter.deleteIfEquals) {
5318
+ throw new StorageNotSupportedError("deleteIfEquals", "this storage adapter");
5319
+ }
5320
+ return this.adapter.deleteIfEquals(this.prefixKey(key), expectedValue);
5321
+ }
5340
5322
  // ============================================
5341
5323
  // Pub/Sub (with channel prefixing)
5342
5324
  // ============================================
@@ -5365,15 +5347,15 @@ function createNamespacedStorage(adapter, prefix) {
5365
5347
 
5366
5348
  // libs/utils/src/storage/factory.ts
5367
5349
  init_memory();
5368
- var import_env4 = require("#env");
5350
+ var import_env3 = require("#env");
5369
5351
  function detectStorageType() {
5370
- if ((0, import_env4.getEnv)("UPSTASH_REDIS_REST_URL") && (0, import_env4.getEnv)("UPSTASH_REDIS_REST_TOKEN")) {
5352
+ if ((0, import_env3.getEnv)("UPSTASH_REDIS_REST_URL") && (0, import_env3.getEnv)("UPSTASH_REDIS_REST_TOKEN")) {
5371
5353
  return "upstash";
5372
5354
  }
5373
- if ((0, import_env4.getEnv)("KV_REST_API_URL") && (0, import_env4.getEnv)("KV_REST_API_TOKEN")) {
5355
+ if ((0, import_env3.getEnv)("KV_REST_API_URL") && (0, import_env3.getEnv)("KV_REST_API_TOKEN")) {
5374
5356
  return "vercel-kv";
5375
5357
  }
5376
- if ((0, import_env4.getEnv)("REDIS_URL") || (0, import_env4.getEnv)("REDIS_HOST")) {
5358
+ if ((0, import_env3.getEnv)("REDIS_URL") || (0, import_env3.getEnv)("REDIS_HOST")) {
5377
5359
  return "redis";
5378
5360
  }
5379
5361
  return "memory";
@@ -5413,10 +5395,10 @@ async function createAdapter(type, config) {
5413
5395
  }
5414
5396
  async function createStorage(config = {}) {
5415
5397
  let type = config.type ?? "auto";
5416
- const fallback = config.fallback ?? ((0, import_env4.isProduction)() ? "error" : "memory");
5398
+ const fallback = config.fallback ?? ((0, import_env3.isProduction)() ? "error" : "memory");
5417
5399
  if (type === "auto") {
5418
5400
  type = detectStorageType();
5419
- if (type === "memory" && (0, import_env4.isProduction)()) {
5401
+ if (type === "memory" && (0, import_env3.isProduction)()) {
5420
5402
  console.warn(
5421
5403
  "[storage] Warning: No distributed storage backend detected in production. Using in-memory storage. Set REDIS_URL, UPSTASH_REDIS_REST_URL, or KV_REST_API_URL."
5422
5404
  );
@@ -5465,6 +5447,7 @@ function getDetectedStorageType() {
5465
5447
  }
5466
5448
 
5467
5449
  // libs/utils/src/storage/index.ts
5450
+ init_redis_error_listener();
5468
5451
  init_errors();
5469
5452
 
5470
5453
  // libs/utils/src/storage/typed-storage.ts
@@ -6120,6 +6103,7 @@ function setMachineIdOverride(id) {
6120
6103
  BaseStorageAdapter,
6121
6104
  DEFAULT_CODE_VERIFIER_LENGTH,
6122
6105
  DEFAULT_MAX_INPUT_LENGTH,
6106
+ DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS,
6123
6107
  EncryptedBlobError,
6124
6108
  EncryptedStorageError,
6125
6109
  EncryptedTypedStorage,
@@ -6152,6 +6136,7 @@ function setMachineIdOverride(id) {
6152
6136
  assertNode,
6153
6137
  asymmetricAlgSchema,
6154
6138
  asymmetricKeyDataSchema,
6139
+ attachRedisErrorListener,
6155
6140
  awaitOutsideRequest,
6156
6141
  base64Decode,
6157
6142
  base64Encode,
@@ -6172,6 +6157,7 @@ function setMachineIdOverride(id) {
6172
6157
  createKeyPersistenceWithStorage,
6173
6158
  createMemoryStorage,
6174
6159
  createNamespacedStorage,
6160
+ createRedisClient,
6175
6161
  createRootStorage,
6176
6162
  createSafeRegExp,
6177
6163
  createSecretData,
@@ -6219,6 +6205,7 @@ function setMachineIdOverride(id) {
6219
6205
  getEnv,
6220
6206
  getEnvFlag,
6221
6207
  getMachineId,
6208
+ getNodeEnv,
6222
6209
  getOrCreateSecret,
6223
6210
  getRuntimeContext,
6224
6211
  getSpawnFn,