@frontmcp/utils 1.10.16 → 1.11.0-rc.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.
Files changed (135) hide show
  1. package/README.md +1 -0
  2. package/browser/async-context/browser-async-context.mjs +177 -0
  3. package/browser/async-context/index.mjs +15 -0
  4. package/browser/async-context/overlap-error.mjs +10 -0
  5. package/browser/class-brand/class-brand.mjs +27 -0
  6. package/browser/class-brand/index.mjs +6 -0
  7. package/browser/content/content.mjs +151 -0
  8. package/browser/content/index.mjs +8 -0
  9. package/browser/crypto/browser.mjs +103 -0
  10. package/browser/crypto/encrypted-blob.mjs +119 -0
  11. package/browser/crypto/hmac-signing.mjs +70 -0
  12. package/browser/crypto/index.mjs +281 -0
  13. package/browser/crypto/jwt-alg.mjs +40 -0
  14. package/browser/crypto/key-persistence/factory.mjs +134 -0
  15. package/browser/crypto/key-persistence/index.mjs +26 -0
  16. package/browser/crypto/key-persistence/key-persistence.mjs +185 -0
  17. package/browser/crypto/key-persistence/schemas.mjs +82 -0
  18. package/browser/crypto/pkce/index.mjs +25 -0
  19. package/browser/crypto/pkce/pkce.mjs +59 -0
  20. package/browser/crypto/runtime.mjs +17 -0
  21. package/browser/crypto/secret-persistence/index.mjs +29 -0
  22. package/browser/crypto/secret-persistence/persistence.mjs +181 -0
  23. package/browser/crypto/secret-persistence/schema.mjs +40 -0
  24. package/browser/crypto/web-rsa.mjs +36 -0
  25. package/browser/env/browser-env.mjs +56 -0
  26. package/browser/env/browser-runtime-context.mjs +85 -0
  27. package/browser/env/build-target.mjs +25 -0
  28. package/browser/env/edge-scope.mjs +17 -0
  29. package/browser/env/index.mjs +47 -0
  30. package/browser/env/provider.mjs +46 -0
  31. package/browser/escape/escape.mjs +43 -0
  32. package/browser/escape/index.mjs +9 -0
  33. package/browser/event-emitter/browser-event-emitter.mjs +49 -0
  34. package/browser/event-emitter/index.mjs +5 -0
  35. package/browser/fs/fs.mjs +227 -0
  36. package/browser/fs/index.mjs +65 -0
  37. package/browser/http/cookie.mjs +85 -0
  38. package/browser/http/http.mjs +18 -0
  39. package/browser/http/index.mjs +11 -0
  40. package/browser/index.mjs +526 -0
  41. package/browser/llm/index.mjs +5 -0
  42. package/browser/llm/llm-tool-handlers.mjs +55 -0
  43. package/browser/machine-id/index.mjs +7 -0
  44. package/browser/machine-id/machine-id.mjs +108 -0
  45. package/browser/naming/index.mjs +10 -0
  46. package/browser/naming/naming.mjs +68 -0
  47. package/browser/net/index.mjs +10 -0
  48. package/browser/net/ip-address.mjs +128 -0
  49. package/browser/net/redirect.mjs +7 -0
  50. package/browser/path/browser-path.mjs +47 -0
  51. package/browser/path/index.mjs +24 -0
  52. package/browser/path/path.mjs +13 -0
  53. package/browser/process/child-process.mjs +23 -0
  54. package/browser/process/index.mjs +15 -0
  55. package/browser/process/owned-process.mjs +175 -0
  56. package/browser/process/process-tree.mjs +149 -0
  57. package/browser/process/process-watch.mjs +48 -0
  58. package/browser/redaction/index.mjs +6 -0
  59. package/browser/redaction/redaction.mjs +74 -0
  60. package/browser/regex/index.mjs +3 -0
  61. package/browser/regex/patterns.mjs +167 -0
  62. package/browser/regex/safe-regex.mjs +93 -0
  63. package/browser/serialization/index.mjs +5 -0
  64. package/browser/serialization/serialization.mjs +22 -0
  65. package/browser/stdio-bridge/errors.mjs +27 -0
  66. package/browser/stdio-bridge/index.mjs +24 -0
  67. package/browser/stdio-bridge/list-changed.mjs +21 -0
  68. package/browser/stdio-bridge/state-machine.mjs +211 -0
  69. package/browser/stdio-bridge/stdio-framer.mjs +91 -0
  70. package/browser/stdio-bridge/upstream-client.mjs +189 -0
  71. package/browser/storage/adapters/base.mjs +139 -0
  72. package/browser/storage/adapters/cloudflare-kv.mjs +149 -0
  73. package/browser/storage/adapters/filesystem.mjs +279 -0
  74. package/browser/storage/adapters/index.mjs +23 -0
  75. package/browser/storage/adapters/indexeddb.mjs +198 -0
  76. package/browser/storage/adapters/localstorage.mjs +222 -0
  77. package/browser/storage/adapters/memory.mjs +350 -0
  78. package/browser/storage/adapters/redis.mjs +497 -0
  79. package/browser/storage/adapters/upstash.mjs +299 -0
  80. package/browser/storage/adapters/vercel-kv.mjs +226 -0
  81. package/browser/storage/change-feed.mjs +189 -0
  82. package/browser/storage/encrypted-typed-storage.mjs +329 -0
  83. package/browser/storage/errors.mjs +92 -0
  84. package/browser/storage/factory.mjs +155 -0
  85. package/browser/storage/index.mjs +96 -0
  86. package/browser/storage/namespace.mjs +186 -0
  87. package/browser/storage/redis-error-listener.mjs +32 -0
  88. package/browser/storage/redis-url.mjs +66 -0
  89. package/browser/storage/typed-storage.mjs +171 -0
  90. package/browser/storage/utils/compare-and-delete.mjs +9 -0
  91. package/browser/storage/utils/index.mjs +24 -0
  92. package/browser/storage/utils/pattern.mjs +68 -0
  93. package/browser/storage/utils/ttl.mjs +48 -0
  94. package/browser/uri/index.mjs +19 -0
  95. package/browser/uri/uri-template.mjs +86 -0
  96. package/browser/uri/uri-validation.mjs +18 -0
  97. package/crypto/browser.d.ts +1 -13
  98. package/crypto/browser.js +1 -1
  99. package/crypto/node.js +1 -1
  100. package/crypto/web-rsa.d.ts +13 -0
  101. package/env/browser-runtime-context.js +1 -1
  102. package/env/runtime-context.js +1 -1
  103. package/esm/crypto/browser.mjs +1 -1
  104. package/esm/crypto/node.mjs +1 -1
  105. package/esm/env/browser-runtime-context.mjs +1 -1
  106. package/esm/env/runtime-context.mjs +1 -1
  107. package/esm/index.mjs +1 -1
  108. package/fs/fs.d.ts +20 -4
  109. package/fs/index.d.ts +1 -1
  110. package/index.d.ts +9 -3
  111. package/index.js +1 -1
  112. package/package.json +24 -8
  113. package/process/child-process.d.ts +2 -0
  114. package/process/index.d.ts +6 -0
  115. package/process/owned-process.d.ts +59 -0
  116. package/process/process-tree.d.ts +78 -0
  117. package/process/process-watch.d.ts +41 -0
  118. package/redaction/index.d.ts +2 -0
  119. package/redaction/redaction.d.ts +24 -0
  120. package/stdio-bridge/errors.d.ts +23 -0
  121. package/stdio-bridge/index.d.ts +9 -0
  122. package/stdio-bridge/list-changed.d.ts +3 -0
  123. package/stdio-bridge/state-machine.d.ts +61 -0
  124. package/stdio-bridge/stdio-framer.d.ts +49 -0
  125. package/stdio-bridge/types.d.ts +10 -0
  126. package/stdio-bridge/upstream-client.d.ts +66 -0
  127. package/storage/adapters/base.d.ts +2 -0
  128. package/storage/adapters/memory.d.ts +1 -0
  129. package/storage/adapters/redis.d.ts +3 -0
  130. package/storage/change-feed.d.ts +47 -0
  131. package/storage/factory.d.ts +6 -0
  132. package/storage/index.d.ts +3 -1
  133. package/storage/namespace.d.ts +2 -0
  134. package/storage/types.d.ts +46 -1
  135. package/storage/utils/compare-and-delete.d.ts +2 -0
