@frontmcp/utils 1.9.1 → 1.9.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.
- package/env/browser-env.d.ts +6 -0
- package/env/browser-env.js +12 -3
- package/env/browser-runtime-context.d.ts +4 -0
- package/env/browser-runtime-context.js +29 -3
- package/esm/env/browser-env.mjs +12 -3
- package/esm/env/browser-runtime-context.mjs +29 -3
- package/esm/index.mjs +99 -17
- package/index.d.ts +2 -2
- package/index.js +102 -17
- package/package.json +2 -2
- package/storage/adapters/index.d.ts +1 -1
- package/storage/adapters/redis.d.ts +2 -0
- package/storage/adapters/vercel-kv.d.ts +41 -0
- package/storage/index.d.ts +5 -3
- package/storage/redis-url.d.ts +28 -0
- package/storage/types.d.ts +31 -9
package/env/browser-env.d.ts
CHANGED
|
@@ -7,6 +7,12 @@
|
|
|
7
7
|
export declare function getEnv(_key: string): string | undefined;
|
|
8
8
|
export declare function getEnv(_key: string, defaultValue: string): string;
|
|
9
9
|
export declare function getCwd(): string;
|
|
10
|
+
/**
|
|
11
|
+
* The NODE_ENV the page runs with: a `process` shim's live value first, then the value a
|
|
12
|
+
* bundler inlined for `process.env.NODE_ENV`, else none. `isProduction()`, `isDevelopment()`
|
|
13
|
+
* and the runtime context's `env` all read it (#770). As on Node, with no NODE_ENV the
|
|
14
|
+
* runtime context reports `development` while `isDevelopment()` stays false.
|
|
15
|
+
*/
|
|
10
16
|
export declare function getNodeEnv(): string | undefined;
|
|
11
17
|
export declare function isProduction(): boolean;
|
|
12
18
|
export declare function isDevelopment(): boolean;
|
package/env/browser-env.js
CHANGED
|
@@ -50,6 +50,14 @@ function isBrowserWorkerScope() {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
// libs/utils/src/env/browser-env.ts
|
|
53
|
+
var NODE_ENV_KEY = "NODE_ENV";
|
|
54
|
+
function bundlerDefinedNodeEnv() {
|
|
55
|
+
try {
|
|
56
|
+
return process.env["NODE_ENV"] || void 0;
|
|
57
|
+
} catch {
|
|
58
|
+
return void 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
53
61
|
function getEnv(_key, defaultValue) {
|
|
54
62
|
return defaultValue;
|
|
55
63
|
}
|
|
@@ -57,13 +65,14 @@ function getCwd() {
|
|
|
57
65
|
return "/";
|
|
58
66
|
}
|
|
59
67
|
function getNodeEnv() {
|
|
60
|
-
|
|
68
|
+
const shim = globalThis.process;
|
|
69
|
+
return shim?.env?.[NODE_ENV_KEY] || bundlerDefinedNodeEnv();
|
|
61
70
|
}
|
|
62
71
|
function isProduction() {
|
|
63
|
-
return
|
|
72
|
+
return getNodeEnv() === "production";
|
|
64
73
|
}
|
|
65
74
|
function isDevelopment() {
|
|
66
|
-
return
|
|
75
|
+
return getNodeEnv() === "development";
|
|
67
76
|
}
|
|
68
77
|
function getEnvFlag(_key) {
|
|
69
78
|
return false;
|
|
@@ -57,5 +57,9 @@ export declare function checkEntryAvailability(availability: EntryAvailability |
|
|
|
57
57
|
missingAxes: (keyof EntryAvailability)[];
|
|
58
58
|
};
|
|
59
59
|
export declare function detectRuntimeContext(): RuntimeContext;
|
|
60
|
+
/**
|
|
61
|
+
* The browser runtime context. Its `env` is read live from the same NODE_ENV as
|
|
62
|
+
* `isProduction()`, defaulting to `development` as on Node (#770).
|
|
63
|
+
*/
|
|
60
64
|
export declare function getRuntimeContext(): RuntimeContext;
|
|
61
65
|
export declare function resetRuntimeContext(): void;
|
|
@@ -29,6 +29,22 @@ __export(browser_runtime_context_exports, {
|
|
|
29
29
|
});
|
|
30
30
|
module.exports = __toCommonJS(browser_runtime_context_exports);
|
|
31
31
|
var import_lazy_zod = require("@frontmcp/lazy-zod");
|
|
32
|
+
|
|
33
|
+
// libs/utils/src/env/browser-env.ts
|
|
34
|
+
var NODE_ENV_KEY = "NODE_ENV";
|
|
35
|
+
function bundlerDefinedNodeEnv() {
|
|
36
|
+
try {
|
|
37
|
+
return process.env["NODE_ENV"] || void 0;
|
|
38
|
+
} catch {
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function getNodeEnv() {
|
|
43
|
+
const shim = globalThis.process;
|
|
44
|
+
return shim?.env?.[NODE_ENV_KEY] || bundlerDefinedNodeEnv();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// libs/utils/src/env/browser-runtime-context.ts
|
|
32
48
|
var entryAvailabilitySchema = import_lazy_zod.z.object({
|
|
33
49
|
os: import_lazy_zod.z.array(import_lazy_zod.z.string().min(1)).optional(),
|
|
34
50
|
platform: import_lazy_zod.z.array(import_lazy_zod.z.string().min(1)).optional(),
|
|
@@ -84,14 +100,24 @@ function detectRuntimeContext() {
|
|
|
84
100
|
deployment: "standalone",
|
|
85
101
|
provider: "bare",
|
|
86
102
|
target: "browser",
|
|
87
|
-
env: "
|
|
103
|
+
env: getNodeEnv() || "development"
|
|
88
104
|
};
|
|
89
105
|
}
|
|
90
|
-
var
|
|
106
|
+
var cached;
|
|
91
107
|
function getRuntimeContext() {
|
|
92
|
-
|
|
108
|
+
if (!cached) {
|
|
109
|
+
const detected = detectRuntimeContext();
|
|
110
|
+
Object.defineProperty(detected, "env", {
|
|
111
|
+
get: () => getNodeEnv() || "development",
|
|
112
|
+
enumerable: true,
|
|
113
|
+
configurable: true
|
|
114
|
+
});
|
|
115
|
+
cached = detected;
|
|
116
|
+
}
|
|
117
|
+
return cached;
|
|
93
118
|
}
|
|
94
119
|
function resetRuntimeContext() {
|
|
120
|
+
cached = void 0;
|
|
95
121
|
}
|
|
96
122
|
// Annotate the CommonJS export names for ESM import in node:
|
|
97
123
|
0 && (module.exports = {
|
package/esm/env/browser-env.mjs
CHANGED
|
@@ -17,6 +17,14 @@ function isBrowserWorkerScope() {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
// libs/utils/src/env/browser-env.ts
|
|
20
|
+
var NODE_ENV_KEY = "NODE_ENV";
|
|
21
|
+
function bundlerDefinedNodeEnv() {
|
|
22
|
+
try {
|
|
23
|
+
return process.env["NODE_ENV"] || void 0;
|
|
24
|
+
} catch {
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
20
28
|
function getEnv(_key, defaultValue) {
|
|
21
29
|
return defaultValue;
|
|
22
30
|
}
|
|
@@ -24,13 +32,14 @@ function getCwd() {
|
|
|
24
32
|
return "/";
|
|
25
33
|
}
|
|
26
34
|
function getNodeEnv() {
|
|
27
|
-
|
|
35
|
+
const shim = globalThis.process;
|
|
36
|
+
return shim?.env?.[NODE_ENV_KEY] || bundlerDefinedNodeEnv();
|
|
28
37
|
}
|
|
29
38
|
function isProduction() {
|
|
30
|
-
return
|
|
39
|
+
return getNodeEnv() === "production";
|
|
31
40
|
}
|
|
32
41
|
function isDevelopment() {
|
|
33
|
-
return
|
|
42
|
+
return getNodeEnv() === "development";
|
|
34
43
|
}
|
|
35
44
|
function getEnvFlag(_key) {
|
|
36
45
|
return false;
|
|
@@ -3,6 +3,22 @@ const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.ur
|
|
|
3
3
|
// __frontmcpCreateRequire
|
|
4
4
|
// libs/utils/src/env/browser-runtime-context.ts
|
|
5
5
|
import { z } from "@frontmcp/lazy-zod";
|
|
6
|
+
|
|
7
|
+
// libs/utils/src/env/browser-env.ts
|
|
8
|
+
var NODE_ENV_KEY = "NODE_ENV";
|
|
9
|
+
function bundlerDefinedNodeEnv() {
|
|
10
|
+
try {
|
|
11
|
+
return process.env["NODE_ENV"] || void 0;
|
|
12
|
+
} catch {
|
|
13
|
+
return void 0;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function getNodeEnv() {
|
|
17
|
+
const shim = globalThis.process;
|
|
18
|
+
return shim?.env?.[NODE_ENV_KEY] || bundlerDefinedNodeEnv();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// libs/utils/src/env/browser-runtime-context.ts
|
|
6
22
|
var entryAvailabilitySchema = z.object({
|
|
7
23
|
os: z.array(z.string().min(1)).optional(),
|
|
8
24
|
platform: z.array(z.string().min(1)).optional(),
|
|
@@ -58,14 +74,24 @@ function detectRuntimeContext() {
|
|
|
58
74
|
deployment: "standalone",
|
|
59
75
|
provider: "bare",
|
|
60
76
|
target: "browser",
|
|
61
|
-
env: "
|
|
77
|
+
env: getNodeEnv() || "development"
|
|
62
78
|
};
|
|
63
79
|
}
|
|
64
|
-
var
|
|
80
|
+
var cached;
|
|
65
81
|
function getRuntimeContext() {
|
|
66
|
-
|
|
82
|
+
if (!cached) {
|
|
83
|
+
const detected = detectRuntimeContext();
|
|
84
|
+
Object.defineProperty(detected, "env", {
|
|
85
|
+
get: () => getNodeEnv() || "development",
|
|
86
|
+
enumerable: true,
|
|
87
|
+
configurable: true
|
|
88
|
+
});
|
|
89
|
+
cached = detected;
|
|
90
|
+
}
|
|
91
|
+
return cached;
|
|
67
92
|
}
|
|
68
93
|
function resetRuntimeContext() {
|
|
94
|
+
cached = void 0;
|
|
69
95
|
}
|
|
70
96
|
export {
|
|
71
97
|
checkEntryAvailability,
|
package/esm/index.mjs
CHANGED
|
@@ -3015,6 +3015,55 @@ var init_redis_error_listener = __esm({
|
|
|
3015
3015
|
}
|
|
3016
3016
|
});
|
|
3017
3017
|
|
|
3018
|
+
// libs/utils/src/storage/redis-url.ts
|
|
3019
|
+
function decodeOrRaw(encoded) {
|
|
3020
|
+
try {
|
|
3021
|
+
return decodeURIComponent(encoded);
|
|
3022
|
+
} catch {
|
|
3023
|
+
return encoded;
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
function mergeRedisUrlFields(rawUrl, fields) {
|
|
3027
|
+
let url;
|
|
3028
|
+
try {
|
|
3029
|
+
url = new URL(rawUrl);
|
|
3030
|
+
} catch {
|
|
3031
|
+
return void 0;
|
|
3032
|
+
}
|
|
3033
|
+
const urlHost = url.hostname.replace(/^\[(.*)\]$/, "$1");
|
|
3034
|
+
const urlPort = url.port ? Number(url.port) : void 0;
|
|
3035
|
+
const urlPassword = url.password ? decodeOrRaw(url.password) : void 0;
|
|
3036
|
+
const urlDbText = url.pathname.replace(/^\/+/, "") || url.searchParams.get("db") || void 0;
|
|
3037
|
+
const urlDb = urlDbText === void 0 ? void 0 : Number(urlDbText);
|
|
3038
|
+
const urlUsesTls = url.protocol === "rediss:";
|
|
3039
|
+
const fillIns = {};
|
|
3040
|
+
const conflicts = [];
|
|
3041
|
+
if (fields.host !== void 0 && fields.host !== urlHost) conflicts.push("host");
|
|
3042
|
+
if (fields.port !== void 0) {
|
|
3043
|
+
if (urlPort === void 0) fillIns.port = fields.port;
|
|
3044
|
+
else if (fields.port !== urlPort) conflicts.push("port");
|
|
3045
|
+
}
|
|
3046
|
+
if (fields.password !== void 0) {
|
|
3047
|
+
if (urlPassword === void 0) fillIns.password = fields.password;
|
|
3048
|
+
else if (fields.password !== urlPassword) conflicts.push("password");
|
|
3049
|
+
}
|
|
3050
|
+
if (fields.db !== void 0) {
|
|
3051
|
+
if (urlDb === void 0) fillIns.db = fields.db;
|
|
3052
|
+
else if (fields.db !== urlDb) conflicts.push("db");
|
|
3053
|
+
}
|
|
3054
|
+
if (fields.tls === true && !urlUsesTls) fillIns.tls = true;
|
|
3055
|
+
if (fields.tls === false && urlUsesTls) conflicts.push("tls");
|
|
3056
|
+
return { fillIns, conflicts };
|
|
3057
|
+
}
|
|
3058
|
+
function describeRedisUrlConflicts(conflicts) {
|
|
3059
|
+
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.`;
|
|
3060
|
+
}
|
|
3061
|
+
var init_redis_url = __esm({
|
|
3062
|
+
"libs/utils/src/storage/redis-url.ts"() {
|
|
3063
|
+
"use strict";
|
|
3064
|
+
}
|
|
3065
|
+
});
|
|
3066
|
+
|
|
3018
3067
|
// libs/utils/src/storage/utils/index.ts
|
|
3019
3068
|
var init_utils = __esm({
|
|
3020
3069
|
"libs/utils/src/storage/utils/index.ts"() {
|
|
@@ -3054,6 +3103,17 @@ function getRedisClass() {
|
|
|
3054
3103
|
});
|
|
3055
3104
|
}
|
|
3056
3105
|
}
|
|
3106
|
+
function resolveUrlFillIns(connectionUrl, options) {
|
|
3107
|
+
const { url: configUrl, ...siblingFields } = { url: void 0, ...options.config };
|
|
3108
|
+
if (options.url && configUrl && configUrl !== options.url) {
|
|
3109
|
+
throw new StorageConfigError("redis", "redis.url and redis.config.url name different servers; set one of them.");
|
|
3110
|
+
}
|
|
3111
|
+
const merge = mergeRedisUrlFields(connectionUrl, siblingFields);
|
|
3112
|
+
if (merge && merge.conflicts.length > 0) {
|
|
3113
|
+
throw new StorageConfigError("redis", describeRedisUrlConflicts(merge.conflicts));
|
|
3114
|
+
}
|
|
3115
|
+
return merge?.fillIns ?? {};
|
|
3116
|
+
}
|
|
3057
3117
|
function createRedisClient(options) {
|
|
3058
3118
|
const RedisClass = getRedisClass();
|
|
3059
3119
|
const baseOptions = { lazyConnect: false, maxRetriesPerRequest: 3 };
|
|
@@ -3074,6 +3134,7 @@ var init_redis = __esm({
|
|
|
3074
3134
|
"use strict";
|
|
3075
3135
|
init_errors();
|
|
3076
3136
|
init_redis_error_listener();
|
|
3137
|
+
init_redis_url();
|
|
3077
3138
|
init_utils();
|
|
3078
3139
|
init_compare_and_delete();
|
|
3079
3140
|
init_base();
|
|
@@ -3087,6 +3148,8 @@ var init_redis = __esm({
|
|
|
3087
3148
|
connecting;
|
|
3088
3149
|
keyPrefix;
|
|
3089
3150
|
subscriptionHandlers = /* @__PURE__ */ new Map();
|
|
3151
|
+
connectionUrl;
|
|
3152
|
+
urlFillIns;
|
|
3090
3153
|
constructor(options = {}) {
|
|
3091
3154
|
super();
|
|
3092
3155
|
const hasClient = options.client !== void 0;
|
|
@@ -3108,6 +3171,9 @@ var init_redis = __esm({
|
|
|
3108
3171
|
this.options = options;
|
|
3109
3172
|
this.ownsClient = !hasClient;
|
|
3110
3173
|
this.keyPrefix = options.keyPrefix ?? "";
|
|
3174
|
+
const configUrl = options.config?.url;
|
|
3175
|
+
this.connectionUrl = options.url ?? (typeof configUrl === "string" ? configUrl : void 0);
|
|
3176
|
+
this.urlFillIns = this.connectionUrl ? resolveUrlFillIns(this.connectionUrl, options) : {};
|
|
3111
3177
|
}
|
|
3112
3178
|
// ============================================
|
|
3113
3179
|
// Connection Lifecycle
|
|
@@ -3130,8 +3196,8 @@ var init_redis = __esm({
|
|
|
3130
3196
|
client = this.options.client;
|
|
3131
3197
|
} else {
|
|
3132
3198
|
const RedisClass = getRedisClass();
|
|
3133
|
-
if (this.
|
|
3134
|
-
client = new RedisClass(this.
|
|
3199
|
+
if (this.connectionUrl) {
|
|
3200
|
+
client = new RedisClass(this.connectionUrl, this.buildRedisOptions());
|
|
3135
3201
|
} else {
|
|
3136
3202
|
client = new RedisClass(this.buildRedisOptions());
|
|
3137
3203
|
}
|
|
@@ -3367,14 +3433,19 @@ var init_redis = __esm({
|
|
|
3367
3433
|
* Build Redis options from config.
|
|
3368
3434
|
*/
|
|
3369
3435
|
buildRedisOptions() {
|
|
3370
|
-
if (this.
|
|
3436
|
+
if (this.connectionUrl) {
|
|
3437
|
+
const { port, password, db, tls } = this.urlFillIns;
|
|
3371
3438
|
return {
|
|
3439
|
+
...port !== void 0 ? { port } : {},
|
|
3440
|
+
...password !== void 0 ? { password } : {},
|
|
3441
|
+
...db !== void 0 ? { db } : {},
|
|
3442
|
+
...tls ? { tls: {} } : {},
|
|
3372
3443
|
lazyConnect: false,
|
|
3373
3444
|
maxRetriesPerRequest: 3
|
|
3374
3445
|
};
|
|
3375
3446
|
}
|
|
3376
3447
|
const config = this.options.config;
|
|
3377
|
-
if (!config) {
|
|
3448
|
+
if (!config || typeof config.url === "string" || config.host === void 0) {
|
|
3378
3449
|
throw new StorageConfigError("redis", "Redis config is required when URL is not provided");
|
|
3379
3450
|
}
|
|
3380
3451
|
return {
|
|
@@ -3393,8 +3464,8 @@ var init_redis = __esm({
|
|
|
3393
3464
|
async createSubscriber() {
|
|
3394
3465
|
const RedisClass = getRedisClass();
|
|
3395
3466
|
let subscriber;
|
|
3396
|
-
if (this.
|
|
3397
|
-
subscriber = new RedisClass(this.
|
|
3467
|
+
if (this.connectionUrl) {
|
|
3468
|
+
subscriber = new RedisClass(this.connectionUrl, this.buildRedisOptions());
|
|
3398
3469
|
} else if (this.options.config) {
|
|
3399
3470
|
subscriber = new RedisClass(this.buildRedisOptions());
|
|
3400
3471
|
} else if (this.options.client) {
|
|
@@ -3445,7 +3516,8 @@ var init_redis = __esm({
|
|
|
3445
3516
|
// libs/utils/src/storage/adapters/vercel-kv.ts
|
|
3446
3517
|
var vercel_kv_exports = {};
|
|
3447
3518
|
__export(vercel_kv_exports, {
|
|
3448
|
-
VercelKvStorageAdapter: () => VercelKvStorageAdapter
|
|
3519
|
+
VercelKvStorageAdapter: () => VercelKvStorageAdapter,
|
|
3520
|
+
createVercelKvClient: () => createVercelKvClient
|
|
3449
3521
|
});
|
|
3450
3522
|
async function loadVercelKv() {
|
|
3451
3523
|
try {
|
|
@@ -3457,6 +3529,15 @@ async function loadVercelKv() {
|
|
|
3457
3529
|
throw new Error("@vercel/kv is required for Vercel KV storage adapter. Install it with: npm install @vercel/kv");
|
|
3458
3530
|
}
|
|
3459
3531
|
}
|
|
3532
|
+
async function createVercelKvClient(connection = {}) {
|
|
3533
|
+
const url = connection.url ?? process.env["KV_REST_API_URL"];
|
|
3534
|
+
const token = connection.token ?? process.env["KV_REST_API_TOKEN"];
|
|
3535
|
+
if (!url || !token) {
|
|
3536
|
+
throw new StorageConfigError("vercel-kv", "Pass url and token, or set KV_REST_API_URL and KV_REST_API_TOKEN.");
|
|
3537
|
+
}
|
|
3538
|
+
const { createClient } = await loadVercelKv();
|
|
3539
|
+
return createClient({ url, token, cache: void 0, automaticDeserialization: false });
|
|
3540
|
+
}
|
|
3460
3541
|
var VercelKvStorageAdapter;
|
|
3461
3542
|
var init_vercel_kv = __esm({
|
|
3462
3543
|
"libs/utils/src/storage/adapters/vercel-kv.ts"() {
|
|
@@ -3489,13 +3570,7 @@ var init_vercel_kv = __esm({
|
|
|
3489
3570
|
async connect() {
|
|
3490
3571
|
if (this.connected) return;
|
|
3491
3572
|
try {
|
|
3492
|
-
|
|
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");
|
|
3497
|
-
}
|
|
3498
|
-
this.client = createClient({ url, token, cache: void 0, automaticDeserialization: false });
|
|
3573
|
+
this.client = await createVercelKvClient({ url: this.options.url, token: this.options.token });
|
|
3499
3574
|
await this.client.exists("__healthcheck__");
|
|
3500
3575
|
this.connected = true;
|
|
3501
3576
|
} catch (e) {
|
|
@@ -3668,9 +3743,12 @@ var upstash_exports = {};
|
|
|
3668
3743
|
__export(upstash_exports, {
|
|
3669
3744
|
UpstashStorageAdapter: () => UpstashStorageAdapter
|
|
3670
3745
|
});
|
|
3671
|
-
function
|
|
3746
|
+
async function loadUpstashRedis() {
|
|
3672
3747
|
try {
|
|
3673
|
-
|
|
3748
|
+
const mod = await import("@upstash/redis");
|
|
3749
|
+
const resolved = typeof mod.Redis === "function" ? mod : mod.default;
|
|
3750
|
+
if (!resolved || typeof resolved.Redis !== "function") throw new Error("Redis missing");
|
|
3751
|
+
return resolved;
|
|
3674
3752
|
} catch {
|
|
3675
3753
|
throw new Error(
|
|
3676
3754
|
"@upstash/redis is required for Upstash storage adapter. Install it with: npm install @upstash/redis"
|
|
@@ -3715,7 +3793,7 @@ var init_upstash = __esm({
|
|
|
3715
3793
|
async connect() {
|
|
3716
3794
|
if (this.connected) return;
|
|
3717
3795
|
try {
|
|
3718
|
-
const { Redis } =
|
|
3796
|
+
const { Redis } = await loadUpstashRedis();
|
|
3719
3797
|
const url = this.options.url;
|
|
3720
3798
|
const token = this.options.token;
|
|
3721
3799
|
if (!url || !token) {
|
|
@@ -5243,6 +5321,7 @@ function getDetectedStorageType() {
|
|
|
5243
5321
|
|
|
5244
5322
|
// libs/utils/src/storage/index.ts
|
|
5245
5323
|
init_redis_error_listener();
|
|
5324
|
+
init_redis_url();
|
|
5246
5325
|
init_errors();
|
|
5247
5326
|
|
|
5248
5327
|
// libs/utils/src/storage/typed-storage.ts
|
|
@@ -5956,9 +6035,11 @@ export {
|
|
|
5956
6035
|
createSafeRegExp,
|
|
5957
6036
|
createSecretData,
|
|
5958
6037
|
createStorage,
|
|
6038
|
+
createVercelKvClient,
|
|
5959
6039
|
decryptAesGcm,
|
|
5960
6040
|
decryptValue,
|
|
5961
6041
|
deleteSecret,
|
|
6042
|
+
describeRedisUrlConflicts,
|
|
5962
6043
|
deserializeAndDecrypt,
|
|
5963
6044
|
deserializeBlob,
|
|
5964
6045
|
detectRuntimeContext,
|
|
@@ -6042,6 +6123,7 @@ export {
|
|
|
6042
6123
|
loadSecret,
|
|
6043
6124
|
matchUriTemplate,
|
|
6044
6125
|
matchesPattern,
|
|
6126
|
+
mergeRedisUrlFields,
|
|
6045
6127
|
mkdir,
|
|
6046
6128
|
mkdtemp,
|
|
6047
6129
|
normalizeTTL,
|
package/index.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export { EventEmitter } from './event-emitter';
|
|
|
26
26
|
export { getEnv, getNodeEnv, getCwd, isProduction, isDevelopment, getEnvFlag, isDebug, setEnv, isEdgeRuntime, isServerless, supportsAnsi, getRuntimeContext, resetRuntimeContext, detectRuntimeContext, isEntryAvailable, checkEntryAvailability, entryAvailabilitySchema, } from './env';
|
|
27
27
|
export type { RuntimeContext, EntryAvailability } from './env';
|
|
28
28
|
export { analyzePattern, isPatternSafe, createSafeRegExp, safeTest, safeMatch, safeReplace, safeExec, isInputLengthSafe, DEFAULT_MAX_INPUT_LENGTH, REDOS_THRESHOLDS, trimLeading, trimTrailing, trimBoth, trimChars, extractBracedParams, expandTemplate, hasTemplatePlaceholders, collapseChar, collapseWhitespace, type SafeRegexOptions, type PatternAnalysisResult, } from './regex';
|
|
29
|
-
export { createStorage, createMemoryStorage, getDetectedStorageType, NamespacedStorageImpl, createRootStorage, createNamespacedStorage, buildPrefix, NAMESPACE_SEPARATOR, StorageError, StorageConnectionError, StorageOperationError, StorageNotSupportedError, StorageConfigError, StorageTTLError, StoragePatternError, StorageNotConnectedError, EncryptedStorageError, BaseStorageAdapter, MemoryStorageAdapter, RedisStorageAdapter, createRedisClient, VercelKvStorageAdapter, UpstashStorageAdapter, FileSystemStorageAdapter, type FileSystemAdapterOptions, TypedStorage, type TypedStorageOptions, type TypedSetOptions, type TypedSetEntry, EncryptedTypedStorage, type EncryptedTypedStorageOptions, type EncryptedSetOptions, type EncryptedSetEntry, type EncryptionKey, type StoredEncryptedBlob, type ClientKeyBinding, globToRegex, matchesPattern, validatePattern, escapeGlob, MAX_TTL_SECONDS, validateTTL, validateOptionalTTL, ttlToExpiresAt, expiresAtToTTL, isExpired, normalizeTTL, attachRedisErrorListener, DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS, } from './storage';
|
|
30
|
-
export type { StorageAdapter, NamespacedStorage, RootStorage, SetOptions, SetEntry, MessageHandler, Unsubscribe, MemoryAdapterOptions, RedisAdapterOptions, VercelKvAdapterOptions, UpstashAdapterOptions, StorageType, StorageConfig, RedisErrorListenerOptions, CreateRedisClientOptions, ErrorEmitterClient, } from './storage';
|
|
29
|
+
export { createStorage, createMemoryStorage, getDetectedStorageType, NamespacedStorageImpl, createRootStorage, createNamespacedStorage, buildPrefix, NAMESPACE_SEPARATOR, StorageError, StorageConnectionError, StorageOperationError, StorageNotSupportedError, StorageConfigError, StorageTTLError, StoragePatternError, StorageNotConnectedError, EncryptedStorageError, BaseStorageAdapter, MemoryStorageAdapter, RedisStorageAdapter, createRedisClient, VercelKvStorageAdapter, createVercelKvClient, type VercelKvConnection, UpstashStorageAdapter, FileSystemStorageAdapter, type FileSystemAdapterOptions, TypedStorage, type TypedStorageOptions, type TypedSetOptions, type TypedSetEntry, EncryptedTypedStorage, type EncryptedTypedStorageOptions, type EncryptedSetOptions, type EncryptedSetEntry, type EncryptionKey, type StoredEncryptedBlob, type ClientKeyBinding, globToRegex, matchesPattern, validatePattern, escapeGlob, MAX_TTL_SECONDS, validateTTL, validateOptionalTTL, ttlToExpiresAt, expiresAtToTTL, isExpired, normalizeTTL, attachRedisErrorListener, DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS, mergeRedisUrlFields, describeRedisUrlConflicts, } from './storage';
|
|
30
|
+
export type { StorageAdapter, NamespacedStorage, RootStorage, SetOptions, SetEntry, MessageHandler, Unsubscribe, MemoryAdapterOptions, RedisAdapterOptions, RedisHostConnectionConfig, RedisUrlConnectionConfig, RedisUrlFillInConfig, VercelKvAdapterOptions, UpstashAdapterOptions, StorageType, StorageConfig, RedisErrorListenerOptions, RedisUrlMerge, RedisUrlSiblingFields, CreateRedisClientOptions, ErrorEmitterClient, } from './storage';
|
|
31
31
|
export { processPlatformToolCalls } from './llm';
|
|
32
32
|
export type { CallToolFn, OpenAIToolCallItem, OpenAIToolResponse, ClaudeToolUseBlock, ClaudeToolResultBlock, VercelToolCallInfo, PlatformToolCallsInput, PlatformToolCallsOutput, SupportedPlatform, } from './llm';
|
|
33
33
|
export { getMachineId, setMachineIdOverride } from './machine-id';
|
package/index.js
CHANGED
|
@@ -3017,6 +3017,55 @@ var init_redis_error_listener = __esm({
|
|
|
3017
3017
|
}
|
|
3018
3018
|
});
|
|
3019
3019
|
|
|
3020
|
+
// libs/utils/src/storage/redis-url.ts
|
|
3021
|
+
function decodeOrRaw(encoded) {
|
|
3022
|
+
try {
|
|
3023
|
+
return decodeURIComponent(encoded);
|
|
3024
|
+
} catch {
|
|
3025
|
+
return encoded;
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
function mergeRedisUrlFields(rawUrl, fields) {
|
|
3029
|
+
let url;
|
|
3030
|
+
try {
|
|
3031
|
+
url = new URL(rawUrl);
|
|
3032
|
+
} catch {
|
|
3033
|
+
return void 0;
|
|
3034
|
+
}
|
|
3035
|
+
const urlHost = url.hostname.replace(/^\[(.*)\]$/, "$1");
|
|
3036
|
+
const urlPort = url.port ? Number(url.port) : void 0;
|
|
3037
|
+
const urlPassword = url.password ? decodeOrRaw(url.password) : void 0;
|
|
3038
|
+
const urlDbText = url.pathname.replace(/^\/+/, "") || url.searchParams.get("db") || void 0;
|
|
3039
|
+
const urlDb = urlDbText === void 0 ? void 0 : Number(urlDbText);
|
|
3040
|
+
const urlUsesTls = url.protocol === "rediss:";
|
|
3041
|
+
const fillIns = {};
|
|
3042
|
+
const conflicts = [];
|
|
3043
|
+
if (fields.host !== void 0 && fields.host !== urlHost) conflicts.push("host");
|
|
3044
|
+
if (fields.port !== void 0) {
|
|
3045
|
+
if (urlPort === void 0) fillIns.port = fields.port;
|
|
3046
|
+
else if (fields.port !== urlPort) conflicts.push("port");
|
|
3047
|
+
}
|
|
3048
|
+
if (fields.password !== void 0) {
|
|
3049
|
+
if (urlPassword === void 0) fillIns.password = fields.password;
|
|
3050
|
+
else if (fields.password !== urlPassword) conflicts.push("password");
|
|
3051
|
+
}
|
|
3052
|
+
if (fields.db !== void 0) {
|
|
3053
|
+
if (urlDb === void 0) fillIns.db = fields.db;
|
|
3054
|
+
else if (fields.db !== urlDb) conflicts.push("db");
|
|
3055
|
+
}
|
|
3056
|
+
if (fields.tls === true && !urlUsesTls) fillIns.tls = true;
|
|
3057
|
+
if (fields.tls === false && urlUsesTls) conflicts.push("tls");
|
|
3058
|
+
return { fillIns, conflicts };
|
|
3059
|
+
}
|
|
3060
|
+
function describeRedisUrlConflicts(conflicts) {
|
|
3061
|
+
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.`;
|
|
3062
|
+
}
|
|
3063
|
+
var init_redis_url = __esm({
|
|
3064
|
+
"libs/utils/src/storage/redis-url.ts"() {
|
|
3065
|
+
"use strict";
|
|
3066
|
+
}
|
|
3067
|
+
});
|
|
3068
|
+
|
|
3020
3069
|
// libs/utils/src/storage/utils/index.ts
|
|
3021
3070
|
var init_utils = __esm({
|
|
3022
3071
|
"libs/utils/src/storage/utils/index.ts"() {
|
|
@@ -3056,6 +3105,17 @@ function getRedisClass() {
|
|
|
3056
3105
|
});
|
|
3057
3106
|
}
|
|
3058
3107
|
}
|
|
3108
|
+
function resolveUrlFillIns(connectionUrl, options) {
|
|
3109
|
+
const { url: configUrl, ...siblingFields } = { url: void 0, ...options.config };
|
|
3110
|
+
if (options.url && configUrl && configUrl !== options.url) {
|
|
3111
|
+
throw new StorageConfigError("redis", "redis.url and redis.config.url name different servers; set one of them.");
|
|
3112
|
+
}
|
|
3113
|
+
const merge = mergeRedisUrlFields(connectionUrl, siblingFields);
|
|
3114
|
+
if (merge && merge.conflicts.length > 0) {
|
|
3115
|
+
throw new StorageConfigError("redis", describeRedisUrlConflicts(merge.conflicts));
|
|
3116
|
+
}
|
|
3117
|
+
return merge?.fillIns ?? {};
|
|
3118
|
+
}
|
|
3059
3119
|
function createRedisClient(options) {
|
|
3060
3120
|
const RedisClass = getRedisClass();
|
|
3061
3121
|
const baseOptions = { lazyConnect: false, maxRetriesPerRequest: 3 };
|
|
@@ -3076,6 +3136,7 @@ var init_redis = __esm({
|
|
|
3076
3136
|
"use strict";
|
|
3077
3137
|
init_errors();
|
|
3078
3138
|
init_redis_error_listener();
|
|
3139
|
+
init_redis_url();
|
|
3079
3140
|
init_utils();
|
|
3080
3141
|
init_compare_and_delete();
|
|
3081
3142
|
init_base();
|
|
@@ -3089,6 +3150,8 @@ var init_redis = __esm({
|
|
|
3089
3150
|
connecting;
|
|
3090
3151
|
keyPrefix;
|
|
3091
3152
|
subscriptionHandlers = /* @__PURE__ */ new Map();
|
|
3153
|
+
connectionUrl;
|
|
3154
|
+
urlFillIns;
|
|
3092
3155
|
constructor(options = {}) {
|
|
3093
3156
|
super();
|
|
3094
3157
|
const hasClient = options.client !== void 0;
|
|
@@ -3110,6 +3173,9 @@ var init_redis = __esm({
|
|
|
3110
3173
|
this.options = options;
|
|
3111
3174
|
this.ownsClient = !hasClient;
|
|
3112
3175
|
this.keyPrefix = options.keyPrefix ?? "";
|
|
3176
|
+
const configUrl = options.config?.url;
|
|
3177
|
+
this.connectionUrl = options.url ?? (typeof configUrl === "string" ? configUrl : void 0);
|
|
3178
|
+
this.urlFillIns = this.connectionUrl ? resolveUrlFillIns(this.connectionUrl, options) : {};
|
|
3113
3179
|
}
|
|
3114
3180
|
// ============================================
|
|
3115
3181
|
// Connection Lifecycle
|
|
@@ -3132,8 +3198,8 @@ var init_redis = __esm({
|
|
|
3132
3198
|
client = this.options.client;
|
|
3133
3199
|
} else {
|
|
3134
3200
|
const RedisClass = getRedisClass();
|
|
3135
|
-
if (this.
|
|
3136
|
-
client = new RedisClass(this.
|
|
3201
|
+
if (this.connectionUrl) {
|
|
3202
|
+
client = new RedisClass(this.connectionUrl, this.buildRedisOptions());
|
|
3137
3203
|
} else {
|
|
3138
3204
|
client = new RedisClass(this.buildRedisOptions());
|
|
3139
3205
|
}
|
|
@@ -3369,14 +3435,19 @@ var init_redis = __esm({
|
|
|
3369
3435
|
* Build Redis options from config.
|
|
3370
3436
|
*/
|
|
3371
3437
|
buildRedisOptions() {
|
|
3372
|
-
if (this.
|
|
3438
|
+
if (this.connectionUrl) {
|
|
3439
|
+
const { port, password, db, tls } = this.urlFillIns;
|
|
3373
3440
|
return {
|
|
3441
|
+
...port !== void 0 ? { port } : {},
|
|
3442
|
+
...password !== void 0 ? { password } : {},
|
|
3443
|
+
...db !== void 0 ? { db } : {},
|
|
3444
|
+
...tls ? { tls: {} } : {},
|
|
3374
3445
|
lazyConnect: false,
|
|
3375
3446
|
maxRetriesPerRequest: 3
|
|
3376
3447
|
};
|
|
3377
3448
|
}
|
|
3378
3449
|
const config = this.options.config;
|
|
3379
|
-
if (!config) {
|
|
3450
|
+
if (!config || typeof config.url === "string" || config.host === void 0) {
|
|
3380
3451
|
throw new StorageConfigError("redis", "Redis config is required when URL is not provided");
|
|
3381
3452
|
}
|
|
3382
3453
|
return {
|
|
@@ -3395,8 +3466,8 @@ var init_redis = __esm({
|
|
|
3395
3466
|
async createSubscriber() {
|
|
3396
3467
|
const RedisClass = getRedisClass();
|
|
3397
3468
|
let subscriber;
|
|
3398
|
-
if (this.
|
|
3399
|
-
subscriber = new RedisClass(this.
|
|
3469
|
+
if (this.connectionUrl) {
|
|
3470
|
+
subscriber = new RedisClass(this.connectionUrl, this.buildRedisOptions());
|
|
3400
3471
|
} else if (this.options.config) {
|
|
3401
3472
|
subscriber = new RedisClass(this.buildRedisOptions());
|
|
3402
3473
|
} else if (this.options.client) {
|
|
@@ -3447,7 +3518,8 @@ var init_redis = __esm({
|
|
|
3447
3518
|
// libs/utils/src/storage/adapters/vercel-kv.ts
|
|
3448
3519
|
var vercel_kv_exports = {};
|
|
3449
3520
|
__export(vercel_kv_exports, {
|
|
3450
|
-
VercelKvStorageAdapter: () => VercelKvStorageAdapter
|
|
3521
|
+
VercelKvStorageAdapter: () => VercelKvStorageAdapter,
|
|
3522
|
+
createVercelKvClient: () => createVercelKvClient
|
|
3451
3523
|
});
|
|
3452
3524
|
async function loadVercelKv() {
|
|
3453
3525
|
try {
|
|
@@ -3459,6 +3531,15 @@ async function loadVercelKv() {
|
|
|
3459
3531
|
throw new Error("@vercel/kv is required for Vercel KV storage adapter. Install it with: npm install @vercel/kv");
|
|
3460
3532
|
}
|
|
3461
3533
|
}
|
|
3534
|
+
async function createVercelKvClient(connection = {}) {
|
|
3535
|
+
const url = connection.url ?? process.env["KV_REST_API_URL"];
|
|
3536
|
+
const token = connection.token ?? process.env["KV_REST_API_TOKEN"];
|
|
3537
|
+
if (!url || !token) {
|
|
3538
|
+
throw new StorageConfigError("vercel-kv", "Pass url and token, or set KV_REST_API_URL and KV_REST_API_TOKEN.");
|
|
3539
|
+
}
|
|
3540
|
+
const { createClient } = await loadVercelKv();
|
|
3541
|
+
return createClient({ url, token, cache: void 0, automaticDeserialization: false });
|
|
3542
|
+
}
|
|
3462
3543
|
var VercelKvStorageAdapter;
|
|
3463
3544
|
var init_vercel_kv = __esm({
|
|
3464
3545
|
"libs/utils/src/storage/adapters/vercel-kv.ts"() {
|
|
@@ -3491,13 +3572,7 @@ var init_vercel_kv = __esm({
|
|
|
3491
3572
|
async connect() {
|
|
3492
3573
|
if (this.connected) return;
|
|
3493
3574
|
try {
|
|
3494
|
-
|
|
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");
|
|
3499
|
-
}
|
|
3500
|
-
this.client = createClient({ url, token, cache: void 0, automaticDeserialization: false });
|
|
3575
|
+
this.client = await createVercelKvClient({ url: this.options.url, token: this.options.token });
|
|
3501
3576
|
await this.client.exists("__healthcheck__");
|
|
3502
3577
|
this.connected = true;
|
|
3503
3578
|
} catch (e) {
|
|
@@ -3670,9 +3745,12 @@ var upstash_exports = {};
|
|
|
3670
3745
|
__export(upstash_exports, {
|
|
3671
3746
|
UpstashStorageAdapter: () => UpstashStorageAdapter
|
|
3672
3747
|
});
|
|
3673
|
-
function
|
|
3748
|
+
async function loadUpstashRedis() {
|
|
3674
3749
|
try {
|
|
3675
|
-
|
|
3750
|
+
const mod = await import("@upstash/redis");
|
|
3751
|
+
const resolved = typeof mod.Redis === "function" ? mod : mod.default;
|
|
3752
|
+
if (!resolved || typeof resolved.Redis !== "function") throw new Error("Redis missing");
|
|
3753
|
+
return resolved;
|
|
3676
3754
|
} catch {
|
|
3677
3755
|
throw new Error(
|
|
3678
3756
|
"@upstash/redis is required for Upstash storage adapter. Install it with: npm install @upstash/redis"
|
|
@@ -3717,7 +3795,7 @@ var init_upstash = __esm({
|
|
|
3717
3795
|
async connect() {
|
|
3718
3796
|
if (this.connected) return;
|
|
3719
3797
|
try {
|
|
3720
|
-
const { Redis } =
|
|
3798
|
+
const { Redis } = await loadUpstashRedis();
|
|
3721
3799
|
const url = this.options.url;
|
|
3722
3800
|
const token = this.options.token;
|
|
3723
3801
|
if (!url || !token) {
|
|
@@ -4184,9 +4262,11 @@ __export(index_exports, {
|
|
|
4184
4262
|
createSafeRegExp: () => createSafeRegExp,
|
|
4185
4263
|
createSecretData: () => createSecretData,
|
|
4186
4264
|
createStorage: () => createStorage,
|
|
4265
|
+
createVercelKvClient: () => createVercelKvClient,
|
|
4187
4266
|
decryptAesGcm: () => decryptAesGcm,
|
|
4188
4267
|
decryptValue: () => decryptValue,
|
|
4189
4268
|
deleteSecret: () => deleteSecret,
|
|
4269
|
+
describeRedisUrlConflicts: () => describeRedisUrlConflicts,
|
|
4190
4270
|
deserializeAndDecrypt: () => deserializeAndDecrypt,
|
|
4191
4271
|
deserializeBlob: () => deserializeBlob,
|
|
4192
4272
|
detectRuntimeContext: () => import_runtime_context.detectRuntimeContext,
|
|
@@ -4270,6 +4350,7 @@ __export(index_exports, {
|
|
|
4270
4350
|
loadSecret: () => loadSecret,
|
|
4271
4351
|
matchUriTemplate: () => matchUriTemplate,
|
|
4272
4352
|
matchesPattern: () => matchesPattern,
|
|
4353
|
+
mergeRedisUrlFields: () => mergeRedisUrlFields,
|
|
4273
4354
|
mkdir: () => mkdir,
|
|
4274
4355
|
mkdtemp: () => mkdtemp,
|
|
4275
4356
|
normalizeTTL: () => normalizeTTL,
|
|
@@ -5446,6 +5527,7 @@ function getDetectedStorageType() {
|
|
|
5446
5527
|
|
|
5447
5528
|
// libs/utils/src/storage/index.ts
|
|
5448
5529
|
init_redis_error_listener();
|
|
5530
|
+
init_redis_url();
|
|
5449
5531
|
init_errors();
|
|
5450
5532
|
|
|
5451
5533
|
// libs/utils/src/storage/typed-storage.ts
|
|
@@ -6160,9 +6242,11 @@ function setMachineIdOverride(id) {
|
|
|
6160
6242
|
createSafeRegExp,
|
|
6161
6243
|
createSecretData,
|
|
6162
6244
|
createStorage,
|
|
6245
|
+
createVercelKvClient,
|
|
6163
6246
|
decryptAesGcm,
|
|
6164
6247
|
decryptValue,
|
|
6165
6248
|
deleteSecret,
|
|
6249
|
+
describeRedisUrlConflicts,
|
|
6166
6250
|
deserializeAndDecrypt,
|
|
6167
6251
|
deserializeBlob,
|
|
6168
6252
|
detectRuntimeContext,
|
|
@@ -6246,6 +6330,7 @@ function setMachineIdOverride(id) {
|
|
|
6246
6330
|
loadSecret,
|
|
6247
6331
|
matchUriTemplate,
|
|
6248
6332
|
matchesPattern,
|
|
6333
|
+
mergeRedisUrlFields,
|
|
6249
6334
|
mkdir,
|
|
6250
6335
|
mkdtemp,
|
|
6251
6336
|
normalizeTTL,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontmcp/utils",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.2",
|
|
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.9.
|
|
35
|
+
"@frontmcp/lazy-zod": "1.9.2",
|
|
36
36
|
"@vercel/kv": "^2.0.0 || ^3.0.0",
|
|
37
37
|
"ioredis": "^5.0.0"
|
|
38
38
|
},
|
|
@@ -7,7 +7,7 @@ export { BaseStorageAdapter } from './base';
|
|
|
7
7
|
export { MemoryStorageAdapter } from './memory';
|
|
8
8
|
export { RedisStorageAdapter, createRedisClient } from './redis';
|
|
9
9
|
export type { CreateRedisClientOptions } from './redis';
|
|
10
|
-
export { VercelKvStorageAdapter } from './vercel-kv';
|
|
10
|
+
export { VercelKvStorageAdapter, createVercelKvClient, type VercelKvConnection } from './vercel-kv';
|
|
11
11
|
export { UpstashStorageAdapter } from './upstash';
|
|
12
12
|
export { CloudflareKvStorageAdapter } from './cloudflare-kv';
|
|
13
13
|
export { FileSystemStorageAdapter } from './filesystem';
|
|
@@ -39,6 +39,8 @@ export declare class RedisStorageAdapter extends BaseStorageAdapter {
|
|
|
39
39
|
private connecting?;
|
|
40
40
|
private readonly keyPrefix;
|
|
41
41
|
private readonly subscriptionHandlers;
|
|
42
|
+
private readonly connectionUrl?;
|
|
43
|
+
private readonly urlFillIns;
|
|
42
44
|
constructor(options?: RedisAdapterOptions);
|
|
43
45
|
connect(): Promise<void>;
|
|
44
46
|
private openConnection;
|
|
@@ -6,6 +6,46 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { SetOptions, VercelKvAdapterOptions } from '../types';
|
|
8
8
|
import { BaseStorageAdapter } from './base';
|
|
9
|
+
type VercelKvClient = {
|
|
10
|
+
get: (key: string) => Promise<string | null>;
|
|
11
|
+
set: (key: string, value: string, options?: {
|
|
12
|
+
ex?: number;
|
|
13
|
+
nx?: boolean;
|
|
14
|
+
xx?: boolean;
|
|
15
|
+
}) => Promise<string | null>;
|
|
16
|
+
del: (...keys: string[]) => Promise<number>;
|
|
17
|
+
exists: (...keys: string[]) => Promise<number>;
|
|
18
|
+
mget: (...keys: string[]) => Promise<(string | null)[]>;
|
|
19
|
+
expire: (key: string, seconds: number) => Promise<number>;
|
|
20
|
+
ttl: (key: string) => Promise<number>;
|
|
21
|
+
incr: (key: string) => Promise<number>;
|
|
22
|
+
decr: (key: string) => Promise<number>;
|
|
23
|
+
incrby: (key: string, increment: number) => Promise<number>;
|
|
24
|
+
eval: (script: string, keys: string[], args: string[]) => Promise<unknown>;
|
|
25
|
+
scan: (cursor: number, options?: {
|
|
26
|
+
match?: string;
|
|
27
|
+
count?: number;
|
|
28
|
+
}) => Promise<[string, string[]]>;
|
|
29
|
+
keys: (pattern: string) => Promise<string[]>;
|
|
30
|
+
};
|
|
31
|
+
/** Where a Vercel KV client connects; both default to the `KV_REST_API_URL` / `KV_REST_API_TOKEN` variables. */
|
|
32
|
+
export interface VercelKvConnection {
|
|
33
|
+
url?: string;
|
|
34
|
+
token?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build a `@vercel/kv` REST client, the one loader every FrontMCP Vercel KV store shares (#711).
|
|
38
|
+
*
|
|
39
|
+
* `@vercel/kv` sets `cache: 'default'` on every request, which Cloudflare Workers reject ("The
|
|
40
|
+
* 'cache' field on 'RequestInitializerDict' is not implemented" / "Unsupported cache mode:
|
|
41
|
+
* default"), and the module's `kv` singleton cannot take another value. So each store gets a client
|
|
42
|
+
* of its own with the fetch cache mode unset (the value `@vercel/kv` itself calls equivalent), and
|
|
43
|
+
* values are read back exactly as stored instead of being JSON-parsed.
|
|
44
|
+
*
|
|
45
|
+
* @param connection - URL and token; each falls back to its environment variable
|
|
46
|
+
* @returns The client, typed as the subset of it the caller uses
|
|
47
|
+
*/
|
|
48
|
+
export declare function createVercelKvClient<TClient = VercelKvClient>(connection?: VercelKvConnection): Promise<TClient>;
|
|
9
49
|
/**
|
|
10
50
|
* Vercel KV storage adapter.
|
|
11
51
|
*
|
|
@@ -68,3 +108,4 @@ export declare class VercelKvStorageAdapter extends BaseStorageAdapter {
|
|
|
68
108
|
*/
|
|
69
109
|
private unprefixKey;
|
|
70
110
|
}
|
|
111
|
+
export {};
|
package/storage/index.d.ts
CHANGED
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
* Unified storage abstraction with pluggable backends.
|
|
5
5
|
* Supports Memory (dev), Redis (prod), Vercel KV (edge), and Upstash (edge + pub/sub).
|
|
6
6
|
*/
|
|
7
|
-
export type { StorageAdapter, NamespacedStorage, RootStorage, SetOptions, SetEntry, MessageHandler, Unsubscribe, MemoryAdapterOptions, RedisAdapterOptions, VercelKvAdapterOptions, UpstashAdapterOptions, StorageType, StorageConfig, } from './types';
|
|
7
|
+
export type { StorageAdapter, NamespacedStorage, RootStorage, SetOptions, SetEntry, MessageHandler, Unsubscribe, MemoryAdapterOptions, RedisAdapterOptions, RedisHostConnectionConfig, RedisUrlConnectionConfig, RedisUrlFillInConfig, VercelKvAdapterOptions, UpstashAdapterOptions, StorageType, StorageConfig, } from './types';
|
|
8
8
|
export { createStorage, createMemoryStorage, getDetectedStorageType } from './factory';
|
|
9
9
|
export { attachRedisErrorListener, DEFAULT_REDIS_ERROR_LOG_INTERVAL_MS } from './redis-error-listener';
|
|
10
10
|
export type { RedisErrorListenerOptions, ErrorEmitterClient } from './redis-error-listener';
|
|
11
|
+
export { mergeRedisUrlFields, describeRedisUrlConflicts } from './redis-url';
|
|
12
|
+
export type { RedisUrlMerge, RedisUrlSiblingFields } from './redis-url';
|
|
11
13
|
export { NamespacedStorageImpl, createRootStorage, createNamespacedStorage, buildPrefix, NAMESPACE_SEPARATOR, } from './namespace';
|
|
12
14
|
export { StorageError, StorageConnectionError, StorageOperationError, StorageNotSupportedError, StorageConfigError, StorageTTLError, StoragePatternError, StorageNotConnectedError, EncryptedStorageError, } from './errors';
|
|
13
15
|
export { TypedStorage } from './typed-storage';
|
|
14
16
|
export type { TypedStorageOptions, TypedSetOptions, TypedSetEntry } from './typed-storage.types';
|
|
15
17
|
export { EncryptedTypedStorage } from './encrypted-typed-storage';
|
|
16
18
|
export type { EncryptedTypedStorageOptions, EncryptedSetOptions, EncryptedSetEntry, EncryptionKey, StoredEncryptedBlob, ClientKeyBinding, } from './encrypted-typed-storage.types';
|
|
17
|
-
export { BaseStorageAdapter, MemoryStorageAdapter, RedisStorageAdapter, createRedisClient, VercelKvStorageAdapter, UpstashStorageAdapter, FileSystemStorageAdapter, LocalStorageAdapter, IndexedDBStorageAdapter, } from './adapters';
|
|
18
|
-
export type { CreateRedisClientOptions, FileSystemAdapterOptions, LocalStorageAdapterOptions, IndexedDBAdapterOptions, } from './adapters';
|
|
19
|
+
export { BaseStorageAdapter, MemoryStorageAdapter, RedisStorageAdapter, createRedisClient, VercelKvStorageAdapter, createVercelKvClient, UpstashStorageAdapter, FileSystemStorageAdapter, LocalStorageAdapter, IndexedDBStorageAdapter, } from './adapters';
|
|
20
|
+
export type { CreateRedisClientOptions, VercelKvConnection, FileSystemAdapterOptions, LocalStorageAdapterOptions, IndexedDBAdapterOptions, } from './adapters';
|
|
19
21
|
export { globToRegex, matchesPattern, validatePattern, escapeGlob } from './utils/pattern';
|
|
20
22
|
export { MAX_TTL_SECONDS, validateTTL, validateOptionalTTL, ttlToExpiresAt, expiresAtToTTL, isExpired, normalizeTTL, } from './utils/ttl';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection fields that may be written beside a Redis `url`.
|
|
3
|
+
*/
|
|
4
|
+
export interface RedisUrlSiblingFields {
|
|
5
|
+
host?: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
password?: string;
|
|
8
|
+
db?: number;
|
|
9
|
+
tls?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Outcome of combining a Redis URL with the fields written beside it.
|
|
13
|
+
*/
|
|
14
|
+
export interface RedisUrlMerge {
|
|
15
|
+
/** Fields the URL leaves out that the sibling fields supply. */
|
|
16
|
+
fillIns: Pick<RedisUrlSiblingFields, 'port' | 'password' | 'db' | 'tls'>;
|
|
17
|
+
/** Sibling fields whose value contradicts the URL. */
|
|
18
|
+
conflicts: Array<keyof RedisUrlSiblingFields>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The URL is the base: fields beside it fill in only what it leaves out (a
|
|
22
|
+
* port, a password, a database, TLS), and a field that contradicts it is a
|
|
23
|
+
* conflict the caller must reject (#768). Returns `undefined` for an
|
|
24
|
+
* unparseable URL, which the caller reports on its own terms.
|
|
25
|
+
*/
|
|
26
|
+
export declare function mergeRedisUrlFields(rawUrl: string, fields: RedisUrlSiblingFields): RedisUrlMerge | undefined;
|
|
27
|
+
/** Human-readable reason for {@link RedisUrlMerge.conflicts}. */
|
|
28
|
+
export declare function describeRedisUrlConflicts(conflicts: RedisUrlMerge['conflicts']): string;
|
package/storage/types.d.ts
CHANGED
|
@@ -309,6 +309,31 @@ export interface MemoryAdapterOptions {
|
|
|
309
309
|
*/
|
|
310
310
|
maxEntries?: number;
|
|
311
311
|
}
|
|
312
|
+
/**
|
|
313
|
+
* Redis connection by host.
|
|
314
|
+
*/
|
|
315
|
+
export interface RedisHostConnectionConfig {
|
|
316
|
+
host: string;
|
|
317
|
+
port?: number;
|
|
318
|
+
password?: string;
|
|
319
|
+
db?: number;
|
|
320
|
+
tls?: boolean;
|
|
321
|
+
/** Unset for a host config (a config built from optional settings may carry `url: undefined`). */
|
|
322
|
+
url?: undefined;
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Redis connection by URL (`redis://` / `rediss://`).
|
|
326
|
+
*/
|
|
327
|
+
export interface RedisUrlConnectionConfig extends Partial<Omit<RedisHostConnectionConfig, 'url'>> {
|
|
328
|
+
url: string;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Fields beside a top-level `url` (`{ url, config: { password } }`) that fill in what the URL
|
|
332
|
+
* leaves out. Without a `url` the adapter needs a `host` and rejects this shape.
|
|
333
|
+
*/
|
|
334
|
+
export interface RedisUrlFillInConfig extends Partial<Omit<RedisHostConnectionConfig, 'url'>> {
|
|
335
|
+
url?: undefined;
|
|
336
|
+
}
|
|
312
337
|
/**
|
|
313
338
|
* Options for Redis storage adapter.
|
|
314
339
|
*/
|
|
@@ -319,19 +344,16 @@ export interface RedisAdapterOptions {
|
|
|
319
344
|
*/
|
|
320
345
|
client?: unknown;
|
|
321
346
|
/**
|
|
322
|
-
* Redis connection configuration
|
|
347
|
+
* Redis connection configuration — `host`, or a `url` like the top-level
|
|
348
|
+
* `redis` option. Beside a URL, the other fields only fill in what the URL
|
|
349
|
+
* leaves out; a contradicting field is rejected.
|
|
323
350
|
* Mutually exclusive with `client`.
|
|
324
351
|
*/
|
|
325
|
-
config?:
|
|
326
|
-
host: string;
|
|
327
|
-
port?: number;
|
|
328
|
-
password?: string;
|
|
329
|
-
db?: number;
|
|
330
|
-
tls?: boolean;
|
|
331
|
-
};
|
|
352
|
+
config?: RedisHostConnectionConfig | RedisUrlConnectionConfig | RedisUrlFillInConfig;
|
|
332
353
|
/**
|
|
333
354
|
* Redis connection URI.
|
|
334
|
-
* e.g., "redis://user:pass@host:6379/0"
|
|
355
|
+
* e.g., "redis://user:pass@host:6379/0". Fields in `config` fill in what it
|
|
356
|
+
* leaves out (port, password, db, tls).
|
|
335
357
|
* Mutually exclusive with `client`.
|
|
336
358
|
*/
|
|
337
359
|
url?: string;
|