@windrun-huaiin/backend-core 29.0.2 → 29.0.3
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/dist/lib/upstash/qstash.js +6 -6
- package/dist/lib/upstash/qstash.mjs +6 -6
- package/dist/lib/upstash-config.d.ts +1 -1
- package/dist/lib/upstash-config.d.ts.map +1 -1
- package/dist/lib/upstash-config.js +73 -22
- package/dist/lib/upstash-config.mjs +73 -22
- package/package.json +3 -3
- package/src/lib/upstash/qstash.ts +6 -6
- package/src/lib/upstash-config.ts +107 -31
|
@@ -67,7 +67,7 @@ const publishMessage = (options) => tslib.__awaiter(void 0, void 0, void 0, func
|
|
|
67
67
|
messageId: typeof result === 'string' ? result : (_a = result === null || result === void 0 ? void 0 : result.messageId) !== null && _a !== void 0 ? _a : null,
|
|
68
68
|
message,
|
|
69
69
|
};
|
|
70
|
-
}));
|
|
70
|
+
}), 'publishMessage');
|
|
71
71
|
});
|
|
72
72
|
/**
|
|
73
73
|
* Publish a broadcast message to a QStash URL Group.
|
|
@@ -93,7 +93,7 @@ const publishBroadcastMessage = (options) => tslib.__awaiter(void 0, void 0, voi
|
|
|
93
93
|
messageIds,
|
|
94
94
|
message,
|
|
95
95
|
};
|
|
96
|
-
}));
|
|
96
|
+
}), 'publishBroadcastMessage');
|
|
97
97
|
});
|
|
98
98
|
/**
|
|
99
99
|
* Publish a single-recipient message into a QStash FIFO queue.
|
|
@@ -117,7 +117,7 @@ const publishFIFOQueueMessage = (options) => tslib.__awaiter(void 0, void 0, voi
|
|
|
117
117
|
messageId: typeof result === 'string' ? result : (_b = result === null || result === void 0 ? void 0 : result.messageId) !== null && _b !== void 0 ? _b : null,
|
|
118
118
|
message,
|
|
119
119
|
};
|
|
120
|
-
}));
|
|
120
|
+
}), 'publishFIFOQueueMessage');
|
|
121
121
|
});
|
|
122
122
|
/**
|
|
123
123
|
* Publish a delayed message. Returns message id or null if QStash is unavailable.
|
|
@@ -135,7 +135,7 @@ const publishDelayedMessage = (options) => tslib.__awaiter(void 0, void 0, void
|
|
|
135
135
|
messageId: typeof result === 'string' ? result : (_a = result === null || result === void 0 ? void 0 : result.messageId) !== null && _a !== void 0 ? _a : null,
|
|
136
136
|
message,
|
|
137
137
|
};
|
|
138
|
-
}));
|
|
138
|
+
}), 'publishDelayedMessage');
|
|
139
139
|
});
|
|
140
140
|
/**
|
|
141
141
|
* Schedule a recurring message. Returns schedule id or null if QStash is unavailable.
|
|
@@ -158,7 +158,7 @@ const scheduleMessage = (options) => tslib.__awaiter(void 0, void 0, void 0, fun
|
|
|
158
158
|
scheduleId: typeof result === 'string' ? result : (_f = (_e = result === null || result === void 0 ? void 0 : result.scheduleId) !== null && _e !== void 0 ? _e : result === null || result === void 0 ? void 0 : result.id) !== null && _f !== void 0 ? _f : null,
|
|
159
159
|
message,
|
|
160
160
|
};
|
|
161
|
-
}));
|
|
161
|
+
}), 'scheduleMessage');
|
|
162
162
|
});
|
|
163
163
|
/**
|
|
164
164
|
* Cancel a scheduled message. Returns false if QStash is unavailable.
|
|
@@ -176,7 +176,7 @@ const cancelSchedule = (scheduleId) => tslib.__awaiter(void 0, void 0, void 0, f
|
|
|
176
176
|
return true;
|
|
177
177
|
}
|
|
178
178
|
return false;
|
|
179
|
-
}));
|
|
179
|
+
}), 'cancelSchedule');
|
|
180
180
|
return result !== null && result !== void 0 ? result : false;
|
|
181
181
|
});
|
|
182
182
|
/**
|
|
@@ -65,7 +65,7 @@ const publishMessage = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
65
65
|
messageId: typeof result === 'string' ? result : (_a = result === null || result === void 0 ? void 0 : result.messageId) !== null && _a !== void 0 ? _a : null,
|
|
66
66
|
message,
|
|
67
67
|
};
|
|
68
|
-
}));
|
|
68
|
+
}), 'publishMessage');
|
|
69
69
|
});
|
|
70
70
|
/**
|
|
71
71
|
* Publish a broadcast message to a QStash URL Group.
|
|
@@ -91,7 +91,7 @@ const publishBroadcastMessage = (options) => __awaiter(void 0, void 0, void 0, f
|
|
|
91
91
|
messageIds,
|
|
92
92
|
message,
|
|
93
93
|
};
|
|
94
|
-
}));
|
|
94
|
+
}), 'publishBroadcastMessage');
|
|
95
95
|
});
|
|
96
96
|
/**
|
|
97
97
|
* Publish a single-recipient message into a QStash FIFO queue.
|
|
@@ -115,7 +115,7 @@ const publishFIFOQueueMessage = (options) => __awaiter(void 0, void 0, void 0, f
|
|
|
115
115
|
messageId: typeof result === 'string' ? result : (_b = result === null || result === void 0 ? void 0 : result.messageId) !== null && _b !== void 0 ? _b : null,
|
|
116
116
|
message,
|
|
117
117
|
};
|
|
118
|
-
}));
|
|
118
|
+
}), 'publishFIFOQueueMessage');
|
|
119
119
|
});
|
|
120
120
|
/**
|
|
121
121
|
* Publish a delayed message. Returns message id or null if QStash is unavailable.
|
|
@@ -133,7 +133,7 @@ const publishDelayedMessage = (options) => __awaiter(void 0, void 0, void 0, fun
|
|
|
133
133
|
messageId: typeof result === 'string' ? result : (_a = result === null || result === void 0 ? void 0 : result.messageId) !== null && _a !== void 0 ? _a : null,
|
|
134
134
|
message,
|
|
135
135
|
};
|
|
136
|
-
}));
|
|
136
|
+
}), 'publishDelayedMessage');
|
|
137
137
|
});
|
|
138
138
|
/**
|
|
139
139
|
* Schedule a recurring message. Returns schedule id or null if QStash is unavailable.
|
|
@@ -156,7 +156,7 @@ const scheduleMessage = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
156
156
|
scheduleId: typeof result === 'string' ? result : (_f = (_e = result === null || result === void 0 ? void 0 : result.scheduleId) !== null && _e !== void 0 ? _e : result === null || result === void 0 ? void 0 : result.id) !== null && _f !== void 0 ? _f : null,
|
|
157
157
|
message,
|
|
158
158
|
};
|
|
159
|
-
}));
|
|
159
|
+
}), 'scheduleMessage');
|
|
160
160
|
});
|
|
161
161
|
/**
|
|
162
162
|
* Cancel a scheduled message. Returns false if QStash is unavailable.
|
|
@@ -174,7 +174,7 @@ const cancelSchedule = (scheduleId) => __awaiter(void 0, void 0, void 0, functio
|
|
|
174
174
|
return true;
|
|
175
175
|
}
|
|
176
176
|
return false;
|
|
177
|
-
}));
|
|
177
|
+
}), 'cancelSchedule');
|
|
178
178
|
return result !== null && result !== void 0 ? result : false;
|
|
179
179
|
});
|
|
180
180
|
/**
|
|
@@ -19,5 +19,5 @@ export declare const getRedis: () => Redis | null;
|
|
|
19
19
|
*/
|
|
20
20
|
export declare const getQstash: () => QstashClient | null;
|
|
21
21
|
export declare const withRedis: <T>(fn: (redis: Redis) => Promise<T> | T) => Promise<T | null>;
|
|
22
|
-
export declare const withQstash: <T>(fn: (qstash: QstashClient) => Promise<T> | T) => Promise<T | null>;
|
|
22
|
+
export declare const withQstash: <T>(fn: (qstash: QstashClient) => Promise<T> | T, operation?: string) => Promise<T | null>;
|
|
23
23
|
//# sourceMappingURL=upstash-config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstash-config.d.ts","sourceRoot":"","sources":["../../src/lib/upstash-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"upstash-config.d.ts","sourceRoot":"","sources":["../../src/lib/upstash-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAoHzD,eAAO,MAAM,mBAAmB,GAAI,KAAK,MAAM,KAAG,MAAyD,CAAC;AAE5G,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,EAAE,KAAG,MAAM,EAAmC,CAAC;AAEhG,eAAO,MAAM,mBAAmB,QAAO,MAEtC,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,MAAM,MAAM,KAAG,MAMzD,CAAC;AAyMF;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,QAAO,KAAK,GAAG,IAEnC,CAAC;AAwDF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,QAAO,YAAY,GAAG,IAE3C,CAAC;AA+CF,eAAO,MAAM,SAAS,GAAU,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,KAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAMzF,CAAC;AAEF,eAAO,MAAM,UAAU,GAAU,CAAC,EAChC,IAAI,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAC5C,kBAAqB,KACpB,OAAO,CAAC,CAAC,GAAG,IAAI,CAgBlB,CAAC"}
|
|
@@ -20,6 +20,41 @@ let qstashWarnedHealthSchedule = false;
|
|
|
20
20
|
let redisHealthTimer = null;
|
|
21
21
|
let qstashHealthTimer = null;
|
|
22
22
|
let cachedRedisPrefixed = null;
|
|
23
|
+
const isUpstashDebugEnabled = () => process.env.UPSTASH_DEBUG === 'true';
|
|
24
|
+
const formatDuration = (startedAt) => `${Date.now() - startedAt}ms`;
|
|
25
|
+
const logUpstashDuration = (scope, operation, startedAt, status, error) => {
|
|
26
|
+
if (!isUpstashDebugEnabled()) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const message = `[Upstash Debug] ${scope} ${operation} completed in ${formatDuration(startedAt)} status=${status}`;
|
|
30
|
+
if (error == null) {
|
|
31
|
+
console.log(message);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
35
|
+
console.log(`${message} error=${errorMessage}`);
|
|
36
|
+
};
|
|
37
|
+
const isPromiseLike = (value) => typeof value === 'object' && value !== null && typeof value.then === 'function';
|
|
38
|
+
const trackUpstashOperation = (scope, operation, startedAt, run) => {
|
|
39
|
+
try {
|
|
40
|
+
const result = run();
|
|
41
|
+
if (isPromiseLike(result)) {
|
|
42
|
+
return result.then((resolved) => {
|
|
43
|
+
logUpstashDuration(scope, operation, startedAt, 'ok');
|
|
44
|
+
return resolved;
|
|
45
|
+
}, (error) => {
|
|
46
|
+
logUpstashDuration(scope, operation, startedAt, 'error', error);
|
|
47
|
+
throw error;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
logUpstashDuration(scope, operation, startedAt, 'ok');
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
logUpstashDuration(scope, operation, startedAt, 'error', error);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
23
58
|
const isNonEmpty = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
24
59
|
const isValidUrl = (value) => {
|
|
25
60
|
try {
|
|
@@ -88,33 +123,39 @@ const createPrefixedPipeline = (target, prefix) => {
|
|
|
88
123
|
return value;
|
|
89
124
|
}
|
|
90
125
|
return (...args) => {
|
|
126
|
+
const operation = typeof prop === 'string' ? prop : String(prop);
|
|
127
|
+
const startedAt = Date.now();
|
|
91
128
|
if (prop === 'eval' || prop === 'evalsha' || prop === 'evalro' || prop === 'evalshaRo') {
|
|
92
129
|
const [script, keys, argv] = args;
|
|
93
|
-
return value.call(obj, script, prefixRedisKeys(prefix, keys), argv);
|
|
130
|
+
return trackUpstashOperation('Redis', operation, startedAt, () => value.call(obj, script, prefixRedisKeys(prefix, keys), argv));
|
|
94
131
|
}
|
|
95
132
|
if (prop === 'pipeline' || prop === 'multi') {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
101
|
-
return value.apply(obj, nextArgs);
|
|
102
|
-
}
|
|
103
|
-
if (typeof prop === 'string' && allStringKeyCommands.has(prop)) {
|
|
104
|
-
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
105
|
-
return value.apply(obj, nextArgs);
|
|
133
|
+
return trackUpstashOperation('Redis', operation, startedAt, () => {
|
|
134
|
+
const nested = value.call(obj);
|
|
135
|
+
return createPrefixedPipeline(nested, prefix);
|
|
136
|
+
});
|
|
106
137
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
138
|
+
return trackUpstashOperation('Redis', operation, startedAt, () => {
|
|
139
|
+
if (typeof prop === 'string' && keyArrayCommands.has(prop)) {
|
|
140
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
141
|
+
return value.apply(obj, nextArgs);
|
|
142
|
+
}
|
|
143
|
+
if (typeof prop === 'string' && allStringKeyCommands.has(prop)) {
|
|
144
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
145
|
+
return value.apply(obj, nextArgs);
|
|
146
|
+
}
|
|
147
|
+
if (prop === 'mset') {
|
|
148
|
+
const [entries] = args;
|
|
149
|
+
const prefixedEntries = Object.fromEntries(Object.entries(entries).map(([key, entryValue]) => [prefixRedisKey(prefix, key), entryValue]));
|
|
150
|
+
return value.call(obj, prefixedEntries);
|
|
151
|
+
}
|
|
152
|
+
if (prop === 'hmget') {
|
|
153
|
+
const nextArgs = prefixFirstStringArg(args, prefix);
|
|
154
|
+
return value.apply(obj, nextArgs);
|
|
155
|
+
}
|
|
113
156
|
const nextArgs = prefixFirstStringArg(args, prefix);
|
|
114
157
|
return value.apply(obj, nextArgs);
|
|
115
|
-
}
|
|
116
|
-
const nextArgs = prefixFirstStringArg(args, prefix);
|
|
117
|
-
return value.apply(obj, nextArgs);
|
|
158
|
+
});
|
|
118
159
|
};
|
|
119
160
|
},
|
|
120
161
|
});
|
|
@@ -337,12 +378,22 @@ const withRedis = (fn) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
|
337
378
|
}
|
|
338
379
|
return fn(redis);
|
|
339
380
|
});
|
|
340
|
-
const withQstash = (
|
|
381
|
+
const withQstash = (fn_1, ...args_1) => tslib.__awaiter(void 0, [fn_1, ...args_1], void 0, function* (fn, operation = 'request') {
|
|
382
|
+
const startedAt = Date.now();
|
|
341
383
|
const qstash = yield ensureQstash();
|
|
342
384
|
if (!qstash) {
|
|
385
|
+
logUpstashDuration('QStash', operation, startedAt, 'unavailable');
|
|
343
386
|
return null;
|
|
344
387
|
}
|
|
345
|
-
|
|
388
|
+
try {
|
|
389
|
+
const result = yield fn(qstash);
|
|
390
|
+
logUpstashDuration('QStash', operation, startedAt, 'ok');
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
catch (error) {
|
|
394
|
+
logUpstashDuration('QStash', operation, startedAt, 'error', error);
|
|
395
|
+
throw error;
|
|
396
|
+
}
|
|
346
397
|
});
|
|
347
398
|
|
|
348
399
|
exports.getPrefixedQstashQueueName = getPrefixedQstashQueueName;
|
|
@@ -18,6 +18,41 @@ let qstashWarnedHealthSchedule = false;
|
|
|
18
18
|
let redisHealthTimer = null;
|
|
19
19
|
let qstashHealthTimer = null;
|
|
20
20
|
let cachedRedisPrefixed = null;
|
|
21
|
+
const isUpstashDebugEnabled = () => process.env.UPSTASH_DEBUG === 'true';
|
|
22
|
+
const formatDuration = (startedAt) => `${Date.now() - startedAt}ms`;
|
|
23
|
+
const logUpstashDuration = (scope, operation, startedAt, status, error) => {
|
|
24
|
+
if (!isUpstashDebugEnabled()) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const message = `[Upstash Debug] ${scope} ${operation} completed in ${formatDuration(startedAt)} status=${status}`;
|
|
28
|
+
if (error == null) {
|
|
29
|
+
console.log(message);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
33
|
+
console.log(`${message} error=${errorMessage}`);
|
|
34
|
+
};
|
|
35
|
+
const isPromiseLike = (value) => typeof value === 'object' && value !== null && typeof value.then === 'function';
|
|
36
|
+
const trackUpstashOperation = (scope, operation, startedAt, run) => {
|
|
37
|
+
try {
|
|
38
|
+
const result = run();
|
|
39
|
+
if (isPromiseLike(result)) {
|
|
40
|
+
return result.then((resolved) => {
|
|
41
|
+
logUpstashDuration(scope, operation, startedAt, 'ok');
|
|
42
|
+
return resolved;
|
|
43
|
+
}, (error) => {
|
|
44
|
+
logUpstashDuration(scope, operation, startedAt, 'error', error);
|
|
45
|
+
throw error;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
logUpstashDuration(scope, operation, startedAt, 'ok');
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logUpstashDuration(scope, operation, startedAt, 'error', error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
21
56
|
const isNonEmpty = (value) => typeof value === 'string' && value.trim().length > 0;
|
|
22
57
|
const isValidUrl = (value) => {
|
|
23
58
|
try {
|
|
@@ -86,33 +121,39 @@ const createPrefixedPipeline = (target, prefix) => {
|
|
|
86
121
|
return value;
|
|
87
122
|
}
|
|
88
123
|
return (...args) => {
|
|
124
|
+
const operation = typeof prop === 'string' ? prop : String(prop);
|
|
125
|
+
const startedAt = Date.now();
|
|
89
126
|
if (prop === 'eval' || prop === 'evalsha' || prop === 'evalro' || prop === 'evalshaRo') {
|
|
90
127
|
const [script, keys, argv] = args;
|
|
91
|
-
return value.call(obj, script, prefixRedisKeys(prefix, keys), argv);
|
|
128
|
+
return trackUpstashOperation('Redis', operation, startedAt, () => value.call(obj, script, prefixRedisKeys(prefix, keys), argv));
|
|
92
129
|
}
|
|
93
130
|
if (prop === 'pipeline' || prop === 'multi') {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
99
|
-
return value.apply(obj, nextArgs);
|
|
100
|
-
}
|
|
101
|
-
if (typeof prop === 'string' && allStringKeyCommands.has(prop)) {
|
|
102
|
-
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
103
|
-
return value.apply(obj, nextArgs);
|
|
131
|
+
return trackUpstashOperation('Redis', operation, startedAt, () => {
|
|
132
|
+
const nested = value.call(obj);
|
|
133
|
+
return createPrefixedPipeline(nested, prefix);
|
|
134
|
+
});
|
|
104
135
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
136
|
+
return trackUpstashOperation('Redis', operation, startedAt, () => {
|
|
137
|
+
if (typeof prop === 'string' && keyArrayCommands.has(prop)) {
|
|
138
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
139
|
+
return value.apply(obj, nextArgs);
|
|
140
|
+
}
|
|
141
|
+
if (typeof prop === 'string' && allStringKeyCommands.has(prop)) {
|
|
142
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
143
|
+
return value.apply(obj, nextArgs);
|
|
144
|
+
}
|
|
145
|
+
if (prop === 'mset') {
|
|
146
|
+
const [entries] = args;
|
|
147
|
+
const prefixedEntries = Object.fromEntries(Object.entries(entries).map(([key, entryValue]) => [prefixRedisKey(prefix, key), entryValue]));
|
|
148
|
+
return value.call(obj, prefixedEntries);
|
|
149
|
+
}
|
|
150
|
+
if (prop === 'hmget') {
|
|
151
|
+
const nextArgs = prefixFirstStringArg(args, prefix);
|
|
152
|
+
return value.apply(obj, nextArgs);
|
|
153
|
+
}
|
|
111
154
|
const nextArgs = prefixFirstStringArg(args, prefix);
|
|
112
155
|
return value.apply(obj, nextArgs);
|
|
113
|
-
}
|
|
114
|
-
const nextArgs = prefixFirstStringArg(args, prefix);
|
|
115
|
-
return value.apply(obj, nextArgs);
|
|
156
|
+
});
|
|
116
157
|
};
|
|
117
158
|
},
|
|
118
159
|
});
|
|
@@ -335,12 +376,22 @@ const withRedis = (fn) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
335
376
|
}
|
|
336
377
|
return fn(redis);
|
|
337
378
|
});
|
|
338
|
-
const withQstash = (
|
|
379
|
+
const withQstash = (fn_1, ...args_1) => __awaiter(void 0, [fn_1, ...args_1], void 0, function* (fn, operation = 'request') {
|
|
380
|
+
const startedAt = Date.now();
|
|
339
381
|
const qstash = yield ensureQstash();
|
|
340
382
|
if (!qstash) {
|
|
383
|
+
logUpstashDuration('QStash', operation, startedAt, 'unavailable');
|
|
341
384
|
return null;
|
|
342
385
|
}
|
|
343
|
-
|
|
386
|
+
try {
|
|
387
|
+
const result = yield fn(qstash);
|
|
388
|
+
logUpstashDuration('QStash', operation, startedAt, 'ok');
|
|
389
|
+
return result;
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
logUpstashDuration('QStash', operation, startedAt, 'error', error);
|
|
393
|
+
throw error;
|
|
394
|
+
}
|
|
344
395
|
});
|
|
345
396
|
|
|
346
397
|
export { getPrefixedQstashQueueName, getPrefixedRedisKey, getPrefixedRedisKeys, getQstash, getQstashNamePrefix, getRedis, withQstash, withRedis };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windrun-huaiin/backend-core",
|
|
3
|
-
"version": "29.0.
|
|
3
|
+
"version": "29.0.3",
|
|
4
4
|
"description": "Shared backend primitives: Prisma schema/client, database services, routing helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -149,8 +149,8 @@
|
|
|
149
149
|
"tslib": "^2.8.1",
|
|
150
150
|
"zod": "^4.3.6",
|
|
151
151
|
"@windrun-huaiin/contracts": "^29.0.0",
|
|
152
|
-
"@windrun-huaiin/
|
|
153
|
-
"@windrun-huaiin/
|
|
152
|
+
"@windrun-huaiin/lib": "^29.0.0",
|
|
153
|
+
"@windrun-huaiin/third-ui": "^29.0.3"
|
|
154
154
|
},
|
|
155
155
|
"devDependencies": {
|
|
156
156
|
"@rollup/plugin-alias": "^5.1.1",
|
|
@@ -99,7 +99,7 @@ export const publishMessage = async <TBody extends PublishBody>(
|
|
|
99
99
|
messageId: typeof result === 'string' ? result : result?.messageId ?? null,
|
|
100
100
|
message,
|
|
101
101
|
};
|
|
102
|
-
});
|
|
102
|
+
}, 'publishMessage');
|
|
103
103
|
};
|
|
104
104
|
|
|
105
105
|
/**
|
|
@@ -133,7 +133,7 @@ export const publishBroadcastMessage = async <TBody extends PublishBody>(
|
|
|
133
133
|
messageIds,
|
|
134
134
|
message,
|
|
135
135
|
};
|
|
136
|
-
});
|
|
136
|
+
}, 'publishBroadcastMessage');
|
|
137
137
|
};
|
|
138
138
|
|
|
139
139
|
/**
|
|
@@ -162,7 +162,7 @@ export const publishFIFOQueueMessage = async <TBody extends PublishBody>(
|
|
|
162
162
|
messageId: typeof result === 'string' ? result : result?.messageId ?? null,
|
|
163
163
|
message,
|
|
164
164
|
};
|
|
165
|
-
});
|
|
165
|
+
}, 'publishFIFOQueueMessage');
|
|
166
166
|
};
|
|
167
167
|
|
|
168
168
|
/**
|
|
@@ -183,7 +183,7 @@ export const publishDelayedMessage = async <TBody extends PublishBody>(
|
|
|
183
183
|
messageId: typeof result === 'string' ? result : result?.messageId ?? null,
|
|
184
184
|
message,
|
|
185
185
|
};
|
|
186
|
-
});
|
|
186
|
+
}, 'publishDelayedMessage');
|
|
187
187
|
};
|
|
188
188
|
|
|
189
189
|
export interface ScheduleMessageOptions<TBody extends PublishBody = PublishBody>
|
|
@@ -217,7 +217,7 @@ export const scheduleMessage = async <TBody extends PublishBody>(
|
|
|
217
217
|
scheduleId: typeof result === 'string' ? result : result?.scheduleId ?? result?.id ?? null,
|
|
218
218
|
message,
|
|
219
219
|
};
|
|
220
|
-
});
|
|
220
|
+
}, 'scheduleMessage');
|
|
221
221
|
};
|
|
222
222
|
|
|
223
223
|
/**
|
|
@@ -235,7 +235,7 @@ export const cancelSchedule = async (scheduleId: string): Promise<boolean> => {
|
|
|
235
235
|
return true;
|
|
236
236
|
}
|
|
237
237
|
return false;
|
|
238
|
-
});
|
|
238
|
+
}, 'cancelSchedule');
|
|
239
239
|
|
|
240
240
|
return result ?? false;
|
|
241
241
|
};
|
|
@@ -21,6 +21,63 @@ let redisHealthTimer: ReturnType<typeof setTimeout> | null = null;
|
|
|
21
21
|
let qstashHealthTimer: ReturnType<typeof setTimeout> | null = null;
|
|
22
22
|
let cachedRedisPrefixed: Redis | null = null;
|
|
23
23
|
|
|
24
|
+
const isUpstashDebugEnabled = (): boolean => process.env.UPSTASH_DEBUG === 'true';
|
|
25
|
+
|
|
26
|
+
const formatDuration = (startedAt: number): string => `${Date.now() - startedAt}ms`;
|
|
27
|
+
|
|
28
|
+
const logUpstashDuration = (
|
|
29
|
+
scope: string,
|
|
30
|
+
operation: string,
|
|
31
|
+
startedAt: number,
|
|
32
|
+
status: 'ok' | 'error' | 'unavailable',
|
|
33
|
+
error?: unknown
|
|
34
|
+
): void => {
|
|
35
|
+
if (!isUpstashDebugEnabled()) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const message = `[Upstash Debug] ${scope} ${operation} completed in ${formatDuration(startedAt)} status=${status}`;
|
|
40
|
+
if (error == null) {
|
|
41
|
+
console.log(message);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
46
|
+
console.log(`${message} error=${errorMessage}`);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const isPromiseLike = <T>(value: unknown): value is Promise<T> =>
|
|
50
|
+
typeof value === 'object' && value !== null && typeof (value as Promise<T>).then === 'function';
|
|
51
|
+
|
|
52
|
+
const trackUpstashOperation = <T>(
|
|
53
|
+
scope: string,
|
|
54
|
+
operation: string,
|
|
55
|
+
startedAt: number,
|
|
56
|
+
run: () => T
|
|
57
|
+
): T => {
|
|
58
|
+
try {
|
|
59
|
+
const result = run();
|
|
60
|
+
if (isPromiseLike(result)) {
|
|
61
|
+
return result.then(
|
|
62
|
+
(resolved) => {
|
|
63
|
+
logUpstashDuration(scope, operation, startedAt, 'ok');
|
|
64
|
+
return resolved;
|
|
65
|
+
},
|
|
66
|
+
(error) => {
|
|
67
|
+
logUpstashDuration(scope, operation, startedAt, 'error', error);
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
) as T;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
logUpstashDuration(scope, operation, startedAt, 'ok');
|
|
74
|
+
return result;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
logUpstashDuration(scope, operation, startedAt, 'error', error);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
24
81
|
const isNonEmpty = (value: string | undefined): value is string =>
|
|
25
82
|
typeof value === 'string' && value.trim().length > 0;
|
|
26
83
|
|
|
@@ -115,46 +172,54 @@ const createPrefixedPipeline = <T extends object>(target: T, prefix: string): T
|
|
|
115
172
|
}
|
|
116
173
|
|
|
117
174
|
return (...args: unknown[]) => {
|
|
175
|
+
const operation = typeof prop === 'string' ? prop : String(prop);
|
|
176
|
+
const startedAt = Date.now();
|
|
118
177
|
if (prop === 'eval' || prop === 'evalsha' || prop === 'evalro' || prop === 'evalshaRo') {
|
|
119
178
|
const [script, keys, argv] = args as [string, string[], unknown[]];
|
|
120
|
-
return (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
179
|
+
return trackUpstashOperation('Redis', operation, startedAt, () =>
|
|
180
|
+
(value as (...innerArgs: unknown[]) => unknown).call(
|
|
181
|
+
obj,
|
|
182
|
+
script,
|
|
183
|
+
prefixRedisKeys(prefix, keys),
|
|
184
|
+
argv
|
|
185
|
+
)
|
|
125
186
|
);
|
|
126
187
|
}
|
|
127
188
|
|
|
128
189
|
if (prop === 'pipeline' || prop === 'multi') {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (typeof prop === 'string' && keyArrayCommands.has(prop)) {
|
|
134
|
-
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
135
|
-
return (value as (...innerArgs: unknown[]) => unknown).apply(obj, nextArgs);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (typeof prop === 'string' && allStringKeyCommands.has(prop)) {
|
|
139
|
-
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
140
|
-
return (value as (...innerArgs: unknown[]) => unknown).apply(obj, nextArgs);
|
|
190
|
+
return trackUpstashOperation('Redis', operation, startedAt, () => {
|
|
191
|
+
const nested = (value as (...innerArgs: unknown[]) => unknown).call(obj);
|
|
192
|
+
return createPrefixedPipeline(nested as T, prefix);
|
|
193
|
+
});
|
|
141
194
|
}
|
|
142
195
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
196
|
+
return trackUpstashOperation('Redis', operation, startedAt, () => {
|
|
197
|
+
if (typeof prop === 'string' && keyArrayCommands.has(prop)) {
|
|
198
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
199
|
+
return (value as (...innerArgs: unknown[]) => unknown).apply(obj, nextArgs);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (typeof prop === 'string' && allStringKeyCommands.has(prop)) {
|
|
203
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
204
|
+
return (value as (...innerArgs: unknown[]) => unknown).apply(obj, nextArgs);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (prop === 'mset') {
|
|
208
|
+
const [entries] = args as [Record<string, unknown>];
|
|
209
|
+
const prefixedEntries = Object.fromEntries(
|
|
210
|
+
Object.entries(entries).map(([key, entryValue]) => [prefixRedisKey(prefix, key), entryValue])
|
|
211
|
+
);
|
|
212
|
+
return (value as (...innerArgs: unknown[]) => unknown).call(obj, prefixedEntries);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (prop === 'hmget') {
|
|
216
|
+
const nextArgs = prefixFirstStringArg(args, prefix);
|
|
217
|
+
return (value as (...innerArgs: unknown[]) => unknown).apply(obj, nextArgs);
|
|
218
|
+
}
|
|
150
219
|
|
|
151
|
-
if (prop === 'hmget') {
|
|
152
220
|
const nextArgs = prefixFirstStringArg(args, prefix);
|
|
153
221
|
return (value as (...innerArgs: unknown[]) => unknown).apply(obj, nextArgs);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const nextArgs = prefixFirstStringArg(args, prefix);
|
|
157
|
-
return (value as (...innerArgs: unknown[]) => unknown).apply(obj, nextArgs);
|
|
222
|
+
});
|
|
158
223
|
};
|
|
159
224
|
},
|
|
160
225
|
});
|
|
@@ -393,11 +458,22 @@ export const withRedis = async <T>(fn: (redis: Redis) => Promise<T> | T): Promis
|
|
|
393
458
|
};
|
|
394
459
|
|
|
395
460
|
export const withQstash = async <T>(
|
|
396
|
-
fn: (qstash: QstashClient) => Promise<T> | T
|
|
461
|
+
fn: (qstash: QstashClient) => Promise<T> | T,
|
|
462
|
+
operation = 'request'
|
|
397
463
|
): Promise<T | null> => {
|
|
464
|
+
const startedAt = Date.now();
|
|
398
465
|
const qstash = await ensureQstash();
|
|
399
466
|
if (!qstash) {
|
|
467
|
+
logUpstashDuration('QStash', operation, startedAt, 'unavailable');
|
|
400
468
|
return null;
|
|
401
469
|
}
|
|
402
|
-
|
|
470
|
+
|
|
471
|
+
try {
|
|
472
|
+
const result = await fn(qstash);
|
|
473
|
+
logUpstashDuration('QStash', operation, startedAt, 'ok');
|
|
474
|
+
return result;
|
|
475
|
+
} catch (error) {
|
|
476
|
+
logUpstashDuration('QStash', operation, startedAt, 'error', error);
|
|
477
|
+
throw error;
|
|
478
|
+
}
|
|
403
479
|
};
|