@@ -0,0 +1,186 @@
1
+ // libs/utils/src/storage/namespace.ts
2
+ import { StorageNotSupportedError } from "./errors.mjs";
3
+ var NAMESPACE_SEPARATOR = ":";
4
+ function buildPrefix(name, id) {
5
+ if (id) {
6
+ return `${name}${NAMESPACE_SEPARATOR}${id}${NAMESPACE_SEPARATOR}`;
7
+ }
8
+ return `${name}${NAMESPACE_SEPARATOR}`;
9
+ }
10
+ var NamespacedStorageImpl = class _NamespacedStorageImpl {
11
+ constructor(adapter, prefix = "", root = adapter) {
12
+ this.adapter = adapter;
13
+ this.prefix = prefix;
14
+ this.root = root;
15
+ }
16
+ adapter;
17
+ prefix;
18
+ root;
19
+ // ============================================
20
+ // Key Prefixing Helpers
21
+ // ============================================
22
+ /**
23
+ * Add prefix to a key.
24
+ */
25
+ prefixKey(key) {
26
+ return this.prefix + key;
27
+ }
28
+ /**
29
+ * Remove prefix from a key.
30
+ */
31
+ unprefixKey(key) {
32
+ if (this.prefix && key.startsWith(this.prefix)) {
33
+ return key.slice(this.prefix.length);
34
+ }
35
+ return key;
36
+ }
37
+ /**
38
+ * Add prefix to a pattern (for keys() operation).
39
+ */
40
+ prefixPattern(pattern) {
41
+ return this.prefix + pattern;
42
+ }
43
+ /**
44
+ * Add prefix to a channel (for pub/sub).
45
+ */
46
+ prefixChannel(channel) {
47
+ return this.prefix + channel;
48
+ }
49
+ // ============================================
50
+ // Namespace API
51
+ // ============================================
52
+ namespace(name, id) {
53
+ const newPrefix = this.prefix + buildPrefix(name, id);
54
+ return new _NamespacedStorageImpl(this.adapter, newPrefix, this.root);
55
+ }
56
+ // ============================================
57
+ // Connection Lifecycle (delegated to root)
58
+ // ============================================
59
+ async connect() {
60
+ return this.adapter.connect();
61
+ }
62
+ async disconnect() {
63
+ return this.adapter.disconnect();
64
+ }
65
+ async ping() {
66
+ return this.adapter.ping();
67
+ }
68
+ // ============================================
69
+ // Core Operations (with prefixing)
70
+ // ============================================
71
+ async get(key) {
72
+ return this.adapter.get(this.prefixKey(key));
73
+ }
74
+ async set(key, value, options) {
75
+ return this.adapter.set(this.prefixKey(key), value, options);
76
+ }
77
+ async delete(key) {
78
+ return this.adapter.delete(this.prefixKey(key));
79
+ }
80
+ async exists(key) {
81
+ return this.adapter.exists(this.prefixKey(key));
82
+ }
83
+ // ============================================
84
+ // Batch Operations (with prefixing)
85
+ // ============================================
86
+ async mget(keys) {
87
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
88
+ return this.adapter.mget(prefixedKeys);
89
+ }
90
+ async mset(entries) {
91
+ const prefixedEntries = entries.map((e) => ({
92
+ ...e,
93
+ key: this.prefixKey(e.key)
94
+ }));
95
+ return this.adapter.mset(prefixedEntries);
96
+ }
97
+ async mdelete(keys) {
98
+ const prefixedKeys = keys.map((k) => this.prefixKey(k));
99
+ return this.adapter.mdelete(prefixedKeys);
100
+ }
101
+ // ============================================
102
+ // TTL Operations (with prefixing)
103
+ // ============================================
104
+ async expire(key, ttlSeconds) {
105
+ return this.adapter.expire(this.prefixKey(key), ttlSeconds);
106
+ }
107
+ async ttl(key) {
108
+ return this.adapter.ttl(this.prefixKey(key));
109
+ }
110
+ // ============================================
111
+ // Key Enumeration (with prefixing)
112
+ // ============================================
113
+ async keys(pattern = "*") {
114
+ const prefixedPattern = this.prefixPattern(pattern);
115
+ const keys = await this.adapter.keys(prefixedPattern);
116
+ return keys.map((k) => this.unprefixKey(k));
117
+ }
118
+ async count(pattern = "*") {
119
+ const prefixedPattern = this.prefixPattern(pattern);
120
+ return this.adapter.count(prefixedPattern);
121
+ }
122
+ // ============================================
123
+ // Atomic Operations (with prefixing)
124
+ // ============================================
125
+ async incr(key) {
126
+ return this.adapter.incr(this.prefixKey(key));
127
+ }
128
+ async decr(key) {
129
+ return this.adapter.decr(this.prefixKey(key));
130
+ }
131
+ async incrBy(key, amount) {
132
+ return this.adapter.incrBy(this.prefixKey(key), amount);
133
+ }
134
+ async deleteIfEquals(key, expectedValue) {
135
+ if (!this.adapter.deleteIfEquals) {
136
+ throw new StorageNotSupportedError("deleteIfEquals", "this storage adapter");
137
+ }
138
+ return this.adapter.deleteIfEquals(this.prefixKey(key), expectedValue);
139
+ }
140
+ async expireIfEquals(key, expectedValue, ttlSeconds) {
141
+ if (!this.adapter.expireIfEquals) {
142
+ throw new StorageNotSupportedError("expireIfEquals", "this storage adapter");
143
+ }
144
+ return this.adapter.expireIfEquals(this.prefixKey(key), expectedValue, ttlSeconds);
145
+ }
146
+ async setIfEquals(key, expectedValue, value) {
147
+ if (!this.adapter.setIfEquals) {
148
+ throw new StorageNotSupportedError("setIfEquals", "this storage adapter");
149
+ }
150
+ return this.adapter.setIfEquals(this.prefixKey(key), expectedValue, value);
151
+ }
152
+ // ============================================
153
+ // Pub/Sub (with channel prefixing)
154
+ // ============================================
155
+ async publish(channel, message) {
156
+ return this.adapter.publish(this.prefixChannel(channel), message);
157
+ }
158
+ async subscribe(channel, handler) {
159
+ const prefixedChannel = this.prefixChannel(channel);
160
+ const wrappedHandler = (message, ch) => {
161
+ const unprefixedChannel = ch.startsWith(this.prefix) ? ch.slice(this.prefix.length) : ch;
162
+ handler(message, unprefixedChannel);
163
+ };
164
+ return this.adapter.subscribe(prefixedChannel, wrappedHandler);
165
+ }
166
+ onPubSubReconnect(listener) {
167
+ return this.adapter.onPubSubReconnect?.(listener) ?? (() => void 0);
168
+ }
169
+ supportsPubSub() {
170
+ return this.adapter.supportsPubSub();
171
+ }
172
+ };
173
+ function createRootStorage(adapter) {
174
+ return new NamespacedStorageImpl(adapter, "", adapter);
175
+ }
176
+ function createNamespacedStorage(adapter, prefix) {
177
+ const normalizedPrefix = prefix && !prefix.endsWith(NAMESPACE_SEPARATOR) ? prefix + NAMESPACE_SEPARATOR : prefix;
178
+ return new NamespacedStorageImpl(adapter, normalizedPrefix, adapter);
179
+ }
180
+ export {
181
+ NAMESPACE_SEPARATOR,
182
+ NamespacedStorageImpl,
183
+ buildPrefix,
184
+ createNamespacedStorage,
185
+ createRootStorage
186
+ };
@@ -0,0 +1,32 @@
1
+ // libs/utils/src/storage/redis-error-listener.ts
2
+ var DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS = 3e4;
3
+ function attachRedisErrorListener(client, options = {}) {
4
+ const label = options.label ?? "redis";
5
+ const intervalMs = options.intervalMs ?? DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS;
6
+ let lastLoggedAt = 0;
7
+ let suppressed = 0;
8
+ const listener = (error) => {
9
+ const now = Date.now();
10
+ if (lastLoggedAt !== 0 && now - lastLoggedAt < intervalMs) {
11
+ suppressed++;
12
+ return;
13
+ }
14
+ const extra = suppressed > 0 ? ` (${suppressed} similar error(s) suppressed)` : "";
15
+ suppressed = 0;
16
+ lastLoggedAt = now;
17
+ const message = `[${label}] Redis connection error: ${error?.message ?? String(error)}${extra}`;
18
+ if (options.logger) {
19
+ options.logger.warn(message);
20
+ } else {
21
+ console.warn(message);
22
+ }
23
+ };
24
+ client.on("error", listener);
25
+ return () => {
26
+ client.removeListener("error", listener);
27
+ };
28
+ }
29
+ export {
30
+ DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS,
31
+ attachRedisErrorListener
32
+ };
@@ -0,0 +1,66 @@
1
+ // libs/utils/src/storage/redis-url.ts
2
+ function decodeOrRaw(encoded) {
3
+ try {
4
+ return decodeURIComponent(encoded);
5
+ } catch {
6
+ return encoded;
7
+ }
8
+ }
9
+ function integerOrUndefined(text) {
10
+ const value = Number.parseInt(text ?? "", 10);
11
+ return Number.isNaN(value) ? void 0 : value;
12
+ }
13
+ function readRedisUrl(rawUrl) {
14
+ let url;
15
+ try {
16
+ url = new URL(rawUrl);
17
+ } catch {
18
+ return void 0;
19
+ }
20
+ if (url.protocol !== "redis:" && url.protocol !== "rediss:") return void 0;
21
+ const query = Object.fromEntries(url.searchParams);
22
+ const { host, port, username, password, db, ...otherOptions } = query;
23
+ const queryOptions = {};
24
+ for (const [name, value = ""] of Object.entries(otherOptions)) {
25
+ queryOptions[name] = name === "family" ? integerOrUndefined(value) ?? value : value;
26
+ }
27
+ return {
28
+ host: url.hostname.replace(/^\[(.*)\]$/, "$1") || host,
29
+ port: integerOrUndefined(url.port || port),
30
+ username: decodeOrRaw(url.username) || username || void 0,
31
+ password: decodeOrRaw(url.password) || password || void 0,
32
+ db: integerOrUndefined(url.pathname.replace(/^\/+/, "") || db),
33
+ tls: url.protocol === "rediss:",
34
+ queryOptions
35
+ };
36
+ }
37
+ function mergeRedisUrlFields(rawUrl, fields) {
38
+ const connection = readRedisUrl(rawUrl);
39
+ if (!connection) return void 0;
40
+ const fillIns = {};
41
+ const conflicts = [];
42
+ if (fields.host !== void 0 && fields.host !== connection.host) conflicts.push("host");
43
+ if (fields.port !== void 0) {
44
+ if (connection.port === void 0) fillIns.port = fields.port;
45
+ else if (fields.port !== connection.port) conflicts.push("port");
46
+ }
47
+ if (fields.password !== void 0) {
48
+ if (connection.password === void 0) fillIns.password = fields.password;
49
+ else if (fields.password !== connection.password) conflicts.push("password");
50
+ }
51
+ if (fields.db !== void 0) {
52
+ if (connection.db === void 0) fillIns.db = fields.db;
53
+ else if (fields.db !== connection.db) conflicts.push("db");
54
+ }
55
+ if (fields.tls === true && !connection.tls) fillIns.tls = true;
56
+ if (fields.tls === false && connection.tls) conflicts.push("tls");
57
+ return { connection, fillIns, conflicts };
58
+ }
59
+ function describeRedisUrlConflicts(conflicts) {
60
+ return `redis ${conflicts.join(", ")} contradict${conflicts.length === 1 ? "s" : ""} redis.url. Fields beside a url only fill in what the URL leaves out (port, password, db, tls); put the value in the URL or drop the field.`;
61
+ }
62
+ export {
63
+ describeRedisUrlConflicts,
64
+ mergeRedisUrlFields,
65
+ readRedisUrl
66
+ };
@@ -0,0 +1,171 @@
1
+ // libs/utils/src/storage/typed-storage.ts
2
+ var TypedStorage = class {
3
+ storage;
4
+ serialize;
5
+ deserialize;
6
+ schema;
7
+ throwOnInvalid;
8
+ constructor(storage, options = {}) {
9
+ this.storage = storage;
10
+ this.serialize = options.serialize ?? JSON.stringify;
11
+ this.deserialize = options.deserialize ?? JSON.parse;
12
+ this.schema = options.schema;
13
+ this.throwOnInvalid = options.throwOnInvalid ?? false;
14
+ }
15
+ /**
16
+ * Get a typed value by key.
17
+ *
18
+ * @param key - Storage key
19
+ * @returns The typed value, or null if not found or invalid
20
+ */
21
+ async get(key) {
22
+ const raw = await this.storage.get(key);
23
+ if (raw === null) {
24
+ return null;
25
+ }
26
+ return this.parseValue(raw, key);
27
+ }
28
+ /**
29
+ * Set a typed value with optional TTL.
30
+ *
31
+ * @param key - Storage key
32
+ * @param value - Typed value to store
33
+ * @param options - Optional TTL and conditional flags
34
+ */
35
+ async set(key, value, options) {
36
+ const serialized = this.serialize(value);
37
+ await this.storage.set(key, serialized, options);
38
+ }
39
+ /**
40
+ * Delete a key.
41
+ *
42
+ * @param key - Storage key
43
+ * @returns true if key existed and was deleted
44
+ */
45
+ async delete(key) {
46
+ return this.storage.delete(key);
47
+ }
48
+ /**
49
+ * Check if a key exists.
50
+ *
51
+ * @param key - Storage key
52
+ * @returns true if key exists
53
+ */
54
+ async exists(key) {
55
+ return this.storage.exists(key);
56
+ }
57
+ /**
58
+ * Get multiple typed values.
59
+ *
60
+ * @param keys - Array of storage keys
61
+ * @returns Array of typed values (null for missing/invalid keys)
62
+ */
63
+ async mget(keys) {
64
+ if (keys.length === 0) {
65
+ return [];
66
+ }
67
+ const rawValues = await this.storage.mget(keys);
68
+ return rawValues.map((raw, index) => {
69
+ if (raw === null) {
70
+ return null;
71
+ }
72
+ return this.parseValue(raw, keys[index]);
73
+ });
74
+ }
75
+ /**
76
+ * Set multiple typed values.
77
+ *
78
+ * @param entries - Array of key-value-options entries
79
+ */
80
+ async mset(entries) {
81
+ if (entries.length === 0) {
82
+ return;
83
+ }
84
+ const rawEntries = entries.map((entry) => ({
85
+ key: entry.key,
86
+ value: this.serialize(entry.value),
87
+ options: entry.options
88
+ }));
89
+ await this.storage.mset(rawEntries);
90
+ }
91
+ /**
92
+ * Delete multiple keys.
93
+ *
94
+ * @param keys - Array of storage keys
95
+ * @returns Number of keys actually deleted
96
+ */
97
+ async mdelete(keys) {
98
+ return this.storage.mdelete(keys);
99
+ }
100
+ /**
101
+ * Update TTL on an existing key.
102
+ *
103
+ * @param key - Storage key
104
+ * @param ttlSeconds - New TTL in seconds
105
+ * @returns true if key exists and TTL was set
106
+ */
107
+ async expire(key, ttlSeconds) {
108
+ return this.storage.expire(key, ttlSeconds);
109
+ }
110
+ /**
111
+ * Get remaining TTL for a key.
112
+ *
113
+ * @param key - Storage key
114
+ * @returns TTL in seconds, -1 if no TTL, or null if key doesn't exist
115
+ */
116
+ async ttl(key) {
117
+ return this.storage.ttl(key);
118
+ }
119
+ /**
120
+ * List keys matching a pattern.
121
+ *
122
+ * @param pattern - Glob pattern (default: '*' for all keys)
123
+ * @returns Array of matching keys
124
+ */
125
+ async keys(pattern) {
126
+ return this.storage.keys(pattern);
127
+ }
128
+ /**
129
+ * Count keys matching a pattern.
130
+ *
131
+ * @param pattern - Glob pattern (default: '*' for all keys)
132
+ * @returns Number of matching keys
133
+ */
134
+ async count(pattern) {
135
+ return this.storage.count(pattern);
136
+ }
137
+ /**
138
+ * Get the underlying storage adapter.
139
+ * Use with caution - operations bypass type safety.
140
+ */
141
+ get raw() {
142
+ return this.storage;
143
+ }
144
+ /**
145
+ * Parse and validate a raw value.
146
+ */
147
+ parseValue(raw, key) {
148
+ try {
149
+ const parsed = this.deserialize(raw);
150
+ if (this.schema) {
151
+ const result = this.schema.safeParse(parsed);
152
+ if (!result.success) {
153
+ if (this.throwOnInvalid) {
154
+ throw new Error(`TypedStorage validation failed for key "${key}": ${result.error.message}`);
155
+ }
156
+ return null;
157
+ }
158
+ return result.data;
159
+ }
160
+ return parsed;
161
+ } catch (error) {
162
+ if (this.throwOnInvalid) {
163
+ throw error;
164
+ }
165
+ return null;
166
+ }
167
+ }
168
+ };
169
+ export {
170
+ TypedStorage
171
+ };
@@ -0,0 +1,9 @@
1
+ // libs/utils/src/storage/utils/compare-and-delete.ts
2
+ var COMPARE_AND_DELETE_SCRIPT = `if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) end return 0`;
3
+ var COMPARE_AND_EXPIRE_SCRIPT = `if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('EXPIRE', KEYS[1], ARGV[2]) end return 0`;
4
+ var COMPARE_AND_SET_SCRIPT = `if redis.call('GET', KEYS[1]) == ARGV[1] then redis.call('SET', KEYS[1], ARGV[2]) return 1 end return 0`;
5
+ export {
6
+ COMPARE_AND_DELETE_SCRIPT,
7
+ COMPARE_AND_EXPIRE_SCRIPT,
8
+ COMPARE_AND_SET_SCRIPT
9
+ };
@@ -0,0 +1,24 @@
1
+ // libs/utils/src/storage/utils/index.ts
2
+ import { globToRegex, matchesPattern, validatePattern, escapeGlob } from "./pattern.mjs";
3
+ import {
4
+ MAX_TTL_SECONDS,
5
+ validateTTL,
6
+ validateOptionalTTL,
7
+ ttlToExpiresAt,
8
+ expiresAtToTTL,
9
+ isExpired,
10
+ normalizeTTL
11
+ } from "./ttl.mjs";
12
+ export {
13
+ MAX_TTL_SECONDS,
14
+ escapeGlob,
15
+ expiresAtToTTL,
16
+ globToRegex,
17
+ isExpired,
18
+ matchesPattern,
19
+ normalizeTTL,
20
+ ttlToExpiresAt,
21
+ validateOptionalTTL,
22
+ validatePattern,
23
+ validateTTL
24
+ };
@@ -0,0 +1,68 @@
1
+ // libs/utils/src/storage/utils/pattern.ts
2
+ import { StoragePatternError } from "../errors.mjs";
3
+ var MAX_PATTERN_LENGTH = 500;
4
+ var MAX_WILDCARDS = 20;
5
+ var REGEX_SPECIAL_CHARS = /[.+^${}()|[\]\\]/g;
6
+ function globToRegex(pattern) {
7
+ if (pattern.length > MAX_PATTERN_LENGTH) {
8
+ throw new StoragePatternError(
9
+ pattern.substring(0, 50) + "...",
10
+ `Pattern exceeds maximum length of ${MAX_PATTERN_LENGTH} characters`
11
+ );
12
+ }
13
+ const wildcardCount = (pattern.match(/[*?]/g) || []).length;
14
+ if (wildcardCount > MAX_WILDCARDS) {
15
+ throw new StoragePatternError(pattern, `Pattern has too many wildcards (max: ${MAX_WILDCARDS})`);
16
+ }
17
+ if (pattern === "" || pattern === "*") {
18
+ return /^.*$/;
19
+ }
20
+ let regexStr = "^";
21
+ let prevChar = "";
22
+ for (let i = 0; i < pattern.length; i++) {
23
+ const char = pattern[i];
24
+ switch (char) {
25
+ case "*":
26
+ if (prevChar !== "*") {
27
+ regexStr += ".*";
28
+ }
29
+ break;
30
+ case "?":
31
+ regexStr += ".";
32
+ break;
33
+ default:
34
+ regexStr += char.replace(REGEX_SPECIAL_CHARS, "\\$&");
35
+ }
36
+ prevChar = char;
37
+ }
38
+ regexStr += "$";
39
+ try {
40
+ return new RegExp(regexStr);
41
+ } catch {
42
+ throw new StoragePatternError(pattern, "Failed to compile pattern to regex");
43
+ }
44
+ }
45
+ function matchesPattern(key, pattern) {
46
+ const regex = globToRegex(pattern);
47
+ return regex.test(key);
48
+ }
49
+ function validatePattern(pattern) {
50
+ try {
51
+ globToRegex(pattern);
52
+ return { valid: true };
53
+ } catch (e) {
54
+ return {
55
+ valid: false,
56
+ error: e instanceof Error ? e.message : "Invalid pattern"
57
+ };
58
+ }
59
+ }
60
+ function escapeGlob(literal) {
61
+ return literal.replace(/[*?\\]/g, "\\$&");
62
+ }
63
+ export {
64
+ escapeGlob,
65
+ globToRegex,
66
+ matchesPattern,
67
+ validatePattern
68
+ };
@@ -0,0 +1,48 @@
1
+ // libs/utils/src/storage/utils/ttl.ts
2
+ import { StorageTTLError } from "../errors.mjs";
3
+ var MAX_TTL_SECONDS = 31536e5;
4
+ function validateTTL(ttlSeconds) {
5
+ if (typeof ttlSeconds !== "number") {
6
+ throw new StorageTTLError(ttlSeconds, `TTL must be a number, got ${typeof ttlSeconds}`);
7
+ }
8
+ if (!Number.isFinite(ttlSeconds)) {
9
+ throw new StorageTTLError(ttlSeconds, "TTL must be a finite number");
10
+ }
11
+ if (!Number.isInteger(ttlSeconds)) {
12
+ throw new StorageTTLError(ttlSeconds, `TTL must be an integer, got ${ttlSeconds}`);
13
+ }
14
+ if (ttlSeconds <= 0) {
15
+ throw new StorageTTLError(ttlSeconds, `TTL must be positive, got ${ttlSeconds}`);
16
+ }
17
+ if (ttlSeconds > MAX_TTL_SECONDS) {
18
+ throw new StorageTTLError(ttlSeconds, `TTL exceeds maximum of ${MAX_TTL_SECONDS} seconds`);
19
+ }
20
+ }
21
+ function validateOptionalTTL(ttlSeconds) {
22
+ if (ttlSeconds !== void 0) {
23
+ validateTTL(ttlSeconds);
24
+ }
25
+ }
26
+ function ttlToExpiresAt(ttlSeconds) {
27
+ return Date.now() + ttlSeconds * 1e3;
28
+ }
29
+ function expiresAtToTTL(expiresAt) {
30
+ const remaining = Math.ceil((expiresAt - Date.now()) / 1e3);
31
+ return Math.max(0, remaining);
32
+ }
33
+ function isExpired(expiresAt) {
34
+ if (expiresAt === void 0) return false;
35
+ return Date.now() >= expiresAt;
36
+ }
37
+ function normalizeTTL(ttl, unit) {
38
+ return unit === "milliseconds" ? Math.ceil(ttl / 1e3) : ttl;
39
+ }
40
+ export {
41
+ MAX_TTL_SECONDS,
42
+ expiresAtToTTL,
43
+ isExpired,
44
+ normalizeTTL,
45
+ ttlToExpiresAt,
46
+ validateOptionalTTL,
47
+ validateTTL
48
+ };
@@ -0,0 +1,19 @@
1
+ // libs/utils/src/uri/index.ts
2
+ import { isValidMcpUri, extractUriScheme, isValidMcpUriTemplate } from "./uri-validation.mjs";
3
+ import {
4
+ parseUriTemplate,
5
+ matchUriTemplate,
6
+ expandUriTemplate,
7
+ extractTemplateParams,
8
+ isUriTemplate
9
+ } from "./uri-template.mjs";
10
+ export {
11
+ expandUriTemplate,
12
+ extractTemplateParams,
13
+ extractUriScheme,
14
+ isUriTemplate,
15
+ isValidMcpUri,
16
+ isValidMcpUriTemplate,
17
+ matchUriTemplate,
18
+ parseUriTemplate
19
+ };