@frontmcp/utils 1.8.7 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frontmcp/utils",
3
- "version": "1.8.7",
3
+ "version": "1.9.0",
4
4
  "description": "Shared utility functions for FrontMCP - string manipulation, URI handling, path utilities, and more",
5
5
  "author": "AgentFront <info@agentfront.dev>",
6
6
  "license": "Apache-2.0",
@@ -32,7 +32,7 @@
32
32
  "@noble/hashes": "^2.0.1"
33
33
  },
34
34
  "peerDependencies": {
35
- "@frontmcp/lazy-zod": "1.8.7",
35
+ "@frontmcp/lazy-zod": "1.9.0",
36
36
  "@vercel/kv": "^2.0.0 || ^3.0.0",
37
37
  "ioredis": "^5.0.0"
38
38
  },
@@ -47,32 +47,50 @@
47
47
  "imports": {
48
48
  "#crypto-provider": {
49
49
  "worker": "./crypto/node.js",
50
- "browser": "./crypto/browser.js",
50
+ "browser": {
51
+ "import": "./esm/crypto/browser.mjs",
52
+ "default": "./crypto/browser.js"
53
+ },
51
54
  "default": "./crypto/node.js"
52
55
  },
53
56
  "#async-context": {
54
57
  "worker": "./async-context/node-async-context.js",
55
- "browser": "./async-context/browser-async-context.js",
58
+ "browser": {
59
+ "import": "./esm/async-context/browser-async-context.mjs",
60
+ "default": "./async-context/browser-async-context.js"
61
+ },
56
62
  "default": "./async-context/node-async-context.js"
57
63
  },
58
64
  "#event-emitter": {
59
65
  "worker": "./event-emitter/node-event-emitter.js",
60
- "browser": "./event-emitter/browser-event-emitter.js",
66
+ "browser": {
67
+ "import": "./esm/event-emitter/browser-event-emitter.mjs",
68
+ "default": "./event-emitter/browser-event-emitter.js"
69
+ },
61
70
  "default": "./event-emitter/node-event-emitter.js"
62
71
  },
63
72
  "#env": {
64
73
  "worker": "./env/node-env.js",
65
- "browser": "./env/browser-env.js",
74
+ "browser": {
75
+ "import": "./esm/env/browser-env.mjs",
76
+ "default": "./env/browser-env.js"
77
+ },
66
78
  "default": "./env/node-env.js"
67
79
  },
68
80
  "#runtime-context": {
69
81
  "worker": "./env/runtime-context.js",
70
- "browser": "./env/browser-runtime-context.js",
82
+ "browser": {
83
+ "import": "./esm/env/browser-runtime-context.mjs",
84
+ "default": "./env/browser-runtime-context.js"
85
+ },
71
86
  "default": "./env/runtime-context.js"
72
87
  },
73
88
  "#path": {
74
89
  "worker": "./path/node-path.js",
75
- "browser": "./path/browser-path.js",
90
+ "browser": {
91
+ "import": "./esm/path/browser-path.mjs",
92
+ "default": "./path/browser-path.js"
93
+ },
76
94
  "default": "./path/node-path.js"
77
95
  }
78
96
  },
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // libs/utils/src/path/browser-path.ts
21
+ var browser_path_exports = {};
22
+ __export(browser_path_exports, {
23
+ basename: () => basename,
24
+ dirname: () => dirname,
25
+ extname: () => extname,
26
+ isAbsolute: () => isAbsolute,
27
+ join: () => join,
28
+ pathToFileURL: () => pathToFileURL,
29
+ resolve: () => resolve,
30
+ sep: () => sep
31
+ });
32
+ module.exports = __toCommonJS(browser_path_exports);
33
+ function notAvailable(name) {
34
+ throw new Error(`path.${name}() is not available in browser environments`);
35
+ }
36
+ function resolve(..._segments) {
37
+ return notAvailable("resolve");
38
+ }
39
+ function join(..._segments) {
40
+ return notAvailable("join");
41
+ }
42
+ function basename(p, ext) {
43
+ const trimmed = p.replace(/[/\\]+$/, "");
44
+ const lastSep = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
45
+ let base = lastSep >= 0 ? trimmed.slice(lastSep + 1) : trimmed;
46
+ if (ext && base.endsWith(ext)) {
47
+ base = base.slice(0, -ext.length);
48
+ }
49
+ return base;
50
+ }
51
+ function dirname(_p) {
52
+ return notAvailable("dirname");
53
+ }
54
+ function extname(p) {
55
+ const base = basename(p);
56
+ const dotIndex = base.lastIndexOf(".");
57
+ return dotIndex > 0 ? base.slice(dotIndex) : "";
58
+ }
59
+ function isAbsolute(p) {
60
+ if (!p) return false;
61
+ if (p.startsWith("/") || p.startsWith("\\")) return true;
62
+ if (/^[A-Za-z]:[\\/]/.test(p)) return true;
63
+ return false;
64
+ }
65
+ function pathToFileURL(_p) {
66
+ return notAvailable("pathToFileURL");
67
+ }
68
+ var sep = "/";
69
+ // Annotate the CommonJS export names for ESM import in node:
70
+ 0 && (module.exports = {
71
+ basename,
72
+ dirname,
73
+ extname,
74
+ isAbsolute,
75
+ join,
76
+ pathToFileURL,
77
+ resolve,
78
+ sep
79
+ });
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // libs/utils/src/path/node-path.ts
21
+ var node_path_exports = {};
22
+ __export(node_path_exports, {
23
+ basename: () => import_path.basename,
24
+ dirname: () => import_path.dirname,
25
+ extname: () => import_path.extname,
26
+ isAbsolute: () => import_path.isAbsolute,
27
+ join: () => import_path.join,
28
+ pathToFileURL: () => import_url.pathToFileURL,
29
+ resolve: () => import_path.resolve,
30
+ sep: () => import_path.sep
31
+ });
32
+ module.exports = __toCommonJS(node_path_exports);
33
+ var import_path = require("path");
34
+ var import_url = require("url");
35
+ // Annotate the CommonJS export names for ESM import in node:
36
+ 0 && (module.exports = {
37
+ basename,
38
+ dirname,
39
+ extname,
40
+ isAbsolute,
41
+ join,
42
+ pathToFileURL,
43
+ resolve,
44
+ sep
45
+ });
@@ -4,7 +4,7 @@
4
4
  * Abstract base class for storage adapters.
