@windrun-huaiin/backend-core 20.0.0 → 20.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +6 -0
- package/dist/index.mjs +2 -2
- package/dist/lib/index.js +6 -0
- package/dist/lib/index.mjs +2 -2
- package/dist/lib/upstash/qstash.d.ts +25 -0
- package/dist/lib/upstash/qstash.d.ts.map +1 -1
- package/dist/lib/upstash/qstash.js +52 -0
- package/dist/lib/upstash/qstash.mjs +52 -2
- package/dist/lib/upstash/redis-structures.d.ts +1 -1
- package/dist/lib/upstash/redis-structures.d.ts.map +1 -1
- package/dist/lib/upstash/redis-structures.js +20 -21
- package/dist/lib/upstash/redis-structures.mjs +20 -21
- package/dist/lib/upstash-config.d.ts +4 -0
- package/dist/lib/upstash-config.d.ts.map +1 -1
- package/dist/lib/upstash-config.js +46 -10
- package/dist/lib/upstash-config.mjs +43 -11
- package/package.json +1 -1
- package/src/lib/upstash/qstash.ts +75 -1
- package/src/lib/upstash/redis-structures.ts +25 -23
- package/src/lib/upstash-config.ts +51 -11
package/dist/index.js
CHANGED
|
@@ -115,7 +115,11 @@ exports.fetchPaymentId = stripeConfig.fetchPaymentId;
|
|
|
115
115
|
exports.getStripe = stripeConfig.getStripe;
|
|
116
116
|
exports.updateSubscription = stripeConfig.updateSubscription;
|
|
117
117
|
exports.validateStripeWebhook = stripeConfig.validateStripeWebhook;
|
|
118
|
+
exports.getPrefixedQstashQueueName = upstashConfig.getPrefixedQstashQueueName;
|
|
119
|
+
exports.getPrefixedRedisKey = upstashConfig.getPrefixedRedisKey;
|
|
120
|
+
exports.getPrefixedRedisKeys = upstashConfig.getPrefixedRedisKeys;
|
|
118
121
|
exports.getQstash = upstashConfig.getQstash;
|
|
122
|
+
exports.getQstashNamePrefix = upstashConfig.getQstashNamePrefix;
|
|
119
123
|
exports.getRedis = upstashConfig.getRedis;
|
|
120
124
|
exports.withQstash = upstashConfig.withQstash;
|
|
121
125
|
exports.withRedis = upstashConfig.withRedis;
|
|
@@ -171,7 +175,9 @@ exports.smembers = redisStructures.smembers;
|
|
|
171
175
|
exports.srem = redisStructures.srem;
|
|
172
176
|
exports.ttl = redisStructures.ttl;
|
|
173
177
|
exports.cancelSchedule = qstash.cancelSchedule;
|
|
178
|
+
exports.publishBroadcastMessage = qstash.publishBroadcastMessage;
|
|
174
179
|
exports.publishDelayedMessage = qstash.publishDelayedMessage;
|
|
180
|
+
exports.publishFIFOQueueMessage = qstash.publishFIFOQueueMessage;
|
|
175
181
|
exports.publishMessage = qstash.publishMessage;
|
|
176
182
|
exports.scheduleMessage = qstash.scheduleMessage;
|
|
177
183
|
exports.verifyQstashSignature = qstash.verifyQstashSignature;
|
package/dist/index.mjs
CHANGED
|
@@ -26,10 +26,10 @@ export { getMoneyPriceInitUserContext } from './lib/money-price-helper.mjs';
|
|
|
26
26
|
export { fingerprintConfig } from './lib/fingerprint-config.mjs';
|
|
27
27
|
export { creditsConfig, freeAmount, freeExpiredDays, freeRegisterAmount, oneTimeExpiredDays } from './lib/credit-init.mjs';
|
|
28
28
|
export { ActiveSubscriptionExistsError, cancelSubscription, createCheckoutSession, createCustomerPortalSession, createOrGetCustomer, fetchPaymentId, getStripe, updateSubscription, validateStripeWebhook } from './lib/stripe-config.mjs';
|
|
29
|
-
export { getQstash, getRedis, withQstash, withRedis } from './lib/upstash-config.mjs';
|
|
29
|
+
export { getPrefixedQstashQueueName, getPrefixedRedisKey, getPrefixedRedisKeys, getQstash, getQstashNamePrefix, getRedis, withQstash, withRedis } from './lib/upstash-config.mjs';
|
|
30
30
|
export { acquireLock, releaseLock, withLock } from './lib/upstash/redis-lock.mjs';
|
|
31
31
|
export { getTargetLikeCount, getUserLikedTargets, isTargetLiked, likeTarget, unlikeTarget } from './lib/upstash/redis-like.mjs';
|
|
32
32
|
export { addFavorite, getFavoriteCount, getUserFavorites, isFavorited, removeFavorite } from './lib/upstash/redis-favorite.mjs';
|
|
33
33
|
export { getCounter, getUniqueCounter, incrCounter, incrUniqueCounter } from './lib/upstash/redis-counter.mjs';
|
|
34
34
|
export { del, deleteHashField, deleteKey, exists, expire, getHashAll, getHashField, getHashJson, getJson, getString, hexists, hkeys, hlen, hmget, hmset, listLength, mget, mgetJson, mset, msetJson, pipeline, popList, pushList, rangeList, sadd, scard, setHashField, setHashJson, setJson, setString, sismember, smembers, srem, ttl } from './lib/upstash/redis-structures.mjs';
|
|
35
|
-
export { cancelSchedule, publishDelayedMessage, publishMessage, scheduleMessage, verifyQstashSignature } from './lib/upstash/qstash.mjs';
|
|
35
|
+
export { cancelSchedule, publishBroadcastMessage, publishDelayedMessage, publishFIFOQueueMessage, publishMessage, scheduleMessage, verifyQstashSignature } from './lib/upstash/qstash.mjs';
|
package/dist/lib/index.js
CHANGED
|
@@ -35,7 +35,11 @@ exports.fetchPaymentId = stripeConfig.fetchPaymentId;
|
|
|
35
35
|
exports.getStripe = stripeConfig.getStripe;
|
|
36
36
|
exports.updateSubscription = stripeConfig.updateSubscription;
|
|
37
37
|
exports.validateStripeWebhook = stripeConfig.validateStripeWebhook;
|
|
38
|
+
exports.getPrefixedQstashQueueName = upstashConfig.getPrefixedQstashQueueName;
|
|
39
|
+
exports.getPrefixedRedisKey = upstashConfig.getPrefixedRedisKey;
|
|
40
|
+
exports.getPrefixedRedisKeys = upstashConfig.getPrefixedRedisKeys;
|
|
38
41
|
exports.getQstash = upstashConfig.getQstash;
|
|
42
|
+
exports.getQstashNamePrefix = upstashConfig.getQstashNamePrefix;
|
|
39
43
|
exports.getRedis = upstashConfig.getRedis;
|
|
40
44
|
exports.withQstash = upstashConfig.withQstash;
|
|
41
45
|
exports.withRedis = upstashConfig.withRedis;
|
|
@@ -91,7 +95,9 @@ exports.smembers = redisStructures.smembers;
|
|
|
91
95
|
exports.srem = redisStructures.srem;
|
|
92
96
|
exports.ttl = redisStructures.ttl;
|
|
93
97
|
exports.cancelSchedule = qstash.cancelSchedule;
|
|
98
|
+
exports.publishBroadcastMessage = qstash.publishBroadcastMessage;
|
|
94
99
|
exports.publishDelayedMessage = qstash.publishDelayedMessage;
|
|
100
|
+
exports.publishFIFOQueueMessage = qstash.publishFIFOQueueMessage;
|
|
95
101
|
exports.publishMessage = qstash.publishMessage;
|
|
96
102
|
exports.scheduleMessage = qstash.scheduleMessage;
|
|
97
103
|
exports.verifyQstashSignature = qstash.verifyQstashSignature;
|
package/dist/lib/index.mjs
CHANGED
|
@@ -3,10 +3,10 @@ export { getMoneyPriceInitUserContext } from './money-price-helper.mjs';
|
|
|
3
3
|
export { fingerprintConfig } from './fingerprint-config.mjs';
|
|
4
4
|
export { creditsConfig, freeAmount, freeExpiredDays, freeRegisterAmount, oneTimeExpiredDays } from './credit-init.mjs';
|
|
5
5
|
export { ActiveSubscriptionExistsError, cancelSubscription, createCheckoutSession, createCustomerPortalSession, createOrGetCustomer, fetchPaymentId, getStripe, updateSubscription, validateStripeWebhook } from './stripe-config.mjs';
|
|
6
|
-
export { getQstash, getRedis, withQstash, withRedis } from './upstash-config.mjs';
|
|
6
|
+
export { getPrefixedQstashQueueName, getPrefixedRedisKey, getPrefixedRedisKeys, getQstash, getQstashNamePrefix, getRedis, withQstash, withRedis } from './upstash-config.mjs';
|
|
7
7
|
export { acquireLock, releaseLock, withLock } from './upstash/redis-lock.mjs';
|
|
8
8
|
export { getTargetLikeCount, getUserLikedTargets, isTargetLiked, likeTarget, unlikeTarget } from './upstash/redis-like.mjs';
|
|
9
9
|
export { addFavorite, getFavoriteCount, getUserFavorites, isFavorited, removeFavorite } from './upstash/redis-favorite.mjs';
|
|
10
10
|
export { getCounter, getUniqueCounter, incrCounter, incrUniqueCounter } from './upstash/redis-counter.mjs';
|
|
11
11
|
export { del, deleteHashField, deleteKey, exists, expire, getHashAll, getHashField, getHashJson, getJson, getString, hexists, hkeys, hlen, hmget, hmset, listLength, mget, mgetJson, mset, msetJson, pipeline, popList, pushList, rangeList, sadd, scard, setHashField, setHashJson, setJson, setString, sismember, smembers, srem, ttl } from './upstash/redis-structures.mjs';
|
|
12
|
-
export { cancelSchedule, publishDelayedMessage, publishMessage, scheduleMessage, verifyQstashSignature } from './upstash/qstash.mjs';
|
|
12
|
+
export { cancelSchedule, publishBroadcastMessage, publishDelayedMessage, publishFIFOQueueMessage, publishMessage, scheduleMessage, verifyQstashSignature } from './upstash/qstash.mjs';
|
|
@@ -7,6 +7,15 @@ export interface PublishMessageOptions<TBody extends PublishBody = PublishBody>
|
|
|
7
7
|
url: string;
|
|
8
8
|
body: TBody;
|
|
9
9
|
}
|
|
10
|
+
export interface PublishBroadcastMessageOptions<TBody extends PublishBody = PublishBody> {
|
|
11
|
+
urlGroup: string;
|
|
12
|
+
body: TBody;
|
|
13
|
+
}
|
|
14
|
+
export interface PublishFIFOQueueMessageOptions<TBody extends PublishBody = PublishBody> {
|
|
15
|
+
queueName: string;
|
|
16
|
+
url: string;
|
|
17
|
+
body: TBody;
|
|
18
|
+
}
|
|
10
19
|
/**
|
|
11
20
|
* Publish a message. Returns message id or null if QStash is unavailable.
|
|
12
21
|
*/
|
|
@@ -14,6 +23,22 @@ export declare const publishMessage: <TBody extends PublishBody>(options: Publis
|
|
|
14
23
|
messageId: string | null;
|
|
15
24
|
message: QstashEnvelope<TBody>;
|
|
16
25
|
} | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Publish a broadcast message to a QStash URL Group.
|
|
28
|
+
* Returns message ids or null if QStash is unavailable.
|
|
29
|
+
*/
|
|
30
|
+
export declare const publishBroadcastMessage: <TBody extends PublishBody>(options: PublishBroadcastMessageOptions<TBody>) => Promise<{
|
|
31
|
+
messageIds: string[];
|
|
32
|
+
message: QstashEnvelope<TBody>;
|
|
33
|
+
} | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Publish a single-recipient message into a QStash FIFO queue.
|
|
36
|
+
* Returns message id or null if QStash is unavailable.
|
|
37
|
+
*/
|
|
38
|
+
export declare const publishFIFOQueueMessage: <TBody extends PublishBody>(options: PublishFIFOQueueMessageOptions<TBody>) => Promise<{
|
|
39
|
+
messageId: string | null;
|
|
40
|
+
message: QstashEnvelope<TBody>;
|
|
41
|
+
} | null>;
|
|
17
42
|
/**
|
|
18
43
|
* Publish a delayed message. Returns message id or null if QStash is unavailable.
|
|
19
44
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qstash.d.ts","sourceRoot":"","sources":["../../../src/lib/upstash/qstash.ts"],"names":[],"mappings":"AA8CA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAErF,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,KAAK,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,KAAK,CAAC;CACb;AAiBD;;GAEG;AACH,eAAO,MAAM,cAAc,GAAU,KAAK,SAAS,WAAW,EAC5D,SAAS,qBAAqB,CAAC,KAAK,CAAC,KACpC,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG,IAAI,CAa7E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAU,KAAK,SAAS,WAAW,EACnE,SAAS,qBAAqB,CAAC,KAAK,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,KAC3D,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG,IAAI,CAc7E,CAAC;AAEF,MAAM,WAAW,sBAAsB,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW,CAC7E,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,GAAU,KAAK,SAAS,WAAW,EAC7D,SAAS,sBAAsB,CAAC,KAAK,CAAC,KACrC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG,IAAI,CAsB9E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,GAAU,YAAY,MAAM,KAAG,OAAO,CAAC,OAAO,CAexE,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAU,SAAS,mBAAmB,KAAG,OAAO,CAAC,OAAO,CAqBzF,CAAC"}
|
|
1
|
+
{"version":3,"file":"qstash.d.ts","sourceRoot":"","sources":["../../../src/lib/upstash/qstash.ts"],"names":[],"mappings":"AA8CA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;AAErF,MAAM,WAAW,cAAc,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW;IACrE,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,KAAK,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,KAAK,CAAC;CACb;AAED,MAAM,WAAW,8BAA8B,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,CAAC;CACb;AAED,MAAM,WAAW,8BAA8B,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW;IACrF,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,KAAK,CAAC;CACb;AAiBD;;GAEG;AACH,eAAO,MAAM,cAAc,GAAU,KAAK,SAAS,WAAW,EAC5D,SAAS,qBAAqB,CAAC,KAAK,CAAC,KACpC,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG,IAAI,CAa7E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAAU,KAAK,SAAS,WAAW,EACrE,SAAS,8BAA8B,CAAC,KAAK,CAAC,KAC7C,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG,IAAI,CA0BzE,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAAU,KAAK,SAAS,WAAW,EACrE,SAAS,8BAA8B,CAAC,KAAK,CAAC,KAC7C,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG,IAAI,CAqB7E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAU,KAAK,SAAS,WAAW,EACnE,SAAS,qBAAqB,CAAC,KAAK,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,KAC3D,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG,IAAI,CAc7E,CAAC;AAEF,MAAM,WAAW,sBAAsB,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW,CAC7E,SAAQ,qBAAqB,CAAC,KAAK,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,eAAO,MAAM,eAAe,GAAU,KAAK,SAAS,WAAW,EAC7D,SAAS,sBAAsB,CAAC,KAAK,CAAC,KACrC,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,cAAc,CAAC,KAAK,CAAC,CAAA;CAAE,GAAG,IAAI,CAsB9E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,GAAU,YAAY,MAAM,KAAG,OAAO,CAAC,OAAO,CAexE,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAU,SAAS,mBAAmB,KAAG,OAAO,CAAC,OAAO,CAqBzF,CAAC"}
|
|
@@ -69,6 +69,56 @@ const publishMessage = (options) => tslib.__awaiter(void 0, void 0, void 0, func
|
|
|
69
69
|
};
|
|
70
70
|
}));
|
|
71
71
|
});
|
|
72
|
+
/**
|
|
73
|
+
* Publish a broadcast message to a QStash URL Group.
|
|
74
|
+
* Returns message ids or null if QStash is unavailable.
|
|
75
|
+
*/
|
|
76
|
+
const publishBroadcastMessage = (options) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
77
|
+
const message = createEnvelope(options.body);
|
|
78
|
+
return upstashConfig.withQstash((client) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
79
|
+
const result = yield client.publishJSON({
|
|
80
|
+
urlGroup: options.urlGroup,
|
|
81
|
+
body: message,
|
|
82
|
+
});
|
|
83
|
+
const messageIds = Array.isArray(result)
|
|
84
|
+
? result
|
|
85
|
+
.map((item) => typeof item === 'string' ? item : typeof (item === null || item === void 0 ? void 0 : item.messageId) === 'string' ? item.messageId : null)
|
|
86
|
+
.filter((messageId) => typeof messageId === 'string')
|
|
87
|
+
: typeof result === 'string'
|
|
88
|
+
? [result]
|
|
89
|
+
: typeof (result === null || result === void 0 ? void 0 : result.messageId) === 'string'
|
|
90
|
+
? [result.messageId]
|
|
91
|
+
: [];
|
|
92
|
+
return {
|
|
93
|
+
messageIds,
|
|
94
|
+
message,
|
|
95
|
+
};
|
|
96
|
+
}));
|
|
97
|
+
});
|
|
98
|
+
/**
|
|
99
|
+
* Publish a single-recipient message into a QStash FIFO queue.
|
|
100
|
+
* Returns message id or null if QStash is unavailable.
|
|
101
|
+
*/
|
|
102
|
+
const publishFIFOQueueMessage = (options) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
103
|
+
const message = createEnvelope(options.body);
|
|
104
|
+
const queueName = upstashConfig.getPrefixedQstashQueueName(options.queueName);
|
|
105
|
+
return upstashConfig.withQstash((client) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
106
|
+
var _a, _b;
|
|
107
|
+
const anyClient = client;
|
|
108
|
+
const queueClient = (_a = anyClient.queue) === null || _a === void 0 ? void 0 : _a.call(anyClient, { queueName });
|
|
109
|
+
if (!(queueClient === null || queueClient === void 0 ? void 0 : queueClient.enqueueJSON)) {
|
|
110
|
+
throw new Error('QStash queue enqueueJSON API is unavailable');
|
|
111
|
+
}
|
|
112
|
+
const result = yield queueClient.enqueueJSON({
|
|
113
|
+
url: options.url,
|
|
114
|
+
body: message,
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
messageId: typeof result === 'string' ? result : (_b = result === null || result === void 0 ? void 0 : result.messageId) !== null && _b !== void 0 ? _b : null,
|
|
118
|
+
message,
|
|
119
|
+
};
|
|
120
|
+
}));
|
|
121
|
+
});
|
|
72
122
|
/**
|
|
73
123
|
* Publish a delayed message. Returns message id or null if QStash is unavailable.
|
|
74
124
|
*/
|
|
@@ -154,7 +204,9 @@ const verifyQstashSignature = (options) => tslib.__awaiter(void 0, void 0, void
|
|
|
154
204
|
});
|
|
155
205
|
|
|
156
206
|
exports.cancelSchedule = cancelSchedule;
|
|
207
|
+
exports.publishBroadcastMessage = publishBroadcastMessage;
|
|
157
208
|
exports.publishDelayedMessage = publishDelayedMessage;
|
|
209
|
+
exports.publishFIFOQueueMessage = publishFIFOQueueMessage;
|
|
158
210
|
exports.publishMessage = publishMessage;
|
|
159
211
|
exports.scheduleMessage = scheduleMessage;
|
|
160
212
|
exports.verifyQstashSignature = verifyQstashSignature;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __awaiter } from 'tslib';
|
|
2
2
|
import { Receiver } from '@upstash/qstash';
|
|
3
|
-
import { withQstash } from '../upstash-config.mjs';
|
|
3
|
+
import { withQstash, getPrefixedQstashQueueName } from '../upstash-config.mjs';
|
|
4
4
|
|
|
5
5
|
let cachedReceiver = null;
|
|
6
6
|
let receiverWarnedMissingEnv = false;
|
|
@@ -67,6 +67,56 @@ const publishMessage = (options) => __awaiter(void 0, void 0, void 0, function*
|
|
|
67
67
|
};
|
|
68
68
|
}));
|
|
69
69
|
});
|
|
70
|
+
/**
|
|
71
|
+
* Publish a broadcast message to a QStash URL Group.
|
|
72
|
+
* Returns message ids or null if QStash is unavailable.
|
|
73
|
+
*/
|
|
74
|
+
const publishBroadcastMessage = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
75
|
+
const message = createEnvelope(options.body);
|
|
76
|
+
return withQstash((client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
77
|
+
const result = yield client.publishJSON({
|
|
78
|
+
urlGroup: options.urlGroup,
|
|
79
|
+
body: message,
|
|
80
|
+
});
|
|
81
|
+
const messageIds = Array.isArray(result)
|
|
82
|
+
? result
|
|
83
|
+
.map((item) => typeof item === 'string' ? item : typeof (item === null || item === void 0 ? void 0 : item.messageId) === 'string' ? item.messageId : null)
|
|
84
|
+
.filter((messageId) => typeof messageId === 'string')
|
|
85
|
+
: typeof result === 'string'
|
|
86
|
+
? [result]
|
|
87
|
+
: typeof (result === null || result === void 0 ? void 0 : result.messageId) === 'string'
|
|
88
|
+
? [result.messageId]
|
|
89
|
+
: [];
|
|
90
|
+
return {
|
|
91
|
+
messageIds,
|
|
92
|
+
message,
|
|
93
|
+
};
|
|
94
|
+
}));
|
|
95
|
+
});
|
|
96
|
+
/**
|
|
97
|
+
* Publish a single-recipient message into a QStash FIFO queue.
|
|
98
|
+
* Returns message id or null if QStash is unavailable.
|
|
99
|
+
*/
|
|
100
|
+
const publishFIFOQueueMessage = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
101
|
+
const message = createEnvelope(options.body);
|
|
102
|
+
const queueName = getPrefixedQstashQueueName(options.queueName);
|
|
103
|
+
return withQstash((client) => __awaiter(void 0, void 0, void 0, function* () {
|
|
104
|
+
var _a, _b;
|
|
105
|
+
const anyClient = client;
|
|
106
|
+
const queueClient = (_a = anyClient.queue) === null || _a === void 0 ? void 0 : _a.call(anyClient, { queueName });
|
|
107
|
+
if (!(queueClient === null || queueClient === void 0 ? void 0 : queueClient.enqueueJSON)) {
|
|
108
|
+
throw new Error('QStash queue enqueueJSON API is unavailable');
|
|
109
|
+
}
|
|
110
|
+
const result = yield queueClient.enqueueJSON({
|
|
111
|
+
url: options.url,
|
|
112
|
+
body: message,
|
|
113
|
+
});
|
|
114
|
+
return {
|
|
115
|
+
messageId: typeof result === 'string' ? result : (_b = result === null || result === void 0 ? void 0 : result.messageId) !== null && _b !== void 0 ? _b : null,
|
|
116
|
+
message,
|
|
117
|
+
};
|
|
118
|
+
}));
|
|
119
|
+
});
|
|
70
120
|
/**
|
|
71
121
|
* Publish a delayed message. Returns message id or null if QStash is unavailable.
|
|
72
122
|
*/
|
|
@@ -151,4 +201,4 @@ const verifyQstashSignature = (options) => __awaiter(void 0, void 0, void 0, fun
|
|
|
151
201
|
return true;
|
|
152
202
|
});
|
|
153
203
|
|
|
154
|
-
export { cancelSchedule, publishDelayedMessage, publishMessage, scheduleMessage, verifyQstashSignature };
|
|
204
|
+
export { cancelSchedule, publishBroadcastMessage, publishDelayedMessage, publishFIFOQueueMessage, publishMessage, scheduleMessage, verifyQstashSignature };
|
|
@@ -30,7 +30,7 @@ export declare const setJson: <T>(key: string, value: T, ttlSec?: number) => Pro
|
|
|
30
30
|
*/
|
|
31
31
|
export declare const getJson: <T>(key: string) => Promise<T | null>;
|
|
32
32
|
/**
|
|
33
|
-
* MGET JSON values
|
|
33
|
+
* MGET JSON values. Missing or invalid string values are returned as null.
|
|
34
34
|
*/
|
|
35
35
|
export declare const mgetJson: <T>(keys: string[]) => Promise<(T | null)[] | null>;
|
|
36
36
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redis-structures.d.ts","sourceRoot":"","sources":["../../../src/lib/upstash/redis-structures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAI5C,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACzD,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;AACrD,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC1D,MAAM,MAAM,oBAAoB,CAAC,OAAO,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,KAAK;IACvF,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9B,CAAC;
|
|
1
|
+
{"version":3,"file":"redis-structures.d.ts","sourceRoot":"","sources":["../../../src/lib/upstash/redis-structures.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
var tslib = require('tslib');
|
|
4
4
|
var upstashConfig = require('../upstash-config.js');
|
|
5
5
|
|
|
6
|
+
const parseJsonPayload = (payload) => {
|
|
7
|
+
if (payload == null) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
if (typeof payload === 'string') {
|
|
11
|
+
if (!payload) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(payload);
|
|
16
|
+
}
|
|
17
|
+
catch (_a) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return payload;
|
|
22
|
+
};
|
|
6
23
|
/**
|
|
7
24
|
* Set a plain string value with optional TTL (seconds).
|
|
8
25
|
*/
|
|
@@ -66,19 +83,11 @@ const setJson = (key, value, ttlSec) => tslib.__awaiter(void 0, void 0, void 0,
|
|
|
66
83
|
const getJson = (key) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
67
84
|
return upstashConfig.withRedis((redis) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
68
85
|
const payload = yield redis.get(key);
|
|
69
|
-
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
return JSON.parse(payload);
|
|
74
|
-
}
|
|
75
|
-
catch (_a) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
86
|
+
return parseJsonPayload(payload);
|
|
78
87
|
}));
|
|
79
88
|
});
|
|
80
89
|
/**
|
|
81
|
-
* MGET JSON values
|
|
90
|
+
* MGET JSON values. Missing or invalid string values are returned as null.
|
|
82
91
|
*/
|
|
83
92
|
const mgetJson = (keys) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
84
93
|
return upstashConfig.withRedis((redis) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -86,17 +95,7 @@ const mgetJson = (keys) => tslib.__awaiter(void 0, void 0, void 0, function* ()
|
|
|
86
95
|
return [];
|
|
87
96
|
}
|
|
88
97
|
const payloads = yield redis.mget(...keys);
|
|
89
|
-
return payloads.map((payload) =>
|
|
90
|
-
if (!payload) {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
return JSON.parse(payload);
|
|
95
|
-
}
|
|
96
|
-
catch (_a) {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
});
|
|
98
|
+
return payloads.map((payload) => parseJsonPayload(payload));
|
|
100
99
|
}));
|
|
101
100
|
});
|
|
102
101
|
/**
|
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { __awaiter } from 'tslib';
|
|
2
2
|
import { withRedis } from '../upstash-config.mjs';
|
|
3
3
|
|
|
4
|
+
const parseJsonPayload = (payload) => {
|
|
5
|
+
if (payload == null) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
if (typeof payload === 'string') {
|
|
9
|
+
if (!payload) {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(payload);
|
|
14
|
+
}
|
|
15
|
+
catch (_a) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return payload;
|
|
20
|
+
};
|
|
4
21
|
/**
|
|
5
22
|
* Set a plain string value with optional TTL (seconds).
|
|
6
23
|
*/
|
|
@@ -64,19 +81,11 @@ const setJson = (key, value, ttlSec) => __awaiter(void 0, void 0, void 0, functi
|
|
|
64
81
|
const getJson = (key) => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
82
|
return withRedis((redis) => __awaiter(void 0, void 0, void 0, function* () {
|
|
66
83
|
const payload = yield redis.get(key);
|
|
67
|
-
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
try {
|
|
71
|
-
return JSON.parse(payload);
|
|
72
|
-
}
|
|
73
|
-
catch (_a) {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
84
|
+
return parseJsonPayload(payload);
|
|
76
85
|
}));
|
|
77
86
|
});
|
|
78
87
|
/**
|
|
79
|
-
* MGET JSON values
|
|
88
|
+
* MGET JSON values. Missing or invalid string values are returned as null.
|
|
80
89
|
*/
|
|
81
90
|
const mgetJson = (keys) => __awaiter(void 0, void 0, void 0, function* () {
|
|
82
91
|
return withRedis((redis) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -84,17 +93,7 @@ const mgetJson = (keys) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
84
93
|
return [];
|
|
85
94
|
}
|
|
86
95
|
const payloads = yield redis.mget(...keys);
|
|
87
|
-
return payloads.map((payload) =>
|
|
88
|
-
if (!payload) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
try {
|
|
92
|
-
return JSON.parse(payload);
|
|
93
|
-
}
|
|
94
|
-
catch (_a) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
96
|
+
return payloads.map((payload) => parseJsonPayload(payload));
|
|
98
97
|
}));
|
|
99
98
|
});
|
|
100
99
|
/**
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Redis } from '@upstash/redis';
|
|
2
2
|
import { Client as QstashClient } from '@upstash/qstash';
|
|
3
|
+
export declare const getPrefixedRedisKey: (key: string) => string;
|
|
4
|
+
export declare const getPrefixedRedisKeys: (keys: string[]) => string[];
|
|
5
|
+
export declare const getQstashNamePrefix: () => string;
|
|
6
|
+
export declare const getPrefixedQstashQueueName: (name: string) => string;
|
|
3
7
|
/**
|
|
4
8
|
* Get the Upstash Redis client. Returns null when required env vars are missing/invalid.
|
|
5
9
|
*
|
|
@@ -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;AA2DzD,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;AAiMF;;;;;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,KAC3C,OAAO,CAAC,CAAC,GAAG,IAAI,CAMlB,CAAC"}
|
|
@@ -45,7 +45,19 @@ const getRedisKeyPrefix = () => {
|
|
|
45
45
|
const envSuffix = process.env.NODE_ENV === 'production' ? 'live' : 'test';
|
|
46
46
|
return `${getRequiredRedisAppName()}_${envSuffix}`;
|
|
47
47
|
};
|
|
48
|
-
const
|
|
48
|
+
const buildPrefixedRedisKey = (prefix, key) => `${prefix}:${key}`;
|
|
49
|
+
const getPrefixedRedisKey = (key) => buildPrefixedRedisKey(getRedisKeyPrefix(), key);
|
|
50
|
+
const getPrefixedRedisKeys = (keys) => keys.map(getPrefixedRedisKey);
|
|
51
|
+
const getQstashNamePrefix = () => {
|
|
52
|
+
return getRedisKeyPrefix();
|
|
53
|
+
};
|
|
54
|
+
const getPrefixedQstashQueueName = (name) => {
|
|
55
|
+
if (!isNonEmpty(name)) {
|
|
56
|
+
throw new Error('[Upstash QStash] queue name must not be empty');
|
|
57
|
+
}
|
|
58
|
+
return `${getQstashNamePrefix()}_queue_${name}`;
|
|
59
|
+
};
|
|
60
|
+
const prefixRedisKey = (prefix, key) => buildPrefixedRedisKey(prefix, key);
|
|
49
61
|
const prefixRedisKeys = (prefix, keys) => keys.map((key) => prefixRedisKey(prefix, key));
|
|
50
62
|
const prefixFirstStringArg = (args, prefix) => {
|
|
51
63
|
if (typeof args[0] !== 'string') {
|
|
@@ -55,8 +67,16 @@ const prefixFirstStringArg = (args, prefix) => {
|
|
|
55
67
|
nextArgs[0] = prefixRedisKey(prefix, args[0]);
|
|
56
68
|
return nextArgs;
|
|
57
69
|
};
|
|
58
|
-
const
|
|
59
|
-
return args.
|
|
70
|
+
const prefixVariadicKeyArgs = (args, prefix) => {
|
|
71
|
+
return args.flatMap((arg) => {
|
|
72
|
+
if (typeof arg === 'string') {
|
|
73
|
+
return [prefixRedisKey(prefix, arg)];
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(arg) && arg.every((key) => typeof key === 'string')) {
|
|
76
|
+
return prefixRedisKeys(prefix, arg);
|
|
77
|
+
}
|
|
78
|
+
return [arg];
|
|
79
|
+
});
|
|
60
80
|
};
|
|
61
81
|
const keyArrayCommands = new Set(['mget', 'del']);
|
|
62
82
|
const allStringKeyCommands = new Set(['exists']);
|
|
@@ -77,11 +97,11 @@ const createPrefixedPipeline = (target, prefix) => {
|
|
|
77
97
|
return createPrefixedPipeline(nested, prefix);
|
|
78
98
|
}
|
|
79
99
|
if (typeof prop === 'string' && keyArrayCommands.has(prop)) {
|
|
80
|
-
const nextArgs =
|
|
100
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
81
101
|
return value.apply(obj, nextArgs);
|
|
82
102
|
}
|
|
83
103
|
if (typeof prop === 'string' && allStringKeyCommands.has(prop)) {
|
|
84
|
-
const nextArgs =
|
|
104
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
85
105
|
return value.apply(obj, nextArgs);
|
|
86
106
|
}
|
|
87
107
|
if (prop === 'mset') {
|
|
@@ -111,7 +131,10 @@ const parseMinutes = (value, fallback) => {
|
|
|
111
131
|
};
|
|
112
132
|
const getRedisHealthIntervalMinutes = () => parseMinutes(process.env.UPSTASH_REDIS_HEALTHCHECK_INTERVAL_MINUTES, 10);
|
|
113
133
|
const getQstashHealthIntervalMinutes = () => parseMinutes(process.env.UPSTASH_QSTASH_HEALTHCHECK_INTERVAL_MINUTES, 10);
|
|
114
|
-
const getQstashHealthcheckUrl = () => {
|
|
134
|
+
const getQstashHealthcheckUrl = () => {
|
|
135
|
+
const url = process.env.UPSTASH_QSTASH_HEALTHCHECK_URL;
|
|
136
|
+
return isNonEmpty(url) ? url : null;
|
|
137
|
+
};
|
|
115
138
|
const scheduleRedisHealthCheck = () => {
|
|
116
139
|
if (redisHealthTimer || !cachedRedis) {
|
|
117
140
|
return;
|
|
@@ -148,7 +171,11 @@ const scheduleRedisHealthCheck = () => {
|
|
|
148
171
|
}), delayMs);
|
|
149
172
|
};
|
|
150
173
|
const checkQstashHealth = (token) => tslib.__awaiter(void 0, void 0, void 0, function* () {
|
|
151
|
-
const
|
|
174
|
+
const healthcheckUrl = getQstashHealthcheckUrl();
|
|
175
|
+
if (!healthcheckUrl) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const response = yield fetch(healthcheckUrl, {
|
|
152
179
|
method: 'GET',
|
|
153
180
|
headers: {
|
|
154
181
|
Authorization: `Bearer ${token}`,
|
|
@@ -160,7 +187,7 @@ const checkQstashHealth = (token) => tslib.__awaiter(void 0, void 0, void 0, fun
|
|
|
160
187
|
}
|
|
161
188
|
});
|
|
162
189
|
const scheduleQstashHealthCheck = (token) => {
|
|
163
|
-
if (qstashHealthTimer || !cachedQstash) {
|
|
190
|
+
if (qstashHealthTimer || !cachedQstash || !getQstashHealthcheckUrl()) {
|
|
164
191
|
return;
|
|
165
192
|
}
|
|
166
193
|
const delayMs = getQstashHealthIntervalMinutes() * 60000;
|
|
@@ -173,7 +200,6 @@ const scheduleQstashHealthCheck = (token) => {
|
|
|
173
200
|
yield checkQstashHealth(token);
|
|
174
201
|
}
|
|
175
202
|
catch (error) {
|
|
176
|
-
cachedQstash = null;
|
|
177
203
|
if (!qstashWarnedHealthCheck) {
|
|
178
204
|
qstashWarnedHealthCheck = true;
|
|
179
205
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -279,8 +305,14 @@ const ensureQstash = () => tslib.__awaiter(void 0, void 0, void 0, function* ()
|
|
|
279
305
|
}
|
|
280
306
|
try {
|
|
281
307
|
const client = new qstash.Client({ token: QSTASH_TOKEN });
|
|
282
|
-
yield checkQstashHealth(QSTASH_TOKEN);
|
|
283
308
|
cachedQstash = client;
|
|
309
|
+
checkQstashHealth(QSTASH_TOKEN).catch((error) => {
|
|
310
|
+
if (!qstashWarnedHealthCheck) {
|
|
311
|
+
qstashWarnedHealthCheck = true;
|
|
312
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
313
|
+
console.warn(`[Upstash Config] QStash health check failed: ${message}`);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
284
316
|
scheduleQstashHealthCheck(QSTASH_TOKEN);
|
|
285
317
|
return cachedQstash;
|
|
286
318
|
}
|
|
@@ -313,7 +345,11 @@ const withQstash = (fn) => tslib.__awaiter(void 0, void 0, void 0, function* ()
|
|
|
313
345
|
return fn(qstash);
|
|
314
346
|
});
|
|
315
347
|
|
|
348
|
+
exports.getPrefixedQstashQueueName = getPrefixedQstashQueueName;
|
|
349
|
+
exports.getPrefixedRedisKey = getPrefixedRedisKey;
|
|
350
|
+
exports.getPrefixedRedisKeys = getPrefixedRedisKeys;
|
|
316
351
|
exports.getQstash = getQstash;
|
|
352
|
+
exports.getQstashNamePrefix = getQstashNamePrefix;
|
|
317
353
|
exports.getRedis = getRedis;
|
|
318
354
|
exports.withQstash = withQstash;
|
|
319
355
|
exports.withRedis = withRedis;
|
|
@@ -43,7 +43,19 @@ const getRedisKeyPrefix = () => {
|
|
|
43
43
|
const envSuffix = process.env.NODE_ENV === 'production' ? 'live' : 'test';
|
|
44
44
|
return `${getRequiredRedisAppName()}_${envSuffix}`;
|
|
45
45
|
};
|
|
46
|
-
const
|
|
46
|
+
const buildPrefixedRedisKey = (prefix, key) => `${prefix}:${key}`;
|
|
47
|
+
const getPrefixedRedisKey = (key) => buildPrefixedRedisKey(getRedisKeyPrefix(), key);
|
|
48
|
+
const getPrefixedRedisKeys = (keys) => keys.map(getPrefixedRedisKey);
|
|
49
|
+
const getQstashNamePrefix = () => {
|
|
50
|
+
return getRedisKeyPrefix();
|
|
51
|
+
};
|
|
52
|
+
const getPrefixedQstashQueueName = (name) => {
|
|
53
|
+
if (!isNonEmpty(name)) {
|
|
54
|
+
throw new Error('[Upstash QStash] queue name must not be empty');
|
|
55
|
+
}
|
|
56
|
+
return `${getQstashNamePrefix()}_queue_${name}`;
|
|
57
|
+
};
|
|
58
|
+
const prefixRedisKey = (prefix, key) => buildPrefixedRedisKey(prefix, key);
|
|
47
59
|
const prefixRedisKeys = (prefix, keys) => keys.map((key) => prefixRedisKey(prefix, key));
|
|
48
60
|
const prefixFirstStringArg = (args, prefix) => {
|
|
49
61
|
if (typeof args[0] !== 'string') {
|
|
@@ -53,8 +65,16 @@ const prefixFirstStringArg = (args, prefix) => {
|
|
|
53
65
|
nextArgs[0] = prefixRedisKey(prefix, args[0]);
|
|
54
66
|
return nextArgs;
|
|
55
67
|
};
|
|
56
|
-
const
|
|
57
|
-
return args.
|
|
68
|
+
const prefixVariadicKeyArgs = (args, prefix) => {
|
|
69
|
+
return args.flatMap((arg) => {
|
|
70
|
+
if (typeof arg === 'string') {
|
|
71
|
+
return [prefixRedisKey(prefix, arg)];
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(arg) && arg.every((key) => typeof key === 'string')) {
|
|
74
|
+
return prefixRedisKeys(prefix, arg);
|
|
75
|
+
}
|
|
76
|
+
return [arg];
|
|
77
|
+
});
|
|
58
78
|
};
|
|
59
79
|
const keyArrayCommands = new Set(['mget', 'del']);
|
|
60
80
|
const allStringKeyCommands = new Set(['exists']);
|
|
@@ -75,11 +95,11 @@ const createPrefixedPipeline = (target, prefix) => {
|
|
|
75
95
|
return createPrefixedPipeline(nested, prefix);
|
|
76
96
|
}
|
|
77
97
|
if (typeof prop === 'string' && keyArrayCommands.has(prop)) {
|
|
78
|
-
const nextArgs =
|
|
98
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
79
99
|
return value.apply(obj, nextArgs);
|
|
80
100
|
}
|
|
81
101
|
if (typeof prop === 'string' && allStringKeyCommands.has(prop)) {
|
|
82
|
-
const nextArgs =
|
|
102
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
83
103
|
return value.apply(obj, nextArgs);
|
|
84
104
|
}
|
|
85
105
|
if (prop === 'mset') {
|
|
@@ -109,7 +129,10 @@ const parseMinutes = (value, fallback) => {
|
|
|
109
129
|
};
|
|
110
130
|
const getRedisHealthIntervalMinutes = () => parseMinutes(process.env.UPSTASH_REDIS_HEALTHCHECK_INTERVAL_MINUTES, 10);
|
|
111
131
|
const getQstashHealthIntervalMinutes = () => parseMinutes(process.env.UPSTASH_QSTASH_HEALTHCHECK_INTERVAL_MINUTES, 10);
|
|
112
|
-
const getQstashHealthcheckUrl = () => {
|
|
132
|
+
const getQstashHealthcheckUrl = () => {
|
|
133
|
+
const url = process.env.UPSTASH_QSTASH_HEALTHCHECK_URL;
|
|
134
|
+
return isNonEmpty(url) ? url : null;
|
|
135
|
+
};
|
|
113
136
|
const scheduleRedisHealthCheck = () => {
|
|
114
137
|
if (redisHealthTimer || !cachedRedis) {
|
|
115
138
|
return;
|
|
@@ -146,7 +169,11 @@ const scheduleRedisHealthCheck = () => {
|
|
|
146
169
|
}), delayMs);
|
|
147
170
|
};
|
|
148
171
|
const checkQstashHealth = (token) => __awaiter(void 0, void 0, void 0, function* () {
|
|
149
|
-
const
|
|
172
|
+
const healthcheckUrl = getQstashHealthcheckUrl();
|
|
173
|
+
if (!healthcheckUrl) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const response = yield fetch(healthcheckUrl, {
|
|
150
177
|
method: 'GET',
|
|
151
178
|
headers: {
|
|
152
179
|
Authorization: `Bearer ${token}`,
|
|
@@ -158,7 +185,7 @@ const checkQstashHealth = (token) => __awaiter(void 0, void 0, void 0, function*
|
|
|
158
185
|
}
|
|
159
186
|
});
|
|
160
187
|
const scheduleQstashHealthCheck = (token) => {
|
|
161
|
-
if (qstashHealthTimer || !cachedQstash) {
|
|
188
|
+
if (qstashHealthTimer || !cachedQstash || !getQstashHealthcheckUrl()) {
|
|
162
189
|
return;
|
|
163
190
|
}
|
|
164
191
|
const delayMs = getQstashHealthIntervalMinutes() * 60000;
|
|
@@ -171,7 +198,6 @@ const scheduleQstashHealthCheck = (token) => {
|
|
|
171
198
|
yield checkQstashHealth(token);
|
|
172
199
|
}
|
|
173
200
|
catch (error) {
|
|
174
|
-
cachedQstash = null;
|
|
175
201
|
if (!qstashWarnedHealthCheck) {
|
|
176
202
|
qstashWarnedHealthCheck = true;
|
|
177
203
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -277,8 +303,14 @@ const ensureQstash = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
277
303
|
}
|
|
278
304
|
try {
|
|
279
305
|
const client = new Client({ token: QSTASH_TOKEN });
|
|
280
|
-
yield checkQstashHealth(QSTASH_TOKEN);
|
|
281
306
|
cachedQstash = client;
|
|
307
|
+
checkQstashHealth(QSTASH_TOKEN).catch((error) => {
|
|
308
|
+
if (!qstashWarnedHealthCheck) {
|
|
309
|
+
qstashWarnedHealthCheck = true;
|
|
310
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
311
|
+
console.warn(`[Upstash Config] QStash health check failed: ${message}`);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
282
314
|
scheduleQstashHealthCheck(QSTASH_TOKEN);
|
|
283
315
|
return cachedQstash;
|
|
284
316
|
}
|
|
@@ -311,4 +343,4 @@ const withQstash = (fn) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
311
343
|
return fn(qstash);
|
|
312
344
|
});
|
|
313
345
|
|
|
314
|
-
export { getQstash, getRedis, withQstash, withRedis };
|
|
346
|
+
export { getPrefixedQstashQueueName, getPrefixedRedisKey, getPrefixedRedisKeys, getQstash, getQstashNamePrefix, getRedis, withQstash, withRedis };
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Receiver } from '@upstash/qstash';
|
|
2
|
-
import { withQstash } from '../upstash-config';
|
|
2
|
+
import { getPrefixedQstashQueueName, withQstash } from '../upstash-config';
|
|
3
3
|
|
|
4
4
|
let cachedReceiver: Receiver | null = null;
|
|
5
5
|
let receiverWarnedMissingEnv = false;
|
|
@@ -56,6 +56,17 @@ export interface PublishMessageOptions<TBody extends PublishBody = PublishBody>
|
|
|
56
56
|
body: TBody;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
export interface PublishBroadcastMessageOptions<TBody extends PublishBody = PublishBody> {
|
|
60
|
+
urlGroup: string;
|
|
61
|
+
body: TBody;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface PublishFIFOQueueMessageOptions<TBody extends PublishBody = PublishBody> {
|
|
65
|
+
queueName: string;
|
|
66
|
+
url: string;
|
|
67
|
+
body: TBody;
|
|
68
|
+
}
|
|
69
|
+
|
|
59
70
|
const generateSourceMessageId = (): string => {
|
|
60
71
|
try {
|
|
61
72
|
return crypto.randomUUID();
|
|
@@ -91,6 +102,69 @@ export const publishMessage = async <TBody extends PublishBody>(
|
|
|
91
102
|
});
|
|
92
103
|
};
|
|
93
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Publish a broadcast message to a QStash URL Group.
|
|
107
|
+
* Returns message ids or null if QStash is unavailable.
|
|
108
|
+
*/
|
|
109
|
+
export const publishBroadcastMessage = async <TBody extends PublishBody>(
|
|
110
|
+
options: PublishBroadcastMessageOptions<TBody>
|
|
111
|
+
): Promise<{ messageIds: string[]; message: QstashEnvelope<TBody> } | null> => {
|
|
112
|
+
const message = createEnvelope(options.body);
|
|
113
|
+
|
|
114
|
+
return withQstash(async (client) => {
|
|
115
|
+
const result = await (client as any).publishJSON({
|
|
116
|
+
urlGroup: options.urlGroup,
|
|
117
|
+
body: message,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const messageIds = Array.isArray(result)
|
|
121
|
+
? result
|
|
122
|
+
.map((item) =>
|
|
123
|
+
typeof item === 'string' ? item : typeof item?.messageId === 'string' ? item.messageId : null
|
|
124
|
+
)
|
|
125
|
+
.filter((messageId): messageId is string => typeof messageId === 'string')
|
|
126
|
+
: typeof result === 'string'
|
|
127
|
+
? [result]
|
|
128
|
+
: typeof result?.messageId === 'string'
|
|
129
|
+
? [result.messageId]
|
|
130
|
+
: [];
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
messageIds,
|
|
134
|
+
message,
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Publish a single-recipient message into a QStash FIFO queue.
|
|
141
|
+
* Returns message id or null if QStash is unavailable.
|
|
142
|
+
*/
|
|
143
|
+
export const publishFIFOQueueMessage = async <TBody extends PublishBody>(
|
|
144
|
+
options: PublishFIFOQueueMessageOptions<TBody>
|
|
145
|
+
): Promise<{ messageId: string | null; message: QstashEnvelope<TBody> } | null> => {
|
|
146
|
+
const message = createEnvelope(options.body);
|
|
147
|
+
const queueName = getPrefixedQstashQueueName(options.queueName);
|
|
148
|
+
|
|
149
|
+
return withQstash(async (client) => {
|
|
150
|
+
const anyClient = client as any;
|
|
151
|
+
const queueClient = anyClient.queue?.({ queueName });
|
|
152
|
+
if (!queueClient?.enqueueJSON) {
|
|
153
|
+
throw new Error('QStash queue enqueueJSON API is unavailable');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const result = await queueClient.enqueueJSON({
|
|
157
|
+
url: options.url,
|
|
158
|
+
body: message,
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
messageId: typeof result === 'string' ? result : result?.messageId ?? null,
|
|
163
|
+
message,
|
|
164
|
+
};
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
|
|
94
168
|
/**
|
|
95
169
|
* Publish a delayed message. Returns message id or null if QStash is unavailable.
|
|
96
170
|
*/
|
|
@@ -9,6 +9,26 @@ export type RedisPipelineBuilder<TResult> = (pipeline: ReturnType<Redis['pipelin
|
|
|
9
9
|
exec: () => Promise<TResult>;
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
const parseJsonPayload = <T>(payload: unknown): T | null => {
|
|
13
|
+
if (payload == null) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (typeof payload === 'string') {
|
|
18
|
+
if (!payload) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(payload) as T;
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return payload as T;
|
|
30
|
+
};
|
|
31
|
+
|
|
12
32
|
/**
|
|
13
33
|
* Set a plain string value with optional TTL (seconds).
|
|
14
34
|
*/
|
|
@@ -88,21 +108,13 @@ export const setJson = async <T>(
|
|
|
88
108
|
*/
|
|
89
109
|
export const getJson = async <T>(key: string): Promise<T | null> => {
|
|
90
110
|
return withRedis(async (redis) => {
|
|
91
|
-
const payload = await redis.get<
|
|
92
|
-
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
return JSON.parse(payload) as T;
|
|
98
|
-
} catch {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
111
|
+
const payload = await redis.get<unknown>(key);
|
|
112
|
+
return parseJsonPayload<T>(payload);
|
|
101
113
|
});
|
|
102
114
|
};
|
|
103
115
|
|
|
104
116
|
/**
|
|
105
|
-
* MGET JSON values
|
|
117
|
+
* MGET JSON values. Missing or invalid string values are returned as null.
|
|
106
118
|
*/
|
|
107
119
|
export const mgetJson = async <T>(keys: string[]): Promise<(T | null)[] | null> => {
|
|
108
120
|
return withRedis(async (redis) => {
|
|
@@ -110,18 +122,8 @@ export const mgetJson = async <T>(keys: string[]): Promise<(T | null)[] | null>
|
|
|
110
122
|
return [];
|
|
111
123
|
}
|
|
112
124
|
|
|
113
|
-
const payloads = await redis.mget<
|
|
114
|
-
return payloads.map((payload) =>
|
|
115
|
-
if (!payload) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
try {
|
|
120
|
-
return JSON.parse(payload) as T;
|
|
121
|
-
} catch {
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
+
const payloads = await redis.mget<unknown[]>(...keys);
|
|
126
|
+
return payloads.map((payload) => parseJsonPayload<T>(payload));
|
|
125
127
|
});
|
|
126
128
|
};
|
|
127
129
|
|
|
@@ -56,7 +56,25 @@ const getRedisKeyPrefix = (): string => {
|
|
|
56
56
|
return `${getRequiredRedisAppName()}_${envSuffix}`;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
-
const
|
|
59
|
+
const buildPrefixedRedisKey = (prefix: string, key: string): string => `${prefix}:${key}`;
|
|
60
|
+
|
|
61
|
+
export const getPrefixedRedisKey = (key: string): string => buildPrefixedRedisKey(getRedisKeyPrefix(), key);
|
|
62
|
+
|
|
63
|
+
export const getPrefixedRedisKeys = (keys: string[]): string[] => keys.map(getPrefixedRedisKey);
|
|
64
|
+
|
|
65
|
+
export const getQstashNamePrefix = (): string => {
|
|
66
|
+
return getRedisKeyPrefix();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const getPrefixedQstashQueueName = (name: string): string => {
|
|
70
|
+
if (!isNonEmpty(name)) {
|
|
71
|
+
throw new Error('[Upstash QStash] queue name must not be empty');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return `${getQstashNamePrefix()}_queue_${name}`;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const prefixRedisKey = (prefix: string, key: string): string => buildPrefixedRedisKey(prefix, key);
|
|
60
78
|
|
|
61
79
|
const prefixRedisKeys = (prefix: string, keys: string[]): string[] =>
|
|
62
80
|
keys.map((key) => prefixRedisKey(prefix, key));
|
|
@@ -71,8 +89,18 @@ const prefixFirstStringArg = (args: unknown[], prefix: string): unknown[] => {
|
|
|
71
89
|
return nextArgs;
|
|
72
90
|
};
|
|
73
91
|
|
|
74
|
-
const
|
|
75
|
-
return args.
|
|
92
|
+
const prefixVariadicKeyArgs = (args: unknown[], prefix: string): unknown[] => {
|
|
93
|
+
return args.flatMap((arg) => {
|
|
94
|
+
if (typeof arg === 'string') {
|
|
95
|
+
return [prefixRedisKey(prefix, arg)];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (Array.isArray(arg) && arg.every((key) => typeof key === 'string')) {
|
|
99
|
+
return prefixRedisKeys(prefix, arg);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return [arg];
|
|
103
|
+
});
|
|
76
104
|
};
|
|
77
105
|
|
|
78
106
|
const keyArrayCommands = new Set(['mget', 'del']);
|
|
@@ -103,12 +131,12 @@ const createPrefixedPipeline = <T extends object>(target: T, prefix: string): T
|
|
|
103
131
|
}
|
|
104
132
|
|
|
105
133
|
if (typeof prop === 'string' && keyArrayCommands.has(prop)) {
|
|
106
|
-
const nextArgs =
|
|
134
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
107
135
|
return (value as (...innerArgs: unknown[]) => unknown).apply(obj, nextArgs);
|
|
108
136
|
}
|
|
109
137
|
|
|
110
138
|
if (typeof prop === 'string' && allStringKeyCommands.has(prop)) {
|
|
111
|
-
const nextArgs =
|
|
139
|
+
const nextArgs = prefixVariadicKeyArgs(args, prefix);
|
|
112
140
|
return (value as (...innerArgs: unknown[]) => unknown).apply(obj, nextArgs);
|
|
113
141
|
}
|
|
114
142
|
|
|
@@ -149,8 +177,10 @@ const getRedisHealthIntervalMinutes = (): number =>
|
|
|
149
177
|
const getQstashHealthIntervalMinutes = (): number =>
|
|
150
178
|
parseMinutes(process.env.UPSTASH_QSTASH_HEALTHCHECK_INTERVAL_MINUTES, 10);
|
|
151
179
|
|
|
152
|
-
const getQstashHealthcheckUrl = (): string =>
|
|
153
|
-
process.env.UPSTASH_QSTASH_HEALTHCHECK_URL
|
|
180
|
+
const getQstashHealthcheckUrl = (): string | null => {
|
|
181
|
+
const url = process.env.UPSTASH_QSTASH_HEALTHCHECK_URL;
|
|
182
|
+
return isNonEmpty(url) ? url : null;
|
|
183
|
+
};
|
|
154
184
|
|
|
155
185
|
const scheduleRedisHealthCheck = (): void => {
|
|
156
186
|
if (redisHealthTimer || !cachedRedis) {
|
|
@@ -186,7 +216,12 @@ const scheduleRedisHealthCheck = (): void => {
|
|
|
186
216
|
};
|
|
187
217
|
|
|
188
218
|
const checkQstashHealth = async (token: string): Promise<void> => {
|
|
189
|
-
const
|
|
219
|
+
const healthcheckUrl = getQstashHealthcheckUrl();
|
|
220
|
+
if (!healthcheckUrl) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const response = await fetch(healthcheckUrl, {
|
|
190
225
|
method: 'GET',
|
|
191
226
|
headers: {
|
|
192
227
|
Authorization: `Bearer ${token}`,
|
|
@@ -199,7 +234,7 @@ const checkQstashHealth = async (token: string): Promise<void> => {
|
|
|
199
234
|
};
|
|
200
235
|
|
|
201
236
|
const scheduleQstashHealthCheck = (token: string): void => {
|
|
202
|
-
if (qstashHealthTimer || !cachedQstash) {
|
|
237
|
+
if (qstashHealthTimer || !cachedQstash || !getQstashHealthcheckUrl()) {
|
|
203
238
|
return;
|
|
204
239
|
}
|
|
205
240
|
const delayMs = getQstashHealthIntervalMinutes() * 60_000;
|
|
@@ -211,7 +246,6 @@ const scheduleQstashHealthCheck = (token: string): void => {
|
|
|
211
246
|
try {
|
|
212
247
|
await checkQstashHealth(token);
|
|
213
248
|
} catch (error) {
|
|
214
|
-
cachedQstash = null;
|
|
215
249
|
if (!qstashWarnedHealthCheck) {
|
|
216
250
|
qstashWarnedHealthCheck = true;
|
|
217
251
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -325,8 +359,14 @@ const ensureQstash = async (): Promise<QstashClient | null> => {
|
|
|
325
359
|
|
|
326
360
|
try {
|
|
327
361
|
const client = new QstashClient({ token: QSTASH_TOKEN });
|
|
328
|
-
await checkQstashHealth(QSTASH_TOKEN);
|
|
329
362
|
cachedQstash = client;
|
|
363
|
+
checkQstashHealth(QSTASH_TOKEN).catch((error) => {
|
|
364
|
+
if (!qstashWarnedHealthCheck) {
|
|
365
|
+
qstashWarnedHealthCheck = true;
|
|
366
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
367
|
+
console.warn(`[Upstash Config] QStash health check failed: ${message}`);
|
|
368
|
+
}
|
|
369
|
+
});
|
|
330
370
|
scheduleQstashHealthCheck(QSTASH_TOKEN);
|
|
331
371
|
return cachedQstash;
|
|
332
372
|
} catch (error) {
|