@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/esm/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { createRequire as __frontmcpCreateRequire } from 'module';
2
- const require = __frontmcpCreateRequire(import.meta.url || 'file:///');
1
+ var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
2
+ const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
3
+ // __frontmcpCreateRequire
3
4
  var __defProp = Object.defineProperty;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -594,9 +595,9 @@ function createSecretData(options) {
594
595
  }
595
596
  async function getOrCreateSecret(options) {
596
597
  const cacheKey = resolveSecretPath(options);
597
- const cached4 = secretCache.get(cacheKey);
598
- if (cached4) {
599
- return cached4;
598
+ const cached = secretCache.get(cacheKey);
599
+ if (cached) {
600
+ return cached;
600
601
  }
601
602
  const existing = generationPromises.get(cacheKey);
602
603
  if (existing) {
@@ -833,8 +834,8 @@ var init_key_persistence = __esm({
833
834
  */
834
835
  async get(kid) {
835
836
  if (this.enableCache) {
836
- const cached4 = this.cache.get(kid);
837
- if (cached4) return cached4;
837
+ const cached = this.cache.get(kid);
838
+ if (cached) return cached;
838
839
  }
839
840
  const raw = await this.storage.get(kid);
840
841
  if (!raw) return null;
@@ -1085,6 +1086,75 @@ var init_errors = __esm({
1085
1086
  }
1086
1087
  });
1087
1088
 
1089
+ // libs/utils/src/storage/utils/pattern.ts
1090
+ function globToRegex(pattern) {
1091
+ if (pattern.length > MAX_PATTERN_LENGTH) {
1092
+ throw new StoragePatternError(
1093
+ pattern.substring(0, 50) + "...",
1094
+ `Pattern exceeds maximum length of ${MAX_PATTERN_LENGTH} characters`
1095
+ );
1096
+ }
1097
+ const wildcardCount = (pattern.match(/[*?]/g) || []).length;
1098
+ if (wildcardCount > MAX_WILDCARDS) {
1099
+ throw new StoragePatternError(pattern, `Pattern has too many wildcards (max: ${MAX_WILDCARDS})`);
1100
+ }
1101
+ if (pattern === "" || pattern === "*") {
1102
+ return /^.*$/;
1103
+ }
1104
+ let regexStr = "^";
1105
+ let prevChar = "";
1106
+ for (let i = 0; i < pattern.length; i++) {
1107
+ const char = pattern[i];
1108
+ switch (char) {
1109
+ case "*":
1110
+ if (prevChar !== "*") {
1111
+ regexStr += ".*";
1112
+ }
1113
+ break;
1114
+ case "?":
1115
+ regexStr += ".";
1116
+ break;
1117
+ default:
1118
+ regexStr += char.replace(REGEX_SPECIAL_CHARS, "\\$&");
1119
+ }
1120
+ prevChar = char;
1121
+ }
1122
+ regexStr += "$";
1123
+ try {
1124
+ return new RegExp(regexStr);
1125
+ } catch {
1126
+ throw new StoragePatternError(pattern, "Failed to compile pattern to regex");
1127
+ }
1128
+ }
1129
+ function matchesPattern(key, pattern) {
1130
+ const regex = globToRegex(pattern);
1131
+ return regex.test(key);
1132
+ }
1133
+ function validatePattern(pattern) {
1134
+ try {
1135
+ globToRegex(pattern);
1136
+ return { valid: true };
1137
+ } catch (e) {
1138
+ return {
1139
+ valid: false,
1140
+ error: e instanceof Error ? e.message : "Invalid pattern"
1141
+ };
1142
+ }
1143
+ }
1144
+ function escapeGlob(literal) {
1145
+ return literal.replace(/[*?\\]/g, "\\$&");
1146
+ }
1147
+ var MAX_PATTERN_LENGTH, MAX_WILDCARDS, REGEX_SPECIAL_CHARS;
1148
+ var init_pattern = __esm({
1149
+ "libs/utils/src/storage/utils/pattern.ts"() {
1150
+ "use strict";
1151
+ init_errors();
1152
+ MAX_PATTERN_LENGTH = 500;
1153
+ MAX_WILDCARDS = 20;
1154
+ REGEX_SPECIAL_CHARS = /[.+^${}()|[\]\\]/g;
1155
+ }
1156
+ });
1157
+
1088
1158
  // libs/utils/src/storage/utils/ttl.ts
1089
1159
  function validateTTL(ttlSeconds) {
1090
1160
  if (typeof ttlSeconds !== "number") {
@@ -1199,6 +1269,17 @@ var init_base = __esm({
1199
1269
  const matchedKeys = await this.keys(pattern);
1200
1270
  return matchedKeys.length;
1201
1271
  }
1272
+ /**
1273
+ * Default: not supported, since a read followed by a delete is not atomic.
1274
+ * Override in adapters whose backend can compare and delete in one step.
1275
+ */
1276
+ async deleteIfEquals(_key, _expectedValue) {
1277
+ throw new StorageNotSupportedError(
1278
+ "deleteIfEquals",
1279
+ this.backendName,
1280
+ "Use the memory, Redis, Upstash or Vercel KV adapter for an atomic compare-and-delete."
1281
+ );
1282
+ }
1202
1283
  // ============================================
1203
1284
  // Pub/Sub (default: not supported)
1204
1285
  // ============================================
@@ -1247,85 +1328,16 @@ var init_base = __esm({
1247
1328
  }
1248
1329
  });
1249
1330
 
1250
- // libs/utils/src/storage/utils/pattern.ts
1251
- function globToRegex(pattern) {
1252
- if (pattern.length > MAX_PATTERN_LENGTH) {
1253
- throw new StoragePatternError(
1254
- pattern.substring(0, 50) + "...",
1255
- `Pattern exceeds maximum length of ${MAX_PATTERN_LENGTH} characters`
1256
- );
1257
- }
1258
- const wildcardCount = (pattern.match(/[*?]/g) || []).length;
1259
- if (wildcardCount > MAX_WILDCARDS) {
1260
- throw new StoragePatternError(pattern, `Pattern has too many wildcards (max: ${MAX_WILDCARDS})`);
1261
- }
1262
- if (pattern === "" || pattern === "*") {
1263
- return /^.*$/;
1264
- }
1265
- let regexStr = "^";
1266
- let prevChar = "";
1267
- for (let i = 0; i < pattern.length; i++) {
1268
- const char = pattern[i];
1269
- switch (char) {
1270
- case "*":
1271
- if (prevChar !== "*") {
1272
- regexStr += ".*";
1273
- }
1274
- break;
1275
- case "?":
1276
- regexStr += ".";
1277
- break;
1278
- default:
1279
- regexStr += char.replace(REGEX_SPECIAL_CHARS, "\\$&");
1280
- }
1281
- prevChar = char;
1282
- }
1283
- regexStr += "$";
1284
- try {
1285
- return new RegExp(regexStr);
1286
- } catch {
1287
- throw new StoragePatternError(pattern, "Failed to compile pattern to regex");
1288
- }
1289
- }
1290
- function matchesPattern(key, pattern) {
1291
- const regex = globToRegex(pattern);
1292
- return regex.test(key);
1293
- }
1294
- function validatePattern(pattern) {
1295
- try {
1296
- globToRegex(pattern);
1297
- return { valid: true };
1298
- } catch (e) {
1299
- return {
1300
- valid: false,
1301
- error: e instanceof Error ? e.message : "Invalid pattern"
1302
- };
1303
- }
1304
- }
1305
- function escapeGlob(literal) {
1306
- return literal.replace(/[*?\\]/g, "\\$&");
1307
- }
1308
- var MAX_PATTERN_LENGTH, MAX_WILDCARDS, REGEX_SPECIAL_CHARS;
1309
- var init_pattern = __esm({
1310
- "libs/utils/src/storage/utils/pattern.ts"() {
1311
- "use strict";
1312
- init_errors();
1313
- MAX_PATTERN_LENGTH = 500;
1314
- MAX_WILDCARDS = 20;
1315
- REGEX_SPECIAL_CHARS = /[.+^${}()|[\]\\]/g;
1316
- }
1317
- });
1318
-
1319
1331
  // libs/utils/src/storage/adapters/memory.ts
1320
1332
  import { EventEmitter } from "#event-emitter";
1321
1333
  var DEFAULT_SWEEP_INTERVAL_SECONDS, MAX_TIMEOUT_MS, MemoryStorageAdapter;
1322
1334
  var init_memory = __esm({
1323
1335
  "libs/utils/src/storage/adapters/memory.ts"() {
1324
1336
  "use strict";
1325
- init_base();
1326
1337
  init_errors();
1327
1338
  init_pattern();
1328
1339
  init_ttl();
1340
+ init_base();
1329
1341
  DEFAULT_SWEEP_INTERVAL_SECONDS = 60;
1330
1342
  MAX_TIMEOUT_MS = 2147483647;
1331
1343
  MemoryStorageAdapter = class extends BaseStorageAdapter {
@@ -1417,6 +1429,12 @@ var init_memory = __esm({
1417
1429
  this.ensureConnected();
1418
1430
  return this.deleteEntry(key);
1419
1431
  }
1432
+ async deleteIfEquals(key, expectedValue) {
1433
+ this.ensureConnected();
1434
+ const entry = this.store.get(key);
1435
+ if (!entry || isExpired(entry.expiresAt) || entry.value !== expectedValue) return false;
1436
+ return this.deleteEntry(key);
1437
+ }
1420
1438
  async exists(key) {
1421
1439
  this.ensureConnected();
1422
1440
  const entry = this.store.get(key);
@@ -2962,6 +2980,41 @@ var init_crypto = __esm({
2962
2980
  }
2963
2981
  });
2964
2982
 
2983
+ // libs/utils/src/storage/redis-error-listener.ts
2984
+ function attachRedisErrorListener(client, options = {}) {
2985
+ const label = options.label ?? "redis";
2986
+ const intervalMs = options.intervalMs ?? DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS;
2987
+ let lastLoggedAt = 0;
2988
+ let suppressed = 0;
2989
+ const listener = (error) => {
2990
+ const now = Date.now();
2991
+ if (lastLoggedAt !== 0 && now - lastLoggedAt < intervalMs) {
2992
+ suppressed++;
2993
+ return;
2994
+ }
2995
+ const extra = suppressed > 0 ? ` (${suppressed} similar error(s) suppressed)` : "";
2996
+ suppressed = 0;
2997
+ lastLoggedAt = now;
2998
+ const message = `[${label}] Redis connection error: ${error?.message ?? String(error)}${extra}`;
2999
+ if (options.logger) {
3000
+ options.logger.warn(message);
3001
+ } else {
3002
+ console.warn(message);
3003
+ }
3004
+ };
3005
+ client.on("error", listener);
3006
+ return () => {
3007
+ client.removeListener("error", listener);
3008
+ };
3009
+ }
3010
+ var DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS;
3011
+ var init_redis_error_listener = __esm({
3012
+ "libs/utils/src/storage/redis-error-listener.ts"() {
3013
+ "use strict";
3014
+ DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS = 3e4;
3015
+ }
3016
+ });
3017
+
2965
3018
  // libs/utils/src/storage/utils/index.ts
2966
3019
  var init_utils = __esm({
2967
3020
  "libs/utils/src/storage/utils/index.ts"() {
@@ -2970,10 +3023,20 @@ var init_utils = __esm({
2970
3023
  }
2971
3024
  });
2972
3025
 
3026
+ // libs/utils/src/storage/utils/compare-and-delete.ts
3027
+ var COMPARE_AND_DELETE_SCRIPT;
3028
+ var init_compare_and_delete = __esm({
3029
+ "libs/utils/src/storage/utils/compare-and-delete.ts"() {
3030
+ "use strict";
3031
+ COMPARE_AND_DELETE_SCRIPT = `if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0`;
3032
+ }
3033
+ });
3034
+
2973
3035
  // libs/utils/src/storage/adapters/redis.ts
2974
3036
  var redis_exports = {};
2975
3037
  __export(redis_exports, {
2976
- RedisStorageAdapter: () => RedisStorageAdapter
3038
+ RedisStorageAdapter: () => RedisStorageAdapter,
3039
+ createRedisClient: () => createRedisClient
2977
3040
  });
2978
3041
  function getRedisClass() {
2979
3042
  try {
@@ -2991,12 +3054,28 @@ function getRedisClass() {
2991
3054
  });
2992
3055
  }
2993
3056
  }
3057
+ function createRedisClient(options) {
3058
+ const RedisClass = getRedisClass();
3059
+ const baseOptions = { lazyConnect: false, maxRetriesPerRequest: 3 };
3060
+ const client = options.url ? new RedisClass(options.url, baseOptions) : new RedisClass({
3061
+ ...baseOptions,
3062
+ host: options.host ?? "localhost",
3063
+ port: options.port ?? 6379,
3064
+ password: options.password,
3065
+ db: options.db ?? 0,
3066
+ tls: options.tls ? {} : void 0
3067
+ });
3068
+ attachRedisErrorListener(client, { label: options.label ?? "redis", logger: options.logger });
3069
+ return client;
3070
+ }
2994
3071
  var RedisStorageAdapter;
2995
3072
  var init_redis = __esm({
2996
3073
  "libs/utils/src/storage/adapters/redis.ts"() {
2997
3074
  "use strict";
2998
3075
  init_errors();
3076
+ init_redis_error_listener();
2999
3077
  init_utils();
3078
+ init_compare_and_delete();
3000
3079
  init_base();
3001
3080
  RedisStorageAdapter = class extends BaseStorageAdapter {
3002
3081
  backendName = "redis";
@@ -3004,6 +3083,7 @@ var init_redis = __esm({
3004
3083
  subscriber;
3005
3084
  options;
3006
3085
  ownsClient;
3086
+ detachErrorListener;
3007
3087
  connecting;
3008
3088
  keyPrefix;
3009
3089
  subscriptionHandlers = /* @__PURE__ */ new Map();
@@ -3060,7 +3140,10 @@ var init_redis = __esm({
3060
3140
  await client.ping();
3061
3141
  this.client = client;
3062
3142
  this.connected = true;
3063
- if (this.ownsClient) client.removeListener("error", recordSocketError);
3143
+ if (this.ownsClient) {
3144
+ client.removeListener("error", recordSocketError);
3145
+ this.detachErrorListener = attachRedisErrorListener(client, { label: "RedisStorage" });
3146
+ }
3064
3147
  } catch (e) {
3065
3148
  if (this.ownsClient && client) {
3066
3149
  try {
@@ -3081,6 +3164,8 @@ var init_redis = __esm({
3081
3164
  if (this.ownsClient && this.client) {
3082
3165
  await this.client.quit();
3083
3166
  }
3167
+ this.detachErrorListener?.();
3168
+ this.detachErrorListener = void 0;
3084
3169
  this.client = void 0;
3085
3170
  this.connected = false;
3086
3171
  this.subscriptionHandlers.clear();
@@ -3145,6 +3230,15 @@ var init_redis = __esm({
3145
3230
  const result = await this.getConnectedClient().del(this.prefixKey(key));
3146
3231
  return result > 0;
3147
3232
  }
3233
+ async deleteIfEquals(key, expectedValue) {
3234
+ const deleted = await this.getConnectedClient().eval(
3235
+ COMPARE_AND_DELETE_SCRIPT,
3236
+ 1,
3237
+ this.prefixKey(key),
3238
+ expectedValue
3239
+ );
3240
+ return deleted === 1;
3241
+ }
3148
3242
  async exists(key) {
3149
3243
  const result = await this.getConnectedClient().exists(this.prefixKey(key));
3150
3244
  return result > 0;
@@ -3308,6 +3402,7 @@ var init_redis = __esm({
3308
3402
  } else {
3309
3403
  throw new StorageConfigError("redis", "Cannot create subscriber without url, config, or client");
3310
3404
  }
3405
+ attachRedisErrorListener(subscriber, { label: "RedisStorage:subscriber" });
3311
3406
  subscriber.on("message", (channel, message) => {
3312
3407
  const handlers = this.subscriptionHandlers.get(channel);
3313
3408
  if (handlers) {
@@ -3352,9 +3447,12 @@ var vercel_kv_exports = {};
3352
3447
  __export(vercel_kv_exports, {
3353
3448
  VercelKvStorageAdapter: () => VercelKvStorageAdapter
3354
3449
  });
3355
- function getVercelKv() {
3450
+ async function loadVercelKv() {
3356
3451
  try {
3357
- return __require("@vercel/kv");
3452
+ const mod = await import("@vercel/kv");
3453
+ const resolved = typeof mod.createClient === "function" ? mod : mod.default;
3454
+ if (!resolved || typeof resolved.createClient !== "function") throw new Error("createClient missing");
3455
+ return resolved;
3358
3456
  } catch {
3359
3457
  throw new Error("@vercel/kv is required for Vercel KV storage adapter. Install it with: npm install @vercel/kv");
3360
3458
  }
@@ -3363,9 +3461,10 @@ var VercelKvStorageAdapter;
3363
3461
  var init_vercel_kv = __esm({
3364
3462
  "libs/utils/src/storage/adapters/vercel-kv.ts"() {
3365
3463
  "use strict";
3366
- init_base();
3367
3464
  init_errors();
3465
+ init_compare_and_delete();
3368
3466
  init_ttl();
3467
+ init_base();
3369
3468
  VercelKvStorageAdapter = class extends BaseStorageAdapter {
3370
3469
  backendName = "vercel-kv";
3371
3470
  client;
@@ -3390,17 +3489,13 @@ var init_vercel_kv = __esm({
3390
3489
  async connect() {
3391
3490
  if (this.connected) return;
3392
3491
  try {
3393
- const { createClient, kv } = getVercelKv();
3394
- if (this.options.url === process.env["KV_REST_API_URL"]) {
3395
- this.client = kv;
3396
- } else {
3397
- const url = this.options.url;
3398
- const token = this.options.token;
3399
- if (!url || !token) {
3400
- throw new StorageConfigError("vercel-kv", "URL and token are required");
3401
- }
3402
- this.client = createClient({ url, token });
3492
+ const { createClient } = await loadVercelKv();
3493
+ const url = this.options.url;
3494
+ const token = this.options.token;
3495
+ if (!url || !token) {
3496
+ throw new StorageConfigError("vercel-kv", "URL and token are required");
3403
3497
  }
3498
+ this.client = createClient({ url, token, cache: void 0 });
3404
3499
  await this.client.exists("__healthcheck__");
3405
3500
  this.connected = true;
3406
3501
  } catch (e) {
@@ -3462,6 +3557,14 @@ var init_vercel_kv = __esm({
3462
3557
  const result = await this.getConnectedClient().del(this.prefixKey(key));
3463
3558
  return result > 0;
3464
3559
  }
3560
+ async deleteIfEquals(key, expectedValue) {
3561
+ const deleted = await this.getConnectedClient().eval(
3562
+ COMPARE_AND_DELETE_SCRIPT,
3563
+ [this.prefixKey(key)],
3564
+ [expectedValue]
3565
+ );
3566
+ return deleted === 1;
3567
+ }
3465
3568
  async exists(key) {
3466
3569
  const result = await this.getConnectedClient().exists(this.prefixKey(key));
3467
3570
  return result > 0;
@@ -3579,9 +3682,10 @@ var PUBSUB_POLL_INTERVAL_MS, UpstashStorageAdapter;
3579
3682
  var init_upstash = __esm({
3580
3683
  "libs/utils/src/storage/adapters/upstash.ts"() {
3581
3684
  "use strict";
3582
- init_base();
3583
3685
  init_errors();
3686
+ init_compare_and_delete();
3584
3687
  init_ttl();
3688
+ init_base();
3585
3689
  PUBSUB_POLL_INTERVAL_MS = 100;
3586
3690
  UpstashStorageAdapter = class extends BaseStorageAdapter {
3587
3691
  backendName = "upstash";
@@ -3686,6 +3790,14 @@ var init_upstash = __esm({
3686
3790
  const result = await this.getConnectedClient().del(this.prefixKey(key));
3687
3791
  return result > 0;
3688
3792
  }
3793
+ async deleteIfEquals(key, expectedValue) {
3794
+ const deleted = await this.getConnectedClient().eval(
3795
+ COMPARE_AND_DELETE_SCRIPT,
3796
+ [this.prefixKey(key)],
3797
+ [expectedValue]
3798
+ );
3799
+ return deleted === 1;
3800
+ }
3689
3801
  async exists(key) {
3690
3802
  const result = await this.getConnectedClient().exists(this.prefixKey(key));
3691
3803
  return result > 0;
@@ -3875,7 +3987,9 @@ var init_cloudflare_kv = __esm({
3875
3987
  constructor(options) {
3876
3988
  super();
3877
3989
  if (!options?.namespace) {
3878
- throw new Error("CloudflareKvStorageAdapter: a bound KV `namespace` is required (resolve it from the Worker `env`).");
3990
+ throw new Error(
3991
+ "CloudflareKvStorageAdapter: a bound KV `namespace` is required (resolve it from the Worker `env`)."
3992
+ );
3879
3993
  }
3880
3994
  this.kv = options.namespace;
3881
3995
  this.keyPrefix = options.keyPrefix ?? "";
@@ -3969,6 +4083,13 @@ var init_cloudflare_kv = __esm({
3969
4083
  "Cloudflare KV has no atomic increment (a get+put is racy under eventual consistency). Use a Durable Object for atomic counters."
3970
4084
  );
3971
4085
  }
4086
+ async deleteIfEquals(_key, _expectedValue) {
4087
+ throw new StorageNotSupportedError(
4088
+ "deleteIfEquals",
4089
+ this.backendName,
4090
+ "Cloudflare KV has no compare-and-set; use a Durable Object for an atomic compare-and-delete."
4091
+ );
4092
+ }
3972
4093
  // ── Pub/Sub (unsupported) ────────────────────────────────────────────────
3973
4094
  getPubSubSuggestion() {
3974
4095
  return "Cloudflare KV does not support pub/sub. Use a Durable Object (or Queues) for fan-out.";
@@ -4397,9 +4518,17 @@ function joinPath(...parts) {
4397
4518
  return cleaned.length ? `/${cleaned.join("/")}` : "";
4398
4519
  }
4399
4520
 
4400
- // libs/utils/src/path/node-path.ts
4401
- import { resolve, join, basename, dirname, extname, isAbsolute, sep } from "path";
4402
- import { pathToFileURL } from "url";
4521
+ // libs/utils/src/path/index.ts
4522
+ import {
4523
+ resolve,
4524
+ join,
4525
+ basename,
4526
+ dirname,
4527
+ extname,
4528
+ isAbsolute,
4529
+ pathToFileURL,
4530
+ sep
4531
+ } from "#path";
4403
4532
 
4404
4533
  // libs/utils/src/content/content.ts
4405
4534
  function sanitizeToJson(value) {
@@ -4826,169 +4955,30 @@ import { EventEmitter as EventEmitter2 } from "#event-emitter";
4826
4955
  import {
4827
4956
  getEnv as getEnv2,
4828
4957
  getCwd as getCwd2,
4958
+ getNodeEnv,
4829
4959
  isProduction,
4830
4960
  isDevelopment,
4831
4961
  getEnvFlag,
4832
4962
  isDebug,
4833
4963
  setEnv,
4834
- isEdgeRuntime as isEdgeRuntime2,
4835
- isServerless as isServerless2,
4964
+ isEdgeRuntime,
4965
+ isServerless,
4836
4966
  supportsAnsi
4837
4967
  } from "#env";
4838
-
4839
- // libs/utils/src/env/runtime-context.ts
4840
- import { z as z3 } from "@frontmcp/lazy-zod";
4841
- import { isEdgeRuntime, isServerless } from "#env";
4842
-
4843
- // libs/utils/src/env/build-target.ts
4844
- var cached;
4845
- function getBuildTarget() {
4846
- if (cached) return cached;
4847
- cached = resolveBuildTarget();
4848
- return cached;
4849
- }
4850
- function resolveBuildTarget() {
4851
- const globalAny = globalThis;
4852
- const fromGlobal = globalAny.FRONTMCP_BUILD_TARGET;
4853
- if (typeof fromGlobal === "string" && isKnownTarget(fromGlobal)) return fromGlobal;
4854
- const fromEnv = typeof process !== "undefined" ? process.env?.["FRONTMCP_BUILD_TARGET"] : void 0;
4855
- if (typeof fromEnv === "string" && isKnownTarget(fromEnv)) return fromEnv;
4856
- return "unknown";
4857
- }
4858
- function isKnownTarget(value) {
4859
- return value === "unknown" || value === "node" || value === "distributed" || value === "cli" || value === "vercel" || value === "lambda" || value === "cloudflare" || value === "browser" || value === "sdk" || value === "mcpb";
4860
- }
4861
-
4862
- // libs/utils/src/env/provider.ts
4863
- var cached2;
4864
- function detectProvider() {
4865
- if (cached2) return cached2;
4866
- cached2 = detectProviderUncached();
4867
- return cached2;
4868
- }
4869
- function detectProviderUncached() {
4870
- const explicit = process.env["FRONTMCP_PROVIDER"];
4871
- if (explicit && isKnownProvider(explicit)) return explicit;
4872
- if (process.env["VERCEL"]) return "vercel";
4873
- if (process.env["AWS_LAMBDA_FUNCTION_NAME"]) return "lambda";
4874
- if (process.env["CF_PAGES"]) return "cloudflare";
4875
- if (process.env["NETLIFY"]) return "netlify";
4876
- if (process.env["AZURE_FUNCTIONS_ENVIRONMENT"]) return "azure";
4877
- if (process.env["K_SERVICE"]) return "gcp";
4878
- if (process.env["FLY_APP_NAME"]) return "fly";
4879
- if (process.env["RENDER"]) return "render";
4880
- if (process.env["RAILWAY_ENVIRONMENT"]) return "railway";
4881
- try {
4882
- const nodeFs = __require("node:fs");
4883
- if (nodeFs.existsSync("/.dockerenv")) return "docker";
4884
- } catch {
4885
- }
4886
- return "bare";
4887
- }
4888
- function isKnownProvider(value) {
4889
- return value === "bare" || value === "docker" || value === "vercel" || value === "lambda" || value === "cloudflare" || value === "netlify" || value === "azure" || value === "gcp" || value === "fly" || value === "render" || value === "railway";
4890
- }
4891
-
4892
- // libs/utils/src/env/runtime-context.ts
4893
- var entryAvailabilitySchema = z3.object({
4894
- os: z3.array(z3.string().min(1)).optional(),
4895
- platform: z3.array(z3.string().min(1)).optional(),
4896
- runtime: z3.array(z3.string().min(1)).optional(),
4897
- deployment: z3.array(z3.string().min(1)).optional(),
4898
- provider: z3.array(z3.string().min(1)).optional(),
4899
- target: z3.array(z3.string().min(1)).optional(),
4900
- surface: z3.array(z3.enum(["mcp", "cli", "http-trigger", "job", "agent"])).optional(),
4901
- env: z3.array(z3.string().min(1)).optional()
4902
- }).strict();
4903
- var PROCESS_AXES = [
4904
- // `os` and `platform` both compare against the same RuntimeContext value
4905
- // — keeping `platform` as a deprecated alias lets pre-#417 metadata
4906
- // continue to match without any code change.
4907
- { key: "os", ctxKey: "os" },
4908
- { key: "platform", ctxKey: "platform" },
4909
- { key: "runtime", ctxKey: "runtime" },
4910
- { key: "deployment", ctxKey: "deployment" },
4911
- { key: "provider", ctxKey: "provider" },
4912
- { key: "target", ctxKey: "target" },
4913
- { key: "env", ctxKey: "env" }
4914
- ];
4915
- function isEntryAvailable(availability, ctx, callCtx) {
4916
- if (!availability) return true;
4917
- for (const { key, ctxKey } of PROCESS_AXES) {
4918
- const allowed = availability[key];
4919
- if (allowed === void 0) continue;
4920
- if (allowed.length === 0) return false;
4921
- if (!allowed.includes(ctx[ctxKey])) return false;
4922
- }
4923
- if (availability.surface !== void 0) {
4924
- if (availability.surface.length === 0) return false;
4925
- if (callCtx?.surface !== void 0 && !availability.surface.includes(callCtx.surface)) {
4926
- return false;
4927
- }
4928
- }
4929
- return true;
4930
- }
4931
- function checkEntryAvailability(availability, ctx, callCtx) {
4932
- if (!availability) return { available: true, missingAxes: [] };
4933
- const missing = [];
4934
- for (const { key, ctxKey } of PROCESS_AXES) {
4935
- const allowed = availability[key];
4936
- if (allowed === void 0) continue;
4937
- if (allowed.length === 0 || !allowed.includes(ctx[ctxKey])) {
4938
- missing.push(key);
4939
- }
4940
- }
4941
- if (availability.surface !== void 0) {
4942
- if (availability.surface.length === 0) {
4943
- missing.push("surface");
4944
- } else if (callCtx?.surface !== void 0 && !availability.surface.includes(callCtx.surface)) {
4945
- missing.push("surface");
4946
- }
4947
- }
4948
- return { available: missing.length === 0, missingAxes: missing };
4949
- }
4950
- function detectRuntime() {
4951
- if (typeof globalThis !== "undefined" && "Bun" in globalThis) return "bun";
4952
- if (typeof globalThis !== "undefined" && "Deno" in globalThis) return "deno";
4953
- if (isEdgeRuntime()) return "edge";
4954
- return "node";
4955
- }
4956
- function detectDeployment(runtime) {
4957
- const explicit = process.env["FRONTMCP_DEPLOYMENT_MODE"];
4958
- if (explicit === "distributed") return "distributed";
4959
- if (explicit === "serverless") return "serverless";
4960
- if (runtime === "browser") return "browser";
4961
- if (isServerless()) return "serverless";
4962
- return "standalone";
4963
- }
4964
- function detectRuntimeContext() {
4965
- const runtime = detectRuntime();
4966
- const os = process.platform;
4967
- return {
4968
- os,
4969
- platform: os,
4970
- runtime,
4971
- deployment: detectDeployment(runtime),
4972
- provider: detectProvider(),
4973
- target: getBuildTarget(),
4974
- env: process.env["NODE_ENV"] || "development"
4975
- };
4976
- }
4977
- var cached3;
4978
- function getRuntimeContext() {
4979
- if (!cached3) {
4980
- cached3 = detectRuntimeContext();
4981
- }
4982
- return cached3;
4983
- }
4984
- function resetRuntimeContext() {
4985
- cached3 = void 0;
4986
- }
4968
+ import {
4969
+ getRuntimeContext,
4970
+ resetRuntimeContext,
4971
+ detectRuntimeContext,
4972
+ isEntryAvailable,
4973
+ checkEntryAvailability,
4974
+ entryAvailabilitySchema
4975
+ } from "#runtime-context";
4987
4976
 
4988
4977
  // libs/utils/src/storage/factory.ts
4989
4978
  init_errors();
4990
4979
 
4991
4980
  // libs/utils/src/storage/namespace.ts
4981
+ init_errors();
4992
4982
  var NAMESPACE_SEPARATOR = ":";
4993
4983
  function buildPrefix(name, id) {
4994
4984
  if (id) {
@@ -5120,6 +5110,12 @@ var NamespacedStorageImpl = class _NamespacedStorageImpl {
5120
5110
  async incrBy(key, amount) {
5121
5111
  return this.adapter.incrBy(this.prefixKey(key), amount);
5122
5112
  }
5113
+ async deleteIfEquals(key, expectedValue) {
5114
+ if (!this.adapter.deleteIfEquals) {
5115
+ throw new StorageNotSupportedError("deleteIfEquals", "this storage adapter");
5116
+ }
5117
+ return this.adapter.deleteIfEquals(this.prefixKey(key), expectedValue);
5118
+ }
5123
5119
  // ============================================
5124
5120
  // Pub/Sub (with channel prefixing)
5125
5121
  // ============================================
@@ -5248,6 +5244,7 @@ function getDetectedStorageType() {
5248
5244
  }
5249
5245
 
5250
5246
  // libs/utils/src/storage/index.ts
5247
+ init_redis_error_listener();
5251
5248
  init_errors();
5252
5249
 
5253
5250
  // libs/utils/src/storage/typed-storage.ts
@@ -5902,6 +5899,7 @@ export {
5902
5899
  BaseStorageAdapter,
5903
5900
  DEFAULT_CODE_VERIFIER_LENGTH,
5904
5901
  DEFAULT_MAX_INPUT_LENGTH,
5902
+ DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS,
5905
5903
  EncryptedBlobError,
5906
5904
  EncryptedStorageError,
5907
5905
  EncryptedTypedStorage,
@@ -5934,6 +5932,7 @@ export {
5934
5932
  assertNode,
5935
5933
  asymmetricAlgSchema,
5936
5934
  asymmetricKeyDataSchema,
5935
+ attachRedisErrorListener,
5937
5936
  awaitOutsideRequest,
5938
5937
  base64Decode,
5939
5938
  base64Encode,
@@ -5954,6 +5953,7 @@ export {
5954
5953
  createKeyPersistenceWithStorage,
5955
5954
  createMemoryStorage,
5956
5955
  createNamespacedStorage,
5956
+ createRedisClient,
5957
5957
  createRootStorage,
5958
5958
  createSafeRegExp,
5959
5959
  createSecretData,
@@ -6001,6 +6001,7 @@ export {
6001
6001
  getEnv2 as getEnv,
6002
6002
  getEnvFlag,
6003
6003
  getMachineId,
6004
+ getNodeEnv,
6004
6005
  getOrCreateSecret,
6005
6006
  getRuntimeContext,
6006
6007
  getSpawnFn,
@@ -6016,7 +6017,7 @@ export {
6016
6017
  isDebug,
6017
6018
  isDevelopment,
6018
6019
  isDirEmpty,
6019
- isEdgeRuntime2 as isEdgeRuntime,
6020
+ isEdgeRuntime,
6020
6021
  isEntryAvailable,
6021
6022
  isExpired,
6022
6023
  isInputLengthSafe,
@@ -6030,7 +6031,7 @@ export {
6030
6031
  isSecretKeyData,
6031
6032
  isSecretPersistenceEnabled,
6032
6033
  isSecureRequest,
6033
- isServerless2 as isServerless,
6034
+ isServerless,
6034
6035
  isSignedData,
6035
6036
  isUriTemplate,
6036
6037
  isValidCodeChallenge,