5
5
  * Provides common validation and default implementations.
6
6
  */
7
- import type { StorageAdapter, SetOptions, SetEntry, MessageHandler, Unsubscribe } from '../types';
7
+ import type { MessageHandler, SetEntry, SetOptions, StorageAdapter, Unsubscribe } from '../types';
8
8
  /**
9
9
  * Abstract base class for storage adapters.
10
10
  * Subclasses must implement the abstract methods.
@@ -63,6 +63,11 @@ export declare abstract class BaseStorageAdapter implements StorageAdapter {
63
63
  abstract incr(key: string): Promise<number>;
64
64
  abstract decr(key: string): Promise<number>;
65
65
  abstract incrBy(key: string, amount: number): Promise<number>;
66
+ /**
67
+ * Default: not supported, since a read followed by a delete is not atomic.
68
+ * Override in adapters whose backend can compare and delete in one step.
69
+ */
70
+ deleteIfEquals(_key: string, _expectedValue: string): Promise<boolean>;
66
71
  /**
67
72
  * Default: pub/sub not supported.
68
73
  * Override in adapters that support it.
@@ -18,6 +18,7 @@ export declare class CloudflareKvStorageAdapter extends BaseStorageAdapter {
18
18
  incr(key: string): Promise<number>;
19
19
  decr(key: string): Promise<number>;
20
20
  incrBy(_key: string, _amount: number): Promise<number>;
21
+ deleteIfEquals(_key: string, _expectedValue: string): Promise<boolean>;
21
22
  protected getPubSubSuggestion(): string;
22
23
  /** Validate + enforce KV's 60s minimum `expirationTtl`. */
23
24
  private toExpirationTtl;
@@ -4,8 +4,8 @@
4
4
  * In-memory storage implementation for development and testing.
5
5
  * Supports TTL, pattern matching, pub/sub via EventEmitter.
6
6
  */
7
+ import type { MemoryAdapterOptions, MessageHandler, SetOptions, Unsubscribe } from '../types';
7
8
  import { BaseStorageAdapter } from './base';
8
- import type { MemoryAdapterOptions, SetOptions, MessageHandler, Unsubscribe } from '../types';
9
9
  /**
10
10
  * In-memory storage adapter.
11
11
  *
@@ -42,6 +42,7 @@ export declare class MemoryStorageAdapter extends BaseStorageAdapter {
42
42
  get(key: string): Promise<string | null>;
43
43
  protected doSet(key: string, value: string, options?: SetOptions): Promise<void>;
44
44
  delete(key: string): Promise<boolean>;
45
+ deleteIfEquals(key: string, expectedValue: string): Promise<boolean>;
45
46
  exists(key: string): Promise<boolean>;
46
47
  expire(key: string, ttlSeconds: number): Promise<boolean>;
47
48
  ttl(key: string): Promise<number | null>;
@@ -55,6 +55,7 @@ export declare class RedisStorageAdapter extends BaseStorageAdapter {
55
55
  get(key: string): Promise<string | null>;
56
56
  protected doSet(key: string, value: string, options?: SetOptions): Promise<void>;
57
57
  delete(key: string): Promise<boolean>;
58
+ deleteIfEquals(key: string, expectedValue: string): Promise<boolean>;
58
59
  exists(key: string): Promise<boolean>;
59
60
  mget(keys: string[]): Promise<(string | null)[]>;
60
61
  mset(entries: import('../types').SetEntry[]): Promise<void>;
@@ -4,8 +4,8 @@
4
4
  * Upstash Redis (REST-based) storage implementation for edge deployment.
5
5
  * Supports pub/sub via Upstash's REST API with polling mechanism.
6
6
  */
7
+ import type { MessageHandler, SetOptions, Unsubscribe, UpstashAdapterOptions } from '../types';
7
8
  import { BaseStorageAdapter } from './base';
8
- import type { UpstashAdapterOptions, SetOptions, MessageHandler, Unsubscribe } from '../types';
9
9
  /**
10
10
  * Upstash Redis storage adapter.
11
11
  *
@@ -52,6 +52,7 @@ export declare class UpstashStorageAdapter extends BaseStorageAdapter {
52
52
  get(key: string): Promise<string | null>;
53
53
  protected doSet(key: string, value: string, options?: SetOptions): Promise<void>;
54
54
  delete(key: string): Promise<boolean>;
55
+ deleteIfEquals(key: string, expectedValue: string): Promise<boolean>;
55
56
  exists(key: string): Promise<boolean>;
56
57
  mget(keys: string[]): Promise<(string | null)[]>;
57
58
  mdelete(keys: string[]): Promise<number>;
@@ -4,8 +4,8 @@
4
4
  * Vercel KV (REST-based) storage implementation for edge deployment.
5
5
  * NOTE: Vercel KV does NOT support pub/sub. Use Upstash adapter instead.
6
6
  */
7
+ import type { SetOptions, VercelKvAdapterOptions } from '../types';
7
8
  import { BaseStorageAdapter } from './base';
8
- import type { VercelKvAdapterOptions, SetOptions } from '../types';
9
9
  /**
10
10
  * Vercel KV storage adapter.
11
11
  *
@@ -47,6 +47,7 @@ export declare class VercelKvStorageAdapter extends BaseStorageAdapter {
47
47
  get(key: string): Promise<string | null>;
48
48
  protected doSet(key: string, value: string, options?: SetOptions): Promise<void>;
49
49
  delete(key: string): Promise<boolean>;
50
+ deleteIfEquals(key: string, expectedValue: string): Promise<boolean>;
50
51
  exists(key: string): Promise<boolean>;
51
52
  mget(keys: string[]): Promise<(string | null)[]>;
52
53
  mdelete(keys: string[]): Promise<number>;
@@ -4,7 +4,7 @@
4
4
  * Wraps a StorageAdapter to provide namespace prefixing.
5
5
  * All operations automatically prefix keys with the namespace path.
6
6
  */
7
- import type { StorageAdapter, NamespacedStorage, SetOptions, SetEntry, MessageHandler, Unsubscribe } from './types';
7
+ import type { MessageHandler, NamespacedStorage, SetEntry, SetOptions, StorageAdapter, Unsubscribe } from './types';
8
8
  /**
9
9
  * Separator used between namespace segments.
10
10
  */
@@ -66,6 +66,7 @@ export declare class NamespacedStorageImpl implements NamespacedStorage {
66
66
  incr(key: string): Promise<number>;
67
67
  decr(key: string): Promise<number>;
68
68
  incrBy(key: string, amount: number): Promise<number>;
69
+ deleteIfEquals(key: string, expectedValue: string): Promise<boolean>;
69
70
  publish(channel: string, message: string): Promise<number>;
70
71
  subscribe(channel: string, handler: MessageHandler): Promise<Unsubscribe>;
71
72
  supportsPubSub(): boolean;
@@ -205,6 +205,15 @@ export interface StorageAdapter {
205
205
  * @throws StorageOperationError if value is not a valid integer
206
206
  */
207
207
  incrBy(key: string, amount: number): Promise<number>;
208
+ /**
209
+ * Atomically delete a key only while it still holds `expectedValue` (compare-and-delete).
210
+ *
211
+ * @param key - Storage key
212
+ * @param expectedValue - The exact value the key must hold
213
+ * @returns true if this call deleted the key, false if it was missing or held another value
214
+ * @throws StorageNotSupportedError if the backend has no atomic compare-and-delete
215
+ */
216
+ deleteIfEquals?(key: string, expectedValue: string): Promise<boolean>;
208
217
  /**
209
218
  * Publish a message to a channel.
210
219
  * Not all adapters support pub/sub (e.g., Vercel KV doesn't).
@@ -267,6 +276,8 @@ export interface NamespacedStorage extends StorageAdapter {
267
276
  * @returns A new NamespacedStorage with extended prefix
268
277
  */
269
278
  namespace(name: string, id?: string): NamespacedStorage;
279
+ /** {@link StorageAdapter.deleteIfEquals}; throws StorageNotSupportedError when the adapter has none. */
280
+ deleteIfEquals(key: string, expectedValue: string): Promise<boolean>;
270
281
  /**
271
282
  * Get the underlying root adapter (for advanced use).
272
283
  * Use with caution - operations bypass namespace prefixing.
@@ -0,0 +1,2 @@
1
+ /** Redis Lua script for `deleteIfEquals`: deletes KEYS[1] only while it holds ARGV[1], returning 1 or 0. */
2
+ export declare const COMPARE_AND_DELETE_SCRIPT = "if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